Skip to content

SplitView Extension Migration Guide

ScanuNicco edited this page Sep 25, 2021 · 67 revisions

As Kevin Dangoor pointed out in this Week's "Brackets Weekly", SplitView is a pretty big change under the hood. So this guide is here to help extension authors migrate their extensions to the new paradigm and what extension authors should do to maintain compatibility with Brackets.

For the most-part, Brackets will operate as it does today with a "current" view -- even though there is more than 1 view visible. Most of the changes will be transparent to extension authors but there are, however, a few things which extension authors should be aware of and a few things that extension authors should no longer do.

Here is a summary of the changes:

  • Editor objects and ImageViewers are not immediate descendants of the #editor-holder DOM node anymore. Extensions which manipulate the DOM at or under this node are probably going to stop working or cause stability and or usability issues for Brackets.

  • Editors are not the only thing that live in the Main Viewing Area so DocumentManger.getCurrentDocument() and EditorManager.getCurrentFullEditor() may return null. This is not new but there may be more cases when this will happen so extension authors need to be prepared for this.

  • Brackets will no longer have a single Working Set. Each Pane will manage a list of open files so extensions will need to identify which Working Set they wish to address when access the Working Set APIs. For alternatives to using Working Sets, see the section below on Working Set Alternatives.

  • Working Sets will contain File objects, as they did in previous versions, but the File objects may not map to Document objects as they did in the past. Extension authors will need to be able to handle non-document objects in Working Sets.

  • MainViewManager manages all Pane objects and is the interface to accessing Views, Working Sets, etc... Most MainViewManager APIs require a PaneId to identify which Pane to operate on. There are 2 PaneIds that have special meaning: ALL_PANES and ACTIVE_PANE. ACTIVE_PANE can be used on any API where a PaneId is required. Some APIs, however, such as close, remove, find, etc... will take the ALL_PANES identifier to make traversal much easier.

  • Custom View APIs are being deprecated in lieu of a View Factory Registry. Custom View providers need to construct a factory object that can create views for files.

Changes to DocumentManager

These Events are Deprecated. Extension authors should migrate to the recommended event.

Event Recommended event Notes
DocumentManager.workingSetAdd MainViewManager.workingSetAdd (1)
DocumentManager.workingSetAdd MainViewManager.workingSetAdd (1)
DocumentManager.workingSetRemove MainViewManager.workingSetRemove (1)
DocumentManager.workingSetRemoveList MainViewManager.workingSetRemoveList (1)
DocumentManager.workingSetSort MainViewManager.workingSetSort  
(1) Event will now be fired with File Object(s) that may not map to a Document

These APIs have been Deprecated and Extension Authors should migrate to the recommended API.

API Recommended API
DocumentManager.findInWorkingSet(file) MainViewManager.findView(MainViewManager.ALL_PANES, file)
DocumentManager.getWorkingSet() MainViewManager.getWorkingSet(MainViewManager.ALL_PANES) or MainViewManager.getAllOpenFiles()
DocumentManager.addToWorkingSet(file) MainViewManager.addToWorkingSet(MainViewManager.FOCUSED_PANE, file)
DocumentManager.addListToWorkingSet(list) MainViewManager.addListToWorkingSet(MainViewManager.FOCUSED_PANE, list)
DocumentManager.removeListFromWorkingSet(list) No Direct API Replacement(1)
DocumentManager.setCurrentDocument(doc) No Direct API Replacement(4)
DocumentManager.closeAll() No Direct API Replacement(2)
DocumentManager.closeFullEditor(file) No Direct API Replacement(3)
DocumentManager.beginDocumentNavigation() MainViewManager.beginTraversal()
DocumentManager.finalizeDocumentNavigation() MainViewManager.endTraversal()
DocumentManager.getNextPrevFile() MainViewManager.traverseToNextViewByMRU()
(1) Extensions using `DocumentManger.removeListFromWorkingSet()` should be using `CommandManager.DoCommand(FILE_CLOSE_LIST)` (2) Extensions using `DocumentManger.closeAll()` should be using `CommandManager.DoCommand(FILE_CLOSE_ALL)` (3) Extensions using `DocumentManger.closeFullEditor()` should be using `CommandManager.DoCommand(FILE_CLOSE)` (4) Extensions using `DocumentManager.setCurrentDocument()` should be using `CommandManager.DoCommand(CMD_OPEN)`, or `CommandManager.DoCommand(CMD_ADD_TO_WORKINGSET_AND_OPEN)`

These Public APIs are no longer in use and were identified as not being used by extensions. They have been removed from Brackets.

API
sortWorkingSet()
swapWorkingSetIndexes()
findInWorkingSetAddedOrder()

Changes to EditorManager

These APIs have been Deprecated and Extension Authors should migrate to the recommended API.

API Recommended API Notes
DocumentManager.resizeEditor() WorkspaceManager.recomputeLayout()  
EditorManager.getCurrentlyViewedPath() MainViewManager.getCurrentlyViewedPath() EditorManager.getCurrentlyViewedPath() will return NULL if the current file is an image or otherwise not a document.
EditorManager.setEditorHolder()   Used by 1 or 2 extensions for unit tests but now does nothing. Unit tests can construct a create a MainViewManager and initialize it to a DOM element. See MainViewManager._init()
EditorManager.registerCustomViewer()   Used by 1 or 2 extensions but does nothing. The preferred method is to create a View Factory that can create views of a file.
EditorManager.focusEditor() MainViewManager.focusActivePane() Extensions should be prepared that ImageViews or other Views are focusable in the new model.

These Public APIs are no longer in use and were identified as not being used by extensions. They have been removed from Brackets.

API Notes
EditorManager.notifyPathDeleted() Was marked for internal use only but not private. Not used by extensions.
EditorManager.showingCustomViewerForPath() Not used by extensions. There is no equivelent.

These Events have been removed.

Event Reccomended event Notes
EditorManager.currentFileChange MainViewManager.currentFileChange Not used by any registered extension

These Private APIs have been removed but are being used by extensions. Extensions using these APIs will break.

API Notes
EditorManager._showCustomViewer() Register a View Factory then open the file that is to be used with the custom viewer.
EditorManager._closeCustomViewer() No substition available. Extensions should register View Factories to create custom views and open a file assocatied with the factory object. Closing that view would be a close command on the current view.

Commands

The following commands have been added to support File objects.

Legacy Command New Command Notes
FILE_ADD_TO_WORKING_SET CMD_ADD_TO_PANE_AND_OPEN The new command will resolve to a File object while the legacy command resolves to a Document object.
FILE_OPEN CMD_OPEN The new command will resolve to a File object while the legacy command resolves to a Document object.

Pane Operations

Most of the `MainViewManager` APIs require a Pane ID to denote which pane the operation should target. Should it open the file in the left or right pane, for instance or locate a file in the top or bottom pane. Extension authors can use the following special Pane Ids when addressing a pane:
Constant Functionality Notes
MainViewManager.ALL_PANES The operation traverses all panes Not all APIs will take this constant. See the API for details
MainViewManager.ACTIVE_PANE The operation is performed only on the active pane  

Workingset Alternatives

APIs extension authors can use to get the list of files in the project in various forms that aren't Working Set specific:

MainViewManger.getAllOpenFiles()

Returns a list of all open files. This list will include all temporary views of files (those not in a working set), files opened that are not part of the project, images, files that are opened in an editor and files opened in custom viewers.
@type Array.<File>

Returns a list of all files in the project. Supports Filtering and inclusion of files opened in the working set which are not part of the project. @type JQuery.promise(Array.<File>)

Returns a list of all open documents -- including documents which are in a workingset but not yet opened, documents which have been opened in inline editors but not part of a workingset and opened but not modified or added to any workingset. @type Array.<Document>

Custom Views

EditorManager.registerCustomViewer() made assumptions about the lifespan of a view and would only allow 1 view to be created which didn't scale to the new model so it was deprecated and no longer does anything.

Extension writers must now create a Factory object with this interface to construct custom views:

{
    canOpenFile:function(path:string):boolean
    openFile:function(path:string, pane:Pane):jQuery.Deferred() 
}

Then register the factory by calling MainViewFactory.registerViewFactory()

View Factories will be able to crack a file URL to determine if they support creating a view for that particular file. ImageViewer, for example, responds by testing the file type for a known image type:

    canOpenFile: function (fullPath) {
        var lang = LanguageManager.getLanguageForPath(fullPath);
        return (lang.getId() === "image");
    }

URL cracking provides a fairly robust way to provide custom views for just about every file type, path or filename. View Factories are asked to crack URLs in a first-registered basis so there is no guaranteed order at this time.

{
     getFile: function () @return {!File} File object that belongs to the view (may return null)
     setVisible: function(visible:boolean) - shows or hides the view 
     updateLayout: function(forceRefresh:boolean) - tells the view to do ignore any cached layout data and do a complete layout of its cont
     destroy: function() - called when the view is no longer needed. 
     hasFocus:  function() - called to determine if the view has focus.  
     childHasFocus: function() - called to determine if a child component of the view has focus.  
     focus: function() - called to tell the view to take focus
     getScrollPos: function() - called to get the current view scroll state. @return {Object=}
     adjustScrollPos: function(state:Object=, heightDelta:number) - called to restore the scroll state and adjust the height by heightDelta
     switchContainers: function($newContainer:jQuery} - called to reparent the view to a new container
     getContainer: function() - called to get the current container @return {!jQuery} - the view's parent container
     getViewState: function() @return {?*} - Called when the pane wants the view to save its view state.  Return any data you needed to res
     restoreViewState: function(!viewState:*) - Called to restore the view state. The viewState argument is whatever data was returned from
}

Here is a sample extension

Extensions that will Break

For the most part, we expect that most extensions will continue to work as they do today. We have, however, identified a handful of extensions which do not work with Bracket's new architecture:

Extension Why it breaks Suggested remediation
CodeOverview Appnds nodes directly to #editor-holder Could listen for EditorManager.activeEditorChange and append nodes to the $(Editor.getRootElement()) but would need to keep track of previous instances and remove them.
MainWindow Resizes nodes inside of #editor-holder Just needs to resize #editor-holder and it should just work.
Workdspaces Resizes nodes inside of #editor-holder Just needs to resize #editor-holder and it should just work.
CharLImit Appnds DOM nodes directly to #editor-holder Could listen for EditorManager.activeEditorChange and append nodes to the $(Editor.getRootElement()) but would need to keep track of previous instances and remove them.
EpicLinter Assumes #editor-holder is the width of the editor Should use $(Editor.getRootElement()).width() instead.
Shreder Sets the width of #editor-holder to zero Unable to test with Release 0.42 or SplitView
Brackets Tabs Removes the working set and replaces it with tabs The DOM node #working-set-header no longer exists so this extension fails. There are numerous other issues that would need to be fixed for this extension to work.
Code Outline Bases size based on #working-set-header which no longer exists Probably can just use $(#sidebar).width()
Font Viewer Registers a Custom View for Font Files Register a View Provider Factory for font files
Brackets GIT
  1. Adds DOM nodes to #working-set-header
  2. Uses EditorManager._closeCustomViewer()
  3. Uses EditorManager._showCustomViewer
  4.     </ol>
    </td>
    <td>
        <ol>
            <li>Could append to <code>.pane-view-header</code> and use <code>ACTIVE_PANE` or <code>ALL_PANES</code> when closing unchanged documents.</li>
            <li>Private methods are subject to change or removal at any time</li>
        </ol>
    </td>
    

Unit Tests that will Break

The following Extension's Unit Tests will no longer work. Fixing these isn't critical but extension authors should maintain their reliance on running unit tests regularly to ensure their extension continues to work normally. This list identifies those which will break.

Extension Why it breaks Suggested remediation URL
QuickRequire Appends nodes directly to #editor-holder Could listen for EditorManager.activeEditorChange and append nodes to the $(Editor.getRootElement()) but would need to keep track of previous instances and remove them.
Simple JS Code Hints Uses EditorManager.setEditorHolder() Just use MainViewManager._init().
Type Script Code Intel Uses EditorManager.setEditorHolder() Just use MainViewManager._init().

Courtesy Notices

The following extensions were tested and didn't have major issues but some part of their functionality was broken due to the architecture change.

  • Brackets Vim - VIM readme.md references the ongoing SplitView work in core. Please let us know how it works for you. We tried your extension but the command :vsp throws an RTE in both master without SplitView and this branch. Otherwise it appeared that VIM commands worked normally in the SplitView branch.

  • Brackets Icons - Extension still works but icons are no longer displayed in the working set.

  • Brackets Files Icons - Extension still works but icons are no longer displayed in the working set.

  • Markdown Preview - Extension still works but no longer synchronizes the scroll position.

  • Column Ruler - Extension still works but doesn't show ruler correctly when the main view is split.

Final Notes

We encourage all extension authors to test their extensions even if they were not listed above. Extensions are one of the easiest ways to customize and contribute to Brackets so we'd like to thank you for your contributions and help make the transition as seamless and easy as possible. If there is anything missing from this migration guide or an API then please let the brackets team know.

Again, thank you for your contributions!

Clone this wiki locally