Vscode: Custom webview editor API

Created on 10 Jul 2019  Ā·  136Comments  Ā·  Source: microsoft/vscode

Updated: April 24, 2020

The Custom Text Editor API has shipped with VS Code 1.44! Checkout the documentation and example extension to get started creating your custom text editors.

If you encounter any issues using CustomTextEditorProvider, please open a new issue.

This issue now tracks custom editors for binary files, which we aim to finalize for VS Code 1.46


Overview

The custom editor API aims to allow extensions to create fully customizable read/write editors that are used in place of VS Code's standard text editor for specific resources. These editors will be based on webviews. We will support editors for both binary and text resources

A XAML custom editor, for example, could show a WYSIWYG style editor for your .xaml files. Our end goal is to give extensions the most flexibility possible while keeping VS Code fast, lean, and consistent.

Non-goals

Many features that are potentially related to custom editors are out of scope of this current proposal, including:

  • Specifying how resources are persisted and how resources are synchronized with webviews
  • Specifying what an edit is
  • Customization of VS Code's text editor using html / css
  • Embedding of VS Code's text editor in webviews
  • UI related to custom editors (beyond switching view types)

These all would fall under separate feature requests. Many also be a better fit for an external helper library instead of VS Code core

Tracking progress

This issue thread captures the evolution of the webview editor proposal. That means that many comments may now be out of date. Some of these have been minimized, others have not.

For the most up to date info, see:

api api-finalization custom-editors plan-item

Most helpful comment

@mjbvz Is there a way for the extension to tell VS Code to NOT load the custom editor.
I'm thinking of scenarios such as the git diff view. When user opens a file in source control history view, then we need to display the differences. At this point, we'd prefer for VS Code to display its own text base diff viewer.

Further to this, sometimes I've found the need to view the raw file content instead of using the custom editor. Out of curiousity, is there a provision for such a scenario. E.g. displaying an icon on the top right, that allows users to open the raw viewer. Similar to displaying the icon to open settings.json file instead of the settings viewer.

All 136 comments

Two main parts of this that I see:

  • Custom views for resources (interactive but not editable)

    A good example would be an image or 3d model viewer. To test out this api, we could even look into moving our builtin image preview into an extension

    • Basic case of just opening a custom view for a resource
    • Switching the custom view for a resource (if multiple potential custom views are registered for it)
    • Configuring the default custom view for a resource
    • Making sure that extensions are lazily activated for all these cases
  • Custom editors for resources (hooking the custom views up to VS Code's text editor lifecycle)

    These would be custom views that can be edited and saved like normal VS Code text editors

    • Handling saves?
    • How much should VS Code control and how much should we leave up to extension? Copy/paste for example. Is that something VS Code should specifically delegate to the custom editor or let each extension implement?

Some quick sketches

Api

export enum WebviewEditorState {
    Default = 1,
    Dirty = 2,
}

export interface WebviewEditor extends WebviewPanel {
    state: WebviewEditorState;

    readonly onWillSave: Event<{ waitUntil: (thenable: Thenable<any>) => void }>
}

export interface WebviewEditorProvider {
    /**
    * Fills out a `WebviewEditor` for a given resource.
    *
    * The provider should take ownership of passed in `editor`.
    */
    resolveWebviewEditor(
        resource: Uri,
        editor: WebviewEditor
    ): Thenable<void>;
}

export function registerWebviewEditorProvider(
    viewType: string,
    provider: WebviewEditorProvider,
): void;

Contributions

{
    "contributes": {
        "webviewEditors": [
            {
                "viewType": "hexViewerExtension.hexEditor",
                                "displayName": "Hex Editor",
                "languages": [
                    "hexdump"
                ]
            }
        ]
    }
}

UX

User clicks on a hexdump file for the first time

  • If no custom view for a hexdump file is registered.

    • Fallback to VS Code's existing behavior
  • If only a single extension is registered for a hexdump file

    • Fire an activation event
    • Extension registers a custom view provider by calling registerWebviewEditorProvider for hexViewerExtension.hexEditor

      • VS Code invokes this provider with a WebviewEditor that the extension fills out

  • If multiple extensions are registered for a hexdump file

    • Show a quick pick UI asking the user to select which custom view they would like. (and potentially persist this choice)
    • After a selection is made, go through flow as if we have a single registered editor

User has a custom view open and wants to change to a different view

  • Show the quick pick for selecting which editor to use. Include vscode's default view
  • After the user makes a selection and changes the editor they wish to use, destory the old view and recreate a new view in its place

I like, I think this goes into the right direction. Other ideas/thoughts when looking at this

  • Is language the right abstraction? hexdump or png is not really a language but types of files and languages might just be a subset of that. So, instead or in addition to language I would favour something like pattern
  • Allow to opt-in to saving, e.g hexdump might be reading only?
  • Many web views work with a master text editor, like markdown or jupiter notebooks, and I think it would make to design for that from the beginning, e.g have a (static) flag to signal that this is a master-detail case (related discussion https://github.com/microsoft/vscode/issues/22068)

Is language the right abstraction? hexdump or png is not really a language but types of files and languages might just be a subset of that. So, instead or in addition to language I would favour something like pattern

Very good point. Think of files like WASM that can be represented _and modified_ as text in multiple formats (parens form, linear form) as well as block diagram as well as hexadecimal...

Or SVG, which could be viewed inside an image editor as well as XML editor.

I'm very hyped about this.

Is it possible i read in the docs for extension developers (about a year ago) that functionality like this was deliberately not supported for performance and stability reasons?

Not that i'm not hyped as well.. ;)

An API proposal and a draft implementation is in #77789. That one ties custom editors to webviews specifically.


An alternative approach that @jrieken and @bpasero brought up is more generic "open handler" extension point. Here's a quick draft of what this could look like:

Contributions

{
    "contributes": {
        "openHandler": [
            {
                "id": "imageView.imageViewer",
                                "displayName": "Image Viewer",
                "extensions": [
                    "png",
                    "jpg"
                ]
            }
        ]
    }
}

API

/* ignoring all the editable stuff about webview editors for the moment */

export interface WebviewEditor extends WebviewPanel { }

export function createWebviewEditor(resoruce: Uri, viewType: string, showOptions: ViewColumn | { viewColumn: ViewColumn, preserveFocus?: boolean }, options?: WebviewPanelOptions & WebviewOptions): WebviewEditor;

export function registerOpenHandler(
    id: string,
    handler: (resource: Uri, viewColumn: ViewColumn) => void,
): void;

Usage

export function activate() {
  vscode.window.registerOpenHandler('imageView.imageViewer', (resource: vscode.Uri, viewColumn: ViewColumn) => {
      vscode.window.createWebviewEditor(resource, 'imageView.imageView', viewColumn);
      ...
  });
}

Advantages over first API proposal

  • Very generic. Enables some potentially interesting uses like opening a external editor when clicking on a resource for example
  • Could be built on by future apis since it is not webview specific

Disadvantages

  • Maybe too generic? It lets extensions potentially do all sorts of non-standard things
  • Could this let extensions slow down the VS Code UX?
  • It would likely require duplicating more API around webviews (we'd need serializers for example)
  • Should extensions be able to arbitrarily invoke createWebviewEditor? Just as one example of why we may not want to allow this, we likey only want to allow one editor for a given resource per view column

/cc @Tyriar @kieferrm Since I know you were also interested in the discussion around the "open handler" proposal

Some thoughts:

  • The open handler seems too flexible to me. Since it doesn't return any value and it could literally do anything, the extension would need to do a bunch of management around editors that VS Code can do for no cost using the other approach. For example, clicking on a file with the same editor twice, the extension would need to know to focus the existing editor.
  • I think undo and redo should definitely be considered from the beginning in order to make more useful non-read only editors.

After talking over the "open handler" proposal with @Tyriar and thinking about it more, I agree that it is likely too open ended in its current form. Does anyone has any specific use cases for the "open handler" proposal that could not be addressed by the previous custom editor proposed API or by existing VS Code apis?


As for commands such as undo/redo in webview, a couple of options:

  • Add events for very common commands such as save, undo, and redo:

    readonly onWillSave: Event<{ waitUntil: (thenable: Thenable<any>) => void }>
    readonly onWillUndo: Event<{ waitUntil: (thenable: Thenable<any>) => void }>
    readonly onWillRedo: Event<{ waitUntil: (thenable: Thenable<any>) => void }>
    ....
    
  • Add a generic way to listen for commands:

    onWillExecuteCommand(command: 'workbench.action.files.save' | 'undo' | 'redo' | ..., handler: (e) => Thenable<any>>
    

Both solutions would require that extensions explicitly state which commands they are interested in. This lets us avoid sending all commands to extensions, and would also let us enable/disable the relevant buttons in the menubar. (We may still want to consider save a special case since it will be very common and may have special requirements)

Does anyone has any specific use cases for the "open handler" proposal that could not be addressed by the previous custom editor proposed API

I think a use-case would be files that open as text but need some prior processing. E.g clicking a foo.class-file shouldn't open a web view editor but, after a decompile-step, should open a foo.generated.java-file. However, we can treat that as a different problem and think about it as a default text content provider so certain file-types.

Let's continue with the existing proposal.

I think undo and redo should definitely be considered from the beginning in order to make more useful non-read only editors.

@Tyriar Can you explain why? We have added save-logic because editors render the dirty state (in their titles) but undo/redo isn't part of the editor UX. I agree that undo/redo is important but to me it's just commands that an extension should register.

Does anyone has any specific use cases for the "open handler" proposal that could not be addressed by the previous custom editor proposed API or by existing VS Code apis?

So are we saying that https://marketplace.visualstudio.com/items?itemName=slevesque.vscode-hexdump needs to rewrite the extension so that the standalone editor is embedded into a webview? So they would have to ship the standalone editor as part of their extension? This scenario is quite popular (see https://github.com/Microsoft/vscode/issues/2582, has even more upvotes than https://github.com/Microsoft/vscode/issues/12176).

Maybe I misunderstood and we are still thinking of supporting text editors to open but with the previous API.

Maybe I misunderstood and we are still thinking of supporting text editors to open but with the previous API.

Yeah, I think that's still a valid scenario, e.g. the webview editor should have an option to let the workbench know that it wants to show to the side of a text editor. For hexdump but also for markdown

To be clear, here is what I expect:

  • I open the explorer
  • I click on a file that is binary
  • hexdump extension opens with a text editor with custom content

I agree that I would not put the burden of finding out if the editor is already opened onto the extension but on us.

Can you explain why? We have added save-logic because editors render the dirty state (in their titles) but undo/redo isn't part of the editor UX. I agree that undo/redo is important but to me it's just commands that an extension should register.

@bpasero I don't mind so much on how this is done, but if the user has a custom keybindings for the regular undo/redo commands it should work in the custom editor too.

Does anyone has any specific use cases for the "open handler" proposal that could not be addressed by the previous custom editor proposed API or by existing VS Code apis?

The original resolveWebviewEditor proposal is, at a broad level, probably enough for our needs. One thing it's missing compared to createWebviewEditor is starting a new document with no backing file. Is there a path to supporting this in the resolveWebviewEditor API?

Add a generic way to listen for commands:

YES! There are so many commands that we want to hook into. In addition to Save/Undo/Redo: Save As, Find/Replace; the LSP commands like Go To Definition/Find References, and the DAP commands like Debug Start/Continue/Step Into/Step Over/Stop.

First iteration of custom editors proposal (#77789) is merged in order to get testing and feedback in VS insiders.

Here's what things stand:

Api

export interface WebviewEditor extends WebviewPanel { }

export interface WebviewEditorProvider {
    /**
    * Fills out a `WebviewEditor` for a given resource.
    *
    * The provider should take ownership of passed in `editor`.
    */
    resolveWebviewEditor(
        resource: Uri,
        editor: WebviewEditor
    ): Thenable<void>;
}

namespace window {
    export function registerWebviewEditorProvider(
        viewType: string,
        provider: WebviewEditorProvider,
    ): Disposable;
}

Contribution

"contributes": {
  "webviewEditors": [
    {
      "viewType": "imagePreview.previewEditor",
      "displayName": "%webviewEditors.displayName%",
      "discretion": "default",
      "selector": [
        {
          "filenamePattern": "*.{jpg,png}"
        }
      ]
    }
  ]
}
  • discretion — Controls if the custom editor should be used by default or not when the extension is active (the default is true).
  • selector — Let you specify both a filenamePattern and an optional scheme that the custom editor should be enabled for

Other changes

  • VS Code's image preview has been moved into an extension powered by webview editors

  • A new workbench.experimental.editorAssociations user setting allows configuring which custom editor to use for a given resource. The schema of setting will likely be reworked as it is not too user friendly ATM

  • A new open with command has been added to the explorer context menu. This lets you select which editor to open the target resource with

  • A new reopen with command has been added. This commands lets you re-open the current editor using a custom editor

Still todo

  • Confirm #77789 didn't break the world ;)

  • Ux improvements, bug fixes, polish

  • Save + dirty file tracking

  • Command execution (copy, paste, ...)

  • Try to understand what we need to do for the new file case. The current implementation can already be used with untitled files (obviously without save or anything hooked up)

Fantastic, thank you! Looking forward to Save :)

@mjbvz any thoughts on how to support an extension such as https://marketplace.visualstudio.com/items?itemName=slevesque.vscode-hexdump can open a monaco text editor?

Save + dirty file tracking

We might need to talk. Today this is entirely controlled by checking for the resource scheme (e.g. supported are everything the file service can handle + untitled). Would we support hot-exit for custom editors? Auto save?

I'm trying this API and it's looking great so far.

One thing that doesn't seem to work is opening a file programatically. I wrote an extension to handle .ipynb files and set it as the default. This works for when I click a file in the Explorer.
But when I called vscode.window.showTextDocument('file:///path/to/my.ipynb'), I got an exception:

Error: Failed to show text document file:///path/to/my.ipynb, should show in editor #undefined

Would it be hard to make showTextDocument work with custom editors?

Would it be hard to make showTextDocument work with custom editors?

You are not showing a text document as in "returns a text editor" - therefore the API won't be supported. However, you should be able to use the vscode.open-command

Thanks, that worked! Didn't see that one because I was just looking in vscode.d.ts.

np - it does bring up an interesting point and raises the question if we should have something like showWebviewEditor and if we should allow to enumerate/discover web views (my feeling is that we shouldn't since web views are pretty specific to an owner, unlike text editors which are pretty generic)

@bpasero Still not sure about handling that case.

None of us really liked the idea of a generic Open handler api (see https://github.com/microsoft/vscode/issues/77131#issuecomment-522187431 and @Tyriar's comment: https://github.com/microsoft/vscode/issues/77131#issuecomment-523157471)

But revisiting the open handler proposal for a moment: what if extensions didn't need to know anything about editors and instead only remapped uris? This would leave the actual editor opening up to VS Code. Something like:

export function registerOpenHandler(
    id: string,
    handler: (resource: Uri) => Promise<Uri>,
): void;

For the custom text editor case (like the hexdump extension), an open handler would map the incoming uri (file://Users/mat/cat.png) to a uri that a TextDocumentContentProvider could handle ( hexdump://Users/mat/cat.png)

Custom webview editors would do the same, i.e. file://Users/mat/cat.png -> my-webview-scheme://Users/mat/cat.png

Some other aspects of this api direction:

  • We would have an openers contributions that would define the contributed custom editors for a given file type. We may not even to expose a registerOpenHandler function since the contribution could tell us what scheme to remap the uri to:

    "contributions": {
      "openers": [ 
        { 
        "id": "myExt.hexDump",
        "displayName": "Hex Dump",
        "filenamePattern": "*.png",
        "scheme": "file",
        "targetScheme": "hexdump"
        }
      ]
    }
    
  • WebviewEditors would be contributed for a scheme instead of for a file pattern like the current proposal, much like TextDocumentContentProvider or the old vscode.previewHtml command ;)

  • The remapped uris should be hidden to the user

Needs more thought but it seems like an interesting direction. Would be interested in hearing other's thoughts on the idea

For the custom text editor case (like the hexdump extension), an open handler would map the incoming uri (file://Users/mat/cat.png) to a uri that a TextDocumentContentProvider could handle ( hexdump://Users/mat/cat.png)

I like this idea šŸ‘

I've been learning a lot while iterating on the custom editor proposal. I've also been thinking more about how custom editors need to fit into VS Code and have had to rethink some of my earlier assumptions about this.

Here I've attempted to capture what my current thoughts on the direction of custom editors, as well as noting the questions I still have and sketching out some potential approaches to addressing them.

First off, here are a few conclusions I've reached while working on the custom editor proposal:

  • Custom editors need to support non-disk resources

    This includes:

    • Untitled resources
    • Data resources (which we use in the diff view)
  • Custom editors should support dirty resources

    If I make a change in a text file and then open a custom editor on it, I expect the custom editor to reflect the dirty file (not the version on disk).

  • TextDocumentContentProvider is not a custom editor

    TextDocumentContentProvider can only create readonly text buffers and cannot participate in save or any other editor actions.

    A better analog for custom text editors is actually our file system provider api, as this both gives the resource a custom scheme and allows the extension to participate in save. However this API is a really heavyweight way to implement a custom text editor.

  • Extensions should not need to know where the resource data they are rendering comes from, nor should they need to know how the resource data itself will be used by VS Code.

Questions regarding how custom editors should work and what they should be

The existing custom editor api works pretty well for readonly documents. However once we start talking about writable custom editors, the current proposal is vague on a number of important points:

  • On save, should the custom editor be responsible for writing data to disk?

    Do we delegate file saving entirely to custom editors, or should save be more a notification to the custom editor that it should prepare its contents to be written to disk.

  • Does VS Code maintain editor history, or do custom editors maintain their own editor histories?

  • How does a custom editor know when its backing resource has changed?

    • Do we expect it to set up file watchers?
    • How do we handle conflicts?
  • How do we support custom editors for different versions of a file (the diff view case)?

  • When does a custom editor flush its contents back to VS Code?

    For example: I edit a file using a custom editor and then switch to view the same file in VS Code's normal text editor without saving. In this case, I expect to see the modified content as text.

  • How does hot exit work?

  • What about save as?

  • Who provides Ui for all this?

I think most of these questions boil down to a debate of what exactly a custom editor should be and how it interacts with VS Code core. Should custom editors simply be views of a resource? or should they take over responsibility for that resource as well?

Let me quickly sketch out these two extremes to highlight this difference:

Custom Editors as views of resources

At one extreme, we could say that custom editors are simply views of resources. The custom editor takes in some resource state, renders itself, and can trigger events to tell VS Code that the resource has changed. This is a very React-style way of looking at the problem, so why not just express it in React terms:

<MyImageEditor
    /* What we are rendering */
    resource={'file:///Users/matb/cat.gif'}

    /* Some abstraction so that the editor can read the file, since the file on disk may be different than VSCode's version */
    resourceState={...}

    /* Callback fired by the custom editor to signal the state of the editor has changed */
    onContentChanged={...}

    /* Some way to signal if editor is readonly or not? */
    />

Every time the resourceState changes, the custom editor would be re-rendered.

If we take this approach, VS Code core would maintain the data for each resource (just like it does for text editors today). Core would be responsible for writing resources to disk on save, it would track editor history, and would handle reverts and other content operations, re-rendering the custom editor as needed as resource state changes.

Custom editors as owning resources

At the other extreme, we could say that a custom editor both provides a view of a resource and is responsible for that resource. The API for this would be much closer to the existing proposal:

interface CustomEditor {
    state: EditorState; // readonly, dirty, unchanged

    /*
     * Invoked when the user triggers save.
     *
     * Should write the file content to disk.
     */
    save(): Thenable<void>;

    /*
     * Invoked when the user presses undo or redo.
     *
     * Should write the file content so that VS Code can read it
     */
    undo(): Thenable<void>;
    redo(): Thenable<void>;

    ...
}

But this proposal doesn't answer the questions I brought up. It also gives extensions a lot of room for interpretation, and we know that extensions will implement an api like this in different, incompatible ways.

Door Number 1

My feeling is that considering custom editors to be simple views is the better approach (although perhaps not the simpler one for us to implement). It allows core to control resources so that edge cases are handled properly and so that the user experience is consistent no matter what type of custom editors user are using.

At an extremely high level, here's what this approach would mean for the various types of custom editors we have discussed:

A writable, webview based editor

Consider an image editor extension. Under this new proposal, this extension would need to:

  • Declare that it provides an editor for some resources
  • Given some resource state, render an initial webview editor
  • Update its webview editor when the resource state changes
  • When the user makes a change in the image editor, flush that back to VS Code in some way (not to disk!)

A writable, text based editor

Consider a hex dump extension (that is also writable). This extension would need to:

  • Declare that it provides an editor for some set of resources.
  • Given some resource state, return a string
  • Updates its text when the resource state changes
  • When the user makes a text change, flush the actual data for the resources back to VS Code in some way (not to disk!)

As @bpasero brought up, we may be able to leverage the filesystem provider api for this

Questions

  • Are there cases where custom editors as simple view do not work?

  • What would such an api look like in practice?

  • Can we implement it efficiently? I'm viewing a 100mb json file in a custom editor, we can't be sending around 100mb buffers all the time.

Other thoughts or concerns?

Is there an ETA on the API being available in a public build? Since the Python VS Code extension is using a webview based editor for Jupyter notebooks and need access to the File Save menu for our upcoming release in October. Thanks!

@mjbvz Door Number 1 sounds good to me. It's exactly what we'd need for editing notebooks. Although the write to disk problem isn't a big issue for us as the webview doesn't do the writing.

@jmew No. Proposed apis move out of proposed when they are ready. But it will certainly not be by October

Actually thinking more about your 'Door number 1' option, I don't think this is going to work for us unless we can also talk to the webview somehow.

We do a bunch of stuff on the extension side now (like starting the jupyter server) that we'd have to somehow do when the simple view was opened.

We also do conversions on the data using tools installed in the jupyter server, so we'd likely have to roundtrip the data anyway before showing it in the webview.

When the user makes a change in the image editor, flush that back to VS Code in some way (not to disk!)
...
Can we implement it efficiently? I'm viewing a 100mb json file in a custom editor, we can't be sending around 100mb buffers all the time.

I'm concerned about this part, in an image editor it would be too expensive to generate a copy of the actual file on every single change. Instead you would probably maintain a grid of pixels and a bunch of transforms that can be applied and undone. Ideally the webview editor would handle undo/redo in their own way and saves (generating/sending the file) would only be done when absolutely necessary.

I'll echo the above concerns about Door Number 1.

In addition to the performance concerns, I also have concerns about the UI state preservation (eg. an authenticated Jupyter connection for a notebook editor). If the content changes on disk, I don't think the extension should forcibly reload with all the UI state reset.

I do see value in between the extremes though. For example, I don't see any need for the extension to read or write the file directly to disk. That seems better suited to a module which can read and write from a variety of sources.

@mjbvz I think the fundamental discussion is about how much control to give to the custom editor, and who should be responsible for file state. I don’t think we need to go with one extreme or the other, and can have a reasonable middle ground which allows VSCode to guarantee consistent experience across custom-editor extensions.

I think VSCode should be responsible for reading/writing the raw file to disk. There could be a getContents(stream) API which the custom editor has to implement, which VSCode can call:

  • on File->Save/Save As
  • periodically, to be able to handle hot exit scenario.

More formally, here are some ideas for the questions you raised:

On save, should the custom editor be responsible for writing data to disk?

VSCode should be responsible for writing data to disk. Given above approach the custom editor can provide contents to an output stream with getContents API. The custom editor should be notified when data is about to be or has been written to disk (e.g. a didSave or willSave notification)

Do we delegate file saving entirely to custom editors, or should save be more a notification to the custom editor that it should prepare its contents to be written to disk.

See above

Does VS Code maintain editor history, or do custom editors maintain their own editor histories?

Perhaps the custom editor provides VSCode with state/commands for undo/redo stack, or maintains its own history. For Jupyter notebooks it could have been an ā€œinsert/delete cellā€ command (i.e. very context-specific to extension), and we probably don’t want VSCode to keep track of the underlying file’s contents on every keystroke.

How does a custom editor know when its backing resource has changed?

  • Do we expect it to set up file watchers?

VSCode could provide this as a notification to the custom editor as well, notifying it that the underlying file has changed. Similar to LSP (textDocument/didChange)

  • How do we handle conflicts?

Are you referring to source control merge conflicts?
In that case perhaps it’s fine for VSCode to fallback to the text editor for merge conflicts, because SCM will insert merge markers into the file, so the regular text editor UI makes more sense for dealing with that.

How do we support custom editors for different versions of a file (the diff view case)?

Either by allowing the custom editor to provide text for the diff view (@alexandrudima's comment), or expect the custom editor to implement diff-view API’s as well.

When does a custom editor flush its contents back to VS Code?
For example: I edit a file using a custom editor and then switch to view the same file in VS Code's normal text editor without saving. In this case, I expect to see the modified content as text.

The custom editor can be pulled for its contents by VSCode as it sees fit. This gives you control of how frequently to fetch state. I think it should only be onSave/SaveAs, and some periodic fetch (for hot exit case).

How does hot exit work?

Call getContents periodically to fetch state from custom editor, or during an onExit call explicitly, if VSCode has that for hot exits.

What about save as?

This should be similar to Save, where VSCode is responsible for the saving, but notifies the custom editor about the Save As event. Should just be metadata on the notification, I don’t think the custom editor needs to do anything special for Save As.

Who provides Ui for all this?

As much as possible it should be VSCode, because that provides consistency across custom editor extensions, which is better for VSCode.

Hi @mjbvz, is there anything blocking retainContextWhenHidden being implemented for custom editors?

I tried adding the option at https://github.com/microsoft/vscode/blob/313ede61cbad8f9dc748907b3384e059ddddb79a/src/vs/workbench/api/common/extHostWebview.ts#L291 but it just didn't seem to do anything. Do you have any idea why that didn't work?

This is important for editors with complex state so they don't get destroyed when the user switches to another tab.

Just did some testing and one thing I noticed that was it was overly complicated to get the actual file data, I think what I'm meant to do is do all the webview uri mapping stuff and then load it via fetch. While this is certainly convenient when you're just displaying an image, it's more cumbersome for editing a file where you would typically want to deal directly with a buffer. Considering the following:

resolveWebviewEditor(
    resource: Uri,
    editor: WebviewEditor,
    data: Promise<ArrayBuffer>
): Thenable<void>;

// alternatively
export interface WebviewEditor extends WebviewPanel {
    readonly onLoad: Event<ArrayBuffer>
}

This seems like it would be very useful for building a binary or image editor as the extension would not need to deal with the file system at all, provided the method of saving the buffer is similar.

Looking at the use cases for extension-provided editors it seems prudent to allow them to embed VSCode's editor functionality somehow to get access to the editing-related features provided by other extensions - be it a code notebook or the thing that I'm currently building, which is an inline HTML editor.

dcecb9eea6158f561ee703cbcace49b84048e6e3 documents the current thinking on the Webview Editor API (although none of the functionality around edit/save has actually been implemented yet.

This direction takes the recent feedback into account and is a compromise between the two extremes originally proposed: it leaves the specifics of how files are saved up to extensions but abstracts away most of the details about how editor actions interact with custom editors and how editor state is shown to the user. Here's a quick review of the new proposed design:

To start with, resolveWebviewEditor now returns a WebviewEditorCapabilities object

export interface WebviewEditorProvider {
    resolveWebviewEditor(
        input: {
            readonly resource: Uri
        },
        webview: WebviewPanel,
    ): Thenable<WebviewEditorCapabilities>;
}

The WebviewEditorCapabilities describes how the webview editor interacts with VS Code:

    interface WebviewEditorCapabilities {
        rename?(newResource: Uri): Thenable<void>;
        readonly editingCapability?: WebviewEditorEditingCapability;
    }

    interface WebviewEditorEditingCapability {
        save(resource: Uri): Thenable<void>;
        hotExit(hotExitPath: Uri): Thenable<void>;

        readonly onEdit: Event<any>;
        applyEdits(edits: any[]): Thenable<void>;
        undoEdits(edits: any[]): Thenable<void>;
    }

VS Code would then wire up these WebviewEditorCapabilities to the editor itself. For example, pressing cmd+s on a webview editor would invoke the save function. This model should give extensions a lot of freedom to implement the logic for save and undo/redo, while still letting VS Code drive the core user experience.

We will be working on implementing the first parts of the API in November. Keep in mind that the capabilities interfaces may change significantly as we learn more about it

Hi @mjbvz! Thanks for the update, the API looks very promising! I'm one of the maintainers of kogito-tooling and we are currently shipping a DMN/BPMN editor based on VSCode WebViews.

We've been working with the existing API for a while, so I'd like to offer my two cents on this discussion. Basically, the two major challenges we have today are:

  1. Saving
  2. Making the WebView feel like a native editor

Since the first one is most likely going to be solved by this new API, I'll focus on the second one. Native text editors on VSCode have a bunch of features that seem secondary, but actually do improve the UX a lot, like "Confirm before closing a modified file", "Show a "modified" indicator when a file is changed", "Show a (deleted) label on the tab title when the file is externally renamed/deleted", etc..

So for the new API, here are a few questions:

WebviewEditorCapabilities#rename

  • Will this update the editor tab title?

WebviewEditorEditingCapability#save

  • Will this be invoked when users hit Save all too?

WebviewEditorEditingCapability#hotExit

  • Will this be called after a confirmation popup show up in case the files have unsaved changes?

WebviewEditorEditingCapability#onEdit

  • Will this update the "dirty" indicator on the editor tab?

WebviewEditorEditingCapability#applyEdits and WebviewEditorEditingCapability#undoEdits

  • What is going to be the type of the parameter? I'm guessing is the same type we return on onEdit?

Hope that's insighful!

One more thing I'd like to ask is do you guys have an estimate of when this will become a stable API?

Really interested in this feature. GJ guys so far. Thank you.

@mjbvz Are there any plans on providing some API to optionally pass line/column information to the webview.
E.g. when users open a file from VS Code (in problems pane or debug stackframe), it opens a file and goes to a specific line number.
We'd like to have this functionality in the notebook editor for DS functionality in the Python extension.

  • This allows us to build a debugger using the VS Code UI (current plan is to build a debugger with a custom bare minimum UI, without stack frames, without variables, etc, i.e. without majority/any of the VS Code debugger UI).
  • This will also allow us to add support for linting (displaying problems related to python code in problems window).
  • & more.

@kieferrm @greazer /cc

& yes, thanks for the awesome work in this space.

@tiagobento

  1. Extensions would not invoked rename; it is invoked by VS Code on the extension after the user renames a file.

  2. Yes, save lets you hook into saving of files however that is triggered

  3. The intended UX is to match how normal editors work. If hot exit is enabled, there is no popup in this case

  4. Yes. However it is VS Code who decides to mark the file dirty or not based using the edits it knows about. Extensions cannot directly mark the editor as dirty (without pushing an edit)

  5. Edits will need to be a json serializable objects. We do not care about the actual value of this object. That's why any is used for now

@DonJayamanne An initial line number can be passed through the URI using the fragment or query parameters

At the moment, there is no way to reopen the same document again at the different line number. That is something we'll consider supporting in the future. It may be pushed to a separate API proposal after the core custom editor logic is finalized

Update

Here's where things stand on the proposal:

  • The api is unchanged for the most part since the last update, although I have commented out or removed the non-functional parts of it. You can find the latest version in vscode.proposed.d.ts

  • Basic save and undo/redo are now functional

  • I've been using this simple extension to test the apis: https://github.com/mjbvz/vscode-experimental-webview-editor-extension

  • The next goal is hook up saveAs this iteration, and possibly rename as well.

  • Hot exit support will require more time

Testing

If you have a use case in mind for writable custom editors, we'd appreciate your feedback on the newly enabled parts of the api:

  • Open bug reports for stuff that doesn't work

  • Can you implement undo/redo/save using this api? How difficult is it?

  • Are there uses cases around undo/redo/save that would not work with the current api?

@mjbvz Is it possible to use the new editor for customized schema?

In PROBLEMS view, Java language server reports the workspace root folder as the diagnostics resource of the project level errors. When the user clicks it, actually there are no editors able to open it. To address the issue, we want to open a customized web view to show the build path info. But because it doesn't have a specific file, it seems we cannot use filenamePattern to enable the webviewEditors in the contributes of package.json. If the custom editors support schema match, we'd like to return a special uri scheme for project root errors.

@testforstephen Not at the moment. That use case may be a good fit for the Text Document Content provider API?

@mjbvz Thanks. We have a try on text document content provider, but the limitation is it only supports plain text document. It's suitable for code presentation such as decompiled class, but not good for project config presentation. That's why we want to try WebView for more rich presentation.

@testforstephen Yes give text documents a try; you can probably get pretty far with them. As author of the webview API, my advice is always to not use webviews unless you really need to

@mjbvz, After trying out the new API, I'd suggest giving an option to opt out of the undo/redo capability while still being able to set the dirty bit and save, because undo/redo is significantly more complicated for an extension to implement.

The complication I've got is as follows:

I'm building a notebook editor which includes Monaco editors in the webview, so I want to integrate Monaco's undo stack to the Custom Editor undo stack. Monaco can't provide an event to notify when an "undoable edit" occurs because it doesn't know the extent of the edit until after the fact. ie. if I type "hello", it gives me a change event for each of the five keys typed, but pressing Undo at this point will remove the entire word even though the last edit event was only to insert the character "o". It doesn't make sense to delay the events until after all the keys are typed because we want to set the document to dirty immediately, but it also doesn't make sense for me to send five separate onEdit events to your API because I want a single Undo to revert all five characters.

Monaco has the concept of a "current open stack element" which sits at the top of the undo stack. Each edit is coalesced into this element until the client calls pushStackElement, at which point a new open stack element is pushed on top. Each stack element represents one undo step, but can comprise many edits. I think the Custom Editor API should follow a similar model.

Caveat: Even with this system, it wouldn't solve my problem because the Monaco API doesn't currently expose when it internally decides to push a stack element, so I'd also need it expose that as an event.

Another suggestion:
Complex webviews (like graphical editors) requires much communication with the extension code. maintaining it with post messages are hard, since you need to handle callbacks, etc'.
I created an RPC library to ease this communication https://github.com/SAP/vscode-webview-rpc-lib.
It could be great if it would be part of the VSCode APIs.

Funnily enough I was just thinking this morning about a custom view of .gitignore files to make editing those easier. Especially when there are multiple .gitignore files in a project, a GUI editor might be able to incorporate all of them into a tree view, allow drag/drop between branches, generate globs by selecting multiple files to match, all sorts of things.

@KevinWuWon What if we allowed saving of unchanged files too? (unchanged just being files that we don't have any edit stack for) I believe text editors already support saving unchanged files. I believe it is still best to keep the dirty indicator itself tied to the undo/redo/revert so that the UX is consistent with that of text editors

@mjbvz I believe that should be an API to control the dirty indicator.
Also, the save option (file > save + key binding) should be enabled in custom view and there will be another API l, something like workspace.onDidSave maybe.
I'm facing with the exact issue in an extension I'm working on, also with Monaco.

@mjbvz The dirty bit is important not just for the visual indication that there are changes, but also for controlling whether the Save/Don't Save/Cancel dialog appears when the user closes the tab. Also for the "Close Unsaved" command and the like.

Without it, users are prone to losing unsaved work, which is bad.

ok. Let me throw my bits in the mix on this:

First of all this is a really great idea to simplify scaffolding of webviews and custom editors for different file types.

Question: if a custom web editor requires separate config for webview settings, connection strings, etc. would you expect it to manage that via exposed state/edit API?

I'd love to port Data Preview 🈸 to this API when it's fully baked, but I think we could use additional 'config' files mapping for the resource and such web editor. State/edit API is good enough for saves and restores, but falls short for custom editor config settings per file view. Could be a good next step to try and standardize that as part of this effort too.

See https://github.com/RandomFractals/vscode-data-preview/tree/master/data/json for some examples of how I handle it for json data files preview. Could be a good test bed too ...

In my case, I can see data preview config changes handled with undo/redo stack and save.

Do you have read/update/save and undo/redo fully implemented yet for me to try it?

My expectations are vscode would handle file changes and stream data reads of local and remote text and binary formats, per line for text and per 1K or so for binary data. see https://nodejs.org/api/readline.html for example. Any plans of adding that to worksspace.fs api any time soon? My simple data files processing use case for that scenario, whenever that fs api is available: https://github.com/RandomFractals/vscode-data-preview/issues/167 Till then I think I'll stick with nodejs fs api.

Also, I have not implemented Undo/Redo yet, but if you want a practical use case that's beyond the cat drawing apps :), see https://github.com/RandomFractals/vscode-data-preview/issues/25

@mjbvz you might find my data preview Save As use cases interesting to. Would be nice if a web editor could register save as file types, and vscode then exposed that UI to the developer via a standard UX. For most graphics views those webviews typically require custom save as png commands and UI. You can also view Vega Viewer for that scenario: https://github.com/RandomFractals/vscode-vega-viewer

@RandomFractals

  1. Not sure what you mean by separate config? Are you talking about VS Code settings? Can you share your use case

  2. Yes undo and save are ready for testing in insiders.

  3. There is no streaming built in. The current API leaves file watching and saving entirely up to extensions, so you are free to implement file syncing / save however you want including using streaming. Please file separate feature requests if the vscode fs apis don't offer what you need

  4. Creating more advanced save as flows is outside the scope of this api. You are probably better implementing a custom Save as png command for your custom editor if you need to get user input beyond the file name while saving

@KevinWuWon I think the concept of an open stack element for undo/redo may be interesting. But could you implement that using the current API by maintaining your own edit data?

If the user types hello for example:

  1. h

    • Some editing has started so push { id: 1 } onto the VS Code undo stack
      -Also, in your extension, have a map from edit id to actual edit data: { 1: [{ insert: 'h' }]}
  2. e

    • Do nothing with vs code stack
    • Update your extension's internal edit data with the new data: { 1: [{ insert: 'h' }, { insert: 'e' }]}
  3. Continue this for letters through o.

  4. At this point, your extension decide to commit the edit (create an undo stop). When this happens, on the next text change, you push a new edit with a new id onto VS Code's stack

  5. However, if an undo had occurred before you committed the edit, VS Code would tell you to undo { id: 1}. You lookup the real data for the id and then undo that

Nice idea, I think that might actually work. Now I just need Monaco to report when undo stops are created.

@mjbvz so, based on your feedback to my data read/write and save as use cases, this web editor api in its v1 incarnation will be good only for very basic read/save/update files handling with alternative webviews.

I'll open separate tickets for the scenarios I brought up. If you try Data Preview extension, you should have a fairly clear picture of what my requirements are to migrate to this api and I don't think they are advanced in any way, but perhaps could be good for your rev. 2 of this web editor api.

This iteration, we're looking into making the view / model separation more clear for webview editors. The proposed fixes and background for it are fairly verbose, so I've temporarily forked off the discussion for it to #86802. Please post feedback/questions there if you are interested in editable webview editors

Once we settle on what we are going to attempt in #86802, I'll post the results back to this issue and close #86802

@mjbvz it's so weird how you always have 10 layers of misdirection in your github issues spinoffs & PR's reassignments. a bit hard to follow your flow, but ok!

Based on the outcome of #86802, we've updated the proposed api to the following:

/**
 * Defines the editing functionality of a webview editor. This allows the webview editor to hook into standard
 * editor events such as `undo` or `save`.
 *
 * @param EditType Type of edits. Edit objects must be json serializable.
 */
interface WebviewCustomEditorEditingDelegate<EditType> {
    /**
     * Save a resource.
     *
     * @param resource Resource being saved.
     *
     * @return Thenable signaling that the save has completed.
     */
    save(resource: Uri): Thenable<void>;

    /**
     * Save an existing resource at a new path.
     *
     * @param resource Resource being saved.
     * @param targetResource Location to save to.
     *
     * @return Thenable signaling that the save has completed.
     */
    saveAs(resource: Uri, targetResource: Uri): Thenable<void>;

    /**
     * Event triggered by extensions to signal to VS Code that an edit has occurred.
     */
    readonly onEdit: Event<{ readonly resource: Uri, readonly edit: EditType }>;

    /**
     * Apply a set of edits.
     *
     * Note that is not invoked when `onEdit` is called as `onEdit` implies also updating the view to reflect the edit.
     *
     * @param resource Resource being edited.
     * @param edit Array of edits. Sorted from oldest to most recent.
     *
     * @return Thenable signaling that the change has completed.
     */
    applyEdits(resource: Uri, edits: readonly EditType[]): Thenable<void>;

    /**
     * Undo a set of edits.
     *
     * This is triggered when a user undoes an edit or when revert is called on a file.
     *
     * @param resource Resource being edited.
     * @param edit Array of edits. Sorted from most recent to oldest.
     *
     * @return Thenable signaling that the change has completed.
     */
    undoEdits(resource: Uri, edits: readonly EditType[]): Thenable<void>;
}

export interface WebviewCustomEditorProvider {
    /**
     * Resolve a webview editor for a given resource.
     *
     * To resolve a webview editor, a provider must fill in its initial html content and hook up all
     * the event listeners it is interested it. The provider should also take ownership of the passed in `WebviewPanel`.
     *
     * @param resource Resource being resolved.
     * @param webview Webview being resolved. The provider should take ownership of this webview.
     *
     * @return Thenable indicating that the webview editor has been resolved.
     */
    resolveWebviewEditor(
        resource: Uri,
        webview: WebviewPanel,
    ): Thenable<void>;

    /**
     * Controls the editing functionality of a webview editor. This allows the webview editor to hook into standard
     * editor events such as `undo` or `save`.
     *
     * WebviewEditors that do not have `editingCapability` are considered to be readonly. Users can still interact
     * with readonly editors, but these editors will not integrate with VS Code's standard editor functionality.
     */
    readonly editingDelegate?: WebviewCustomEditorEditingDelegate<unknown>;
}

namespace window {
    /**
     * Register a new provider for webview editors of a given type.
     *
     * @param viewType  Type of the webview editor provider.
     * @param provider Resolves webview editors.
     * @param options Content settings for a webview panels the provider is given.
     *
     * @return Disposable that unregisters the `WebviewCustomEditorProvider`.
     */
    export function registerWebviewCustomEditorProvider(
        viewType: string,
        provider: WebviewCustomEditorProvider,
        options?: WebviewPanelOptions,
    ): Disposable;
}

The main differences over the proposed API in VS Code 1.41:

  • The editingDelegate (previously called the editingCapabilities) is now specified on the provider instead of being returned by resolveWebviewEditor

  • This means is now a single editing delegate for all custom editors of a given type. With this setup, actions such as save should only be fired once per resource (not once per editor)

  • To support having a single delegate object, all delegate methods now take a resource that specifies what resource we are talking about

This api is now in vscode.proposed.d.ts and the example custom editors have been updated to use it

We are still looking into how best support hotexit and how many hooks we should expose to extensions around this

@mjbvz Is there a way for the extension to tell VS Code to NOT load the custom editor.
I'm thinking of scenarios such as the git diff view. When user opens a file in source control history view, then we need to display the differences. At this point, we'd prefer for VS Code to display its own text base diff viewer.

Further to this, sometimes I've found the need to view the raw file content instead of using the custom editor. Out of curiousity, is there a provision for such a scenario. E.g. displaying an icon on the top right, that allows users to open the raw viewer. Similar to displaying the icon to open settings.json file instead of the settings viewer.

@mjbvz do you have an ETA on when this api will be released as part of vscode for general public?

I've seen February release mention in some other related issue thread.

Does it mean we'll see it as part of built-in vscode api only in March?

Any heads up on your timeline for the GA relase of this api would be much appreciated to help 3rd party extension devs plan accordingly.

Thanks!

@mjbvz had a question about 'blank' files. Right now we allow a user to create a 'blank' notebook. For a normal text document, this would be handled by openTextDocument with options specifying untitled.

Would this work by just passing the correct 'language' to openTextDocument?

@mjbvz & @rchiodo I have the same scenario in my extensions like create new Vega chart || kepler.gl map.

How do we handle that with this api?

I mean I can provide Untitled templates, but what's the vscode provided tooling for that new graph || map with this custom webview editor api ;) ...

@rchiodo You can open a custom editor for an untitled file using a uri with an untitled: scheme. For a custom editor for *.abc files for example, you would open a file with the uri untitled:file.abc.

Note that you need to open this file using the vscode.open command and not openTextDocument since custom editors are not backed by text documents

@mjbvz Hi! I'm testing the experimental extension you provided (mjbvz/vscode-experimental-webview-editor-extension) and I found something that I don't quite understand.

When closing a file that contains modifications, shouldn't a confirmation popup appear? Take a look at this GIF.

ezgif-6-e75fad339660

I'm using VSCode Insiders.

Version: 1.42.0-insider
Commit: 68615717819b3731705d92ec06b52edd47665291
Date: 2020-01-22T05:26:38.760Z

@tiagobento Prompts on exit are disabled when in development mode (try with a text file as well)

@mjbvz Is that the same case for the "File > Save all" menu? I see it disabled on VSCode Insiders Version: 1.42.0-insider. If so, is there a place where I can find all the differences between VSCode Insiders VS. VScode VS. development mode?

@mjbvz is Undo/Redo supported yet? It kinda works with your ABC editor example, but it doesn't seem to actually call the undo/apply API in the editingDelegate. (I'm guessing the editor itself has a CTRL+Z handler and that's what sort of works).

yeah, this api is just so beta. why I gave up checking it. I'll wait for Py dev tools team to integrate before it's worth another look ...

@rchiodo It works using keyboard shortcuts (however not using the Edit -> Undo menu item). Try the binary editor example (which doesn't have any undo/redo logic inside of it)

@mjbvz I'm attempting to implement Undo/Redo but hitting a problem.

We currently host monaco editors inside of our webview. They also handle ctrl+Z. This makes things a little more complicated in that the new API will also handle ctrl+Z.

At first I thought I could just ignore the subsequent undo from the API, but the problem is the monaco undo is not equivalent. It undoes a bunch of edits at once and I have no way to tell the undo stack in the new API that hey, you can actually concatenate all of these together because they're on the same line (so that they match what monaco thinks).

I _think_ I either need to prevent the monaco editors from allowing undo themselves, or I would need a way to tell the API to concatenate a bunch of edit changes together.

Thoughts?

I thought I add this comment, but couldn't find it hence adding (hopefully not a dup).
Please could you add support for navigation to a specific line.
E.g. displaying problems in problems window will open the custom editor and pass the line number information accordingly (possibly a callback in the custom editor), similarly debugging will open the editor with the right like number focused (yes the custom editor will need to handle the event and display the line according).

@DonJayamanne that's an interesting idea.

I could see that work with my data preview ext. when a user selects a row in a grid to scroll the open text data document to the line of the selected row data ...

Found another problem with undo/redo which I thought I had already mentioned (sorry @mjbvz if Kai mentions it before you see this).

Follow these steps (works with the cat editor):

  1. Open a .cat file
  2. Draw on top of jpg
  3. Save
  4. Undo - File goes dirty
  5. Draw again - File goes clean

@mjbvz & @rchiodo I know you guys are still working through your cat demos & it's cute, but I think this thread and the overall api could use more down to earth examaples, like I'd like to see how 1 would use this custom webview editor api for SVG preview || dot graph preview ...

Feel free to mark this as of topic as usual || just ignore it like you have done the other comments I posted on this thread. my 2 fair cents on this dev front :)

@RandomFractals look here.
https://github.com/microsoft/vscode-python/tree/ds/custom_editor. We've implemented our notebook editor using this API.

I use the cat demo to give feedback to @mjbvz when something doesn't work in my real implementation.

@rchiodo yeah, I've been watching your progress on that :) ...

@mjbvz Hi! I have two questions:

  • Is it possible to use the default text editor as the view? I still want a custom view because it is simply a text representation of a binary file. I probably need to use something other than Webview.
  • Is it possible to write the view code in the Type Script source files, rather than attaching them in the html as a script file? (Like you did with content/binary.js)

@misode

  1. No, that is not in the scope of the custom editors api
  2. Yes, but that is also unrelated to custom editors. Just use normal html script blocks

@mjbvz why is not wiring default binary extensions in the wild like data preview ext. I procured for https://github.com/apache/arrow binary data display is not a part of your scope?

You seem to do a lot of talking & very little walking ;) ...

.@donJayamanne you can thumb it down all you like ... that is still the fact when you get down to earth in the data field in vscode ;) ...

@RandomFractals Binary files work fine. You need to be more specific if you have actual feedback

And look, I'm telling you this so that maybe you can learn something: we're looking for feedback on this API and although you certainly have left a lot of comments, I do not recall you ever providing any helpful feedback about it. In fact, I wasted a lot of time trying to support you on another thread and nothing came of it.

Worse, your tone is actually highly counterproductive. This area is very complex and passive aggressive little snipes like yours makes it not very fun for me to do the actual hard work this API requires. So really, your impact so far has been to slow down the delivery of this API.

You need to seriously recalibrate how you try to contribute to open source projects and how you interact with people in general because your current approach is not productive for anyone.

PS: As a start, make sure you next comment actually provides feedback on this API and is not some meaningless debate of what I've written here.

ok. I am considering leaving this thread. thanks for your lack of feedback on our works in the field & the feedback I actually did provide above too.

I look forward to you finally release this API for ext. devs to use for publishing in marketplace ...

P.S.: I don't think I need to re-calibrate anything. Perhaps you need to adjust your lenses & actually try the ext's trying to fit the api you are drafting. I could punch more holes in it, but I think I mentioned a few like procure an SVG ext, or dot graph || Arrow data demo display and exports to diff. fromats from those starting with png. The proposed API you started last year still can't do that per my reads & insiders runs of it.

So, please do better before you flame the only dev in vscode statosphere that actually got some fun webview editor exts for devs to play with.

Over & out! :)

Based on testing and feedback, we've decided to revise the custom editor API proposal for the VS Code 1.43 release. While this is a fairly dramatic change, many of the core concepts remain the same as before so hopefully should not be too difficult to update your existing prototypes to use the new API.

Motivation

While our existing webview editor API proposal more or less works, creating an editable webview using it is actually fairly tricky! It is especially cumbersome from simple, text based files.

We'd also really like to support bi-directional live editing of text files. For example, if I open the same file in a webview editor and in VS Code's normal editor, edits on either side should be reflected in the other. While this can sort of be implemented using the existing API, it has some big limitations and challenging to get correct.

Overview of changes

To address these problems, we've decided have two types of webview editors:

  • Text based webview editors. These editors used a TextDocument as their data model, which considerably simplifies implementing an editable webview. In almost all cases, this should be what you use for text files

  • Custom webview editors. This is basically the existing proposed API. This gives extension hooks into all the VS Code events, such as save, undo, and so on. These should be used for binary files or in complex text editor cases (such as a single editor that represents multiple resources).

Both editor types now have an explicit model layer based on documents. Text editor use TextDocument as their model, while custom editors use a WebviewEditorCustomDocument. This replaces the delegate based approach previously used.

Here's the current API proposal.

The experimental editors have booth been updated to use this new api.

Next steps

We are still actively working on:

  • Naming (hard!)
  • API details
  • Hook for file renames? (letting you preserve a webview panel instead of having it created and destroyed during the rename)
  • Bug fixes and polish

We'd also appreciate your feedback on this new proposal. Specifically, we'd like to know:

  • Can you use the new API to implement your webview editors?
  • Stumbles you ran into along the way?
  • Missing functionality?

Feedback:
I had implemented a custom webview editor against the previous version of this API and was able to update it and get it mostly working against this new version of the API, so that's good.

What is the expected way for an extension command to get the current custom webview editor? I had a couple commands using the *EditorProvider property activeEditor but that has been removed from the example, and vscode.window.activeTextEditor is always undefined for me, probably because these aren't text editors they're webview editors.

@jess-sheneberger Thanks for testing.

This is up to your extension to track using the webview panel's view state events. Our intent is that webviews can only be accessed by the extension that owns them so we don't provide global methods for getting information about webviews. This is how the existing webview api also works

Also note that the next insiders build has polished the API proposal, including renaming most of the types and contribution points:

  • "webviewEditors" contribution point -> "customEditors" contribution point
  • The name webview has been removed from most type names.
  • CustomEditorProvider are now given a document to resolve rather than having to create a document
  • There is now a single registerCustomEditorProvider method instead of one for text providers and one for custom providers

The checked in api is what we plan to ship as a proposal with VS code 1.43

Are these changes in last night's insiders build? If not, when will they be available?

I'm running into errors that make me think maybe I haven't got the right vscode bits.

Yes, they are all in today's insiders. I just confirmed the example extensions again the b0be0672c210f1fa76109ce675365bb785657e84 insiders build

OK, I'm on that build too. I get the error "Unable to open 'test.cat': Cannot add property userData, object is not extensible" when I use the latest example extensions and try to open a .cat document.

I get the same error in my extension. Please advise.

That’s a known it’s that’s already been fixed in master. You’ll have to wait until the next insiders to be able to set user data on a custom document

I have been looking at the current custom editor proposal. The problem that I see is that I don't have a 1:1 mapping between and -- my file contains many editable things.

There are two main concerns I have with the API as proposed:

  • WebviewCustomDocument is based around a URI. Imagine a resource editor for DLL files (in the PE format). Perhaps a tree view presents sections and metadata. The user then tries to double-click on one of the sub-sections; where the file in question has a clear URI, the construction of a URI that points to a particular section or sub-section within a PE file seems to me to be something that hasn't been done, and would be prone to cross-platform problems.
  • resolveCustomEditor passes in a WebviewPanel. It seems to me that this should instead present an opportunity to new a WebviewPanel to allow the extension to leverage MDI. Perhaps I'm misunderstanding; but the way my extension works (it's based with React) is that it points to sub-section of a file. File parts can be rewritten without affecting the balance of the application. This might work where the editor / document is on a per-file basis; but I provide a UI for sub-file, and I would like that.

I'd be happier with a simpler API to start, with the following capabilities:

  • backup is great as-is
  • mark the WebviewPanel as "dirty" (create the UI affordance instead of requiring me to add the * to the beginning of the file name).
  • When the panel is being closed, if it is dirty, give me an opportunity to asynchronously cause it to Save

@robpaveza

  1. If you are using a tree view type to show files that can then be edited in the custom editor, you likely should use your own uris scheme: my-fancy-dll-extension://dll-name/path/to/subresource

  2. This is how existing webview apis also work. We do this so that VS Code can easily hook up everything it needs on the webviewpanel before it gives it to extension (plus we don't want people to new up a WebviewPanel unless it is returned back to VS Code in the resolve method)

The trouble with the URI scheme of my-fancy-dll-extension://dll-name/path/to/subresource is the dll-name part. If you have two DLLs of the same name in different paths in your workspace, you can't just use dll-name, you need to use full-path-to-dll. That can work fine, but then if you're on a Unixy system, you can't just do my-fancy-dll-extension://full/path/to/dll/path/to/subresource; you need to figure out how to differentiate between the virtual part.

dll-ext://c:\projects\workspace\my.dll:cdata/0
dll-ext://home/rob/projects/workspace/my.dll:cdata/0

Becomes more complex to detect Windows and skip the first : but I guess it's possible.

@mjbvz Hi! I believe the link on https://github.com/microsoft/vscode/issues/77131#issuecomment-589894806 is pointing to an outdated version of the proposed API. The API is now different on vscode master branch and the vscode-experimental-webview-editor-extension repo as well.

@mjbvz I can confirm that the new API does fit out needs. In case you are interested, here [1] are the changes I did to adapt it our extension to the new version. I didn't test the new backup method, but all the others seem to work fine.

[1] https://github.com/tiagobento/kogito-tooling/commit/99a50b4acfde769ed1ee26f20d7e349cb80e3669

@mjbvz it's working good for us too. Had a question though. backup is working for saving, but VS code doesn't know when I load from backup vs the real file and therefore doesn't realize the file is dirty. Do we need some other event or indication that a file is dirty?

@mjbvz Hi! I'm working along with @tiagobento integrating the new Custom Webview Editor API in our editors.

I've been focused on integrating the Undo/Redo on our editor state control API. I managed to succesfully propagate each change that happens on our editor by generating an edit and firing the onDidChange event on our CustomEditorEditingCapability, however I found two issues when trying to execute the Undo/Redo from VSCode.

The first issue is that Undo/Redo menu actions under Edit menu are not working. Clicking them isn't notifying our CustomEditorEditingCapability to execute the undoEdits/applyEdits methods. However the key shortcuts (Ctrl+Z / Ctrl+Y) seem to work, and our capability is able to execute the undoEdits/applyEdits methods.

Second one only happens when running VSCode on Linux. Looks like if two or more changes happen on the editor and I execute the Undo/Redo key shortcut the undo's/redo's are executed twice. For example if we make two changes on the editor that generate two separate edits (_edit1_ and _edit2_). If later I try to undo last change (_edit2_), our capability undoEdits method is executed twice, first time for _edit2_ and then for _edit1_. As I said, the same error happens with Redo. We tried this on different computers (Fedora, Ubuntu, Mac & Windows) and it seems to be present only Linux ones. I attached some gifs showing the error on different systems.

bug_linux
_Wrong behaviour (Fedora): undoing two edits at same time_

bug_mac
_Correct behaviour (Mac): undoing only a single edit each time_

bug_Windows
_Correct behaviour (Windows): undoing only a single edit each time_

@pefernan Thanks for testing. I extracted your comment to https://github.com/microsoft/vscode/issues/92608. We're also investigating hooking up the undo/redo menu items in https://github.com/microsoft/vscode/issues/90110

March Plan

The goal for this iteration is to finalize the custom editor APIs. Editable custom binary editors still have a number of known issues, so we may make the call to keep those in proposed an extra iteration for stabilization. Both CustomTextEditor and readonly CustomEditor have far fewer remaining challenges.

API changes

As part of this finalization push, we're going to continue tweaking the API. While some of these changes may break existing extension, it should not be difficult to update them to the new API version.

Here's the main changes I've checked in:

Moving back to a delegate based model for editing

In the February release, resolveCustomDocument returned an object that describes the editing capabilities of that document. We've since decided to return to the previous delegate model where the CustomEditorProvider itself has a editingDelegate that expresses these capabilities.

I prefer the delegate based approach as will hopefully be more consistent with any future API hooks we add for custom editors. It also avoids some the the confusion about how the capabilities and the CustomDocument are linked.

Undo, save, and others can still live on your CustomDocument object if you have the editingDelegate delegate to the document.

Adding an explicit revert method

There's now a revert method. Previously revert was implemented by calling undoEdits or applyEdits.

Revert signals that the extension should reload the resource from disk, so having an explicit method for this is helpful

Adding a label for edits

When your extension creates edits, it can now pass an optional label. This will be used in the future to display information about the undo stack to users

How you can help

Once the next insiders build comes out, please update your extension to use the latest API. Please report any issues you run into with this and let us know if we've overlooked any keep features (some API features can be added after the initial custom editor api is finalized)

Again, there may be further api tweaks this iteration, but we're going to be more deliberate about them as we move towards finalization

Update — March 17, 2020

I've created a video walking through the current state of the API proposal

API Changes

  • Added cancellation tokens to the resolve methods of CustomEditor and CustomTextEditor.
Cancellation will be used in cases such as when an extension takes too long top open a editor and the user closes it before it has been resolved.
  • Added an experimental moveCustomTextEditor method to CustomTextEditor.

    This will not be finalized this iteration. It was added to make sure our API design could support such an API in the future

    The goal of moveCustomTextEditor is to allow a webview to be persisted across the rename of a resource (currently a rename causes the webview to be destroyed and then recreated). This will be useful for extensions that have expensive webviews

Ongoing

  • Naming. We're still trying to figure out good names (especially on the providers)

  • Hooking up custom editors to the backup restorer: https://github.com/microsoft/vscode/pull/92629 This is proving to be rather complicated.

  • Handling file renames properly (for example, if the file is dirty and then renamed.

Update March 23, 2020

One more set of API changes, mostly focused on improving quality of life for developers. This is the last set of changes I plan to make this iteration. If needed, we still may make a few changes based on our endgame testing and extension author feedback

API Changes

  • CustomDocument is now a class instead of an interface. Extensions can add additional data to a custom document by subclassing CustomDocument. This pattern should be more familiar than CustomDocument.userData

  • resolveCustomDocument has been renamed openCustomDocument. It now is given a uri and must return a custom document. Again, this pattern should be more familiar to developers.

  • Exposed edits on CustomDocument. CustomDocument.appliedEdits contains all edits to get the document's current state. CustomDocument.savedEdits contains all edits to get the document's last save state.

  • The third argument of registerCustomEditorProvider is now a options bag that contains a WebviewPanelOptions instead of being a WebviewPanelOptions directly

Sample extensions
Additionally, I've checked in initial custom editor extension samples: https://github.com/microsoft/vscode-extension-samples/tree/custom-editor-examples/custom-editor-sample

These samples are not yet complete but should already be much clearer (and more up to date) than the experimental-webview-editor-extension samples I previously was using for development (while also increasing cat levels considerably)

After testing and discussion among the VS Code team, we've decided to keep the custom editors for binary files as proposed one more iteration while finalizing CustomTextEditor for the current release. This means that in VS Code 1.44 extensions will be able to ship custom editors for text based files, be those file plaintext, SVG, markdown, xml, json, ...

The binary custom editor side of things (called CustomEditor in the API proposal) is considerably more complicated than the text custom editor API so we're taking time to work though some more use cases. Our focus is specifically around edits and where the editor's source of truth lives since with custom editors there are three processes at play (the webview, the extension, and VS code)

The Custom Text Editor API has shipped with VS Code 1.44! Checkout the documentation and example extension to get started creating your custom text editors

Custom Editor API Change

For binary custom editors, we've revised the proposed API for editable documents. These changes look substantial however most of the core concepts are the same as before (such as when VS code calls your extension and what your extension is responsible for doing).

You can find the current api proposal here and it will ship in the next VS Code insiders build. Here's an summary of the changes:

Removed explicit edit objects
The API previously had edit data objects and let extensions access the full undo/redo stack of edits. These have been removed .

Edits are now tracked using an object that has an undo and redo method on it. To express that an edit has occurred for example, an extension now fires an event that looks like:

this._onDidEdit.fire({
    label: 'Crop', 
    undo: () => { /* undo crop */ },
    redo: () => { /* redo crop */ },
});

There was concern that edits were too opinionated, so we decide that it is better to leave tracking edits and edits stacks up to extensions.

CustomEditorEditingDelegate -> EditableCustomDocument

The new EditableCustomDocument type is now how you implement an editable custom editor. This replaces the CustomEditorEditingDelegate we were using before. All methods from CustomEditorEditingDelegate have moved to instances of EditableCustomDocument

This pattern should be more familiar to extension authors and hopefully provides a more clear guide on how you typically want to implement a custom editor.

Slimmer CustomDocument objects

CustomDocument is now a very minimal interface instead of a class. Many of the properties that is previously shared with TextDocument have been removed since they were not very helpful in practice

Better expression of backup livecylce

Backups now have a more well specified lifecycle. Your extension returns a CustomDocumentBackup and VS Code will call .dispose() on that backup when it is no longer needed (such as when the user saves). Previously extensions had to manage the lifecycle of backups themselves

Next steps

Please try out the new API proposal and let us know if you have any concerns or feedback about it. We'll continue iterating on it this iteration so that it can ship with VS Code 1.45

Hi @mjbvz! Thanks for the updates. I think the API is looking really good. As you might know, I'm one of the maintainers of Kogito Tooling and we ship a custom editor for DMN and BPMN files as _binary_ editors.

We've recently moved to the proposed API, since we expect it to become stable soon. On VS Code 1.44.0, we're using the temporary vscode.window.registerCustomEditorProvider2 method to register our Webview editor provider.

Everything seems to work fine, with exception of the dirty detection mechanism and the undo/redo stack. Since we're counting on VS Code to call the (now deprecated) applyEdits and undoEdits methods, I can only assume that this is an issue that was introduced on 1.44.0. Also, the CustomDocumentEditEvent seems to have no effect when fired.

We've done some tests and we are in fact correctly firing CustomDocumentEditEvent when a change occurs on our editor, but VS Code doesn't seem to recognise that a change was made. This is important to us, since having no dirty detection mechanism can lead to loss of work.

I know this API is currently on proposed state and that it has already been deprecated, but we already did all the testing we could think of and it would be nice if some of you guys could confirm that this is in fact an issue on VS Code side. We would be happy to help testing it further when/if a fix is provided.

Just FYI, we will definitely adapt our codebase to new API as described here https://github.com/microsoft/vscode/issues/77131#issuecomment-611282903 and we're looking forward to shipping our extension with the stable API as soon as it is released.

Thank you in advance 😊

@tiagobento Thanks for testing!

The current custom editor API from vscode.proposed.d.ts only works in that latest VS Code insiders builds (VS Code 1.45). Please make sure you are testing using that pairing

If you are still having issues using the new API in VS Code insiders, can you please share a branch of your extension that is using it so that I can take a look to see what may be going on?

@mjbvz Thx for the reply!

We haven't tested the API that's on master yet. But the API that's on 1.44.0 has an issue with undo/redo and dirty state on VS Code 1.44.0.

It looks like VS Code is not calling the applyEdits and undoEdits methods and also that it's not detecting a dirty state after we fire CustomDocumentEditEvent.. do you know if that's expected?

@tiagobento Since no extensions are can ship with proposed APIs, only the version from master with the latest insiders build is supported. Please update your code to use that

@tiagobento Can you point us to your branch so that we can understand how the API is working for you

@jrieken Sure! That's [1] where we register the webview provider. We're currently working on the 0.3.x branch.

EDIT: This 0.3.x branch is currently targeting VS Code 1.44.0. We're already working on implementing the API for 1.45.0 on master.

[1] https://github.com/kiegroup/kogito-tooling/blob/0.3.x/packages/vscode-extension/src/KogitoWebviewProvider.ts

Hi @mjbvz. I'm working with @tiagobento and @pefernan.
I had the chance to test the proposed API from master with the latest insiders build.
Everything seems to be working on our custom editors, except:

  • The issues reported here are still reproducible;
  • Saving with Ctrl+S: although the operation works, save is being fired twice on our EditableCustomDocument and VS code is showing the message "_Failed to save 'fileXXXX': Canceled_". However, if I try to save from the File menu, then it works fine.

Hope this helps.

Update April 16, 2020

The next code insiders makes a few minor tweaks to the api in order to support a wider range use cases. Here are the details

onDidEdit has been renamed to onDidChange

Added CustomDocumentContentChangeEvent which marks an editor as dirty

While reviewing some of the editor libraries that we expect extension developers to try using in their custom editors, we determined that custom editors generally fall into three different classes:

  • Readonly editors. These already work well today.

  • Editable custom editors that can't integrate into the VS Code's undo/redo stack.

    The editor libraries being provide a way to edit files, but either don't have undo/redo functionality or their undo/redo model doesn't match what VS Code needs.

  • Full custom editors with a stack based undo/redo model. The are what we target today.

Our API supported class 1 and 3 pretty well, but class 2 editors were out of luck. Such editors would either have to claim they were readonly or try to abuse our undo/redo api proposal. Neither are good outcomes.

Asking all editors to match VS code's undo/redo model may be unrelasitic but we still need a way for an editor to all us that they are dirty. Being dirty triggers auto save, backup, and also causes VS code to prompt the user if they try to close a dirty file without saving it first. However we don't want to give extensions direct control over this dirty state because implementing it correctly is surprisingly complicated and the consequences of messing us dirty state tracking are pretty bad: such as lost user data and never being able to close an editor.

So to support this middle class of editors, we decided to add an CustomDocumentContentChangeEvent that an editor can fire if they cannot use the full CustomDocumentEditEvent with undo/redo. CustomDocumentContentChangeEvent only marks an editor as dirty. The only way for an editor that uses CustomDocumentContentChangeEvent to become non-dirty is for the user to save or revert the file. These editors also have no integration with VS Code's standard undo/redo commands.

Keep in mind that CustomDocumentContentChangeEvent has been added to support those cases where undo/redo simply will not work. To provide your users with the best experience, you should use CustomDocumentEditEvent and ensure your editors play nicely with VS Code's undo/redo.

Added an supportsMultipleEditorsPerResource option (the default is false)

Another concern we discussed is that not all custom editors will be able to correctly support having multiple editors instances for the same resource. This is important with editable custom editors since all editor instances for a resource must be in the same state.

Given the complexity of properly synchronizing multiple instances of an editor—as well as the fact that much of this complexity may not be well understood or tested by extension authors—we've decided to only allow a single default instance of a custom editor per resource by default.

If you pass in supportsMultipleEditorsPerResource: true to registerCustomEditorProvider2 however, VS Code will again allow users to split custom editors and open multiple copies of a custom editor for the same resources.

Again, if you want your users to have the best experience with a custom editor, make sure your editors can properly synchronize with each other and then set supportsMultipleEditorsPerResource: true.

@caponetto Thanks for testing this. I just posted an update to #92608: those issues should be fixed now or are known limitations currently.

The duplicate save had the same root cause a the duplicate undo/redo, #82670. This should be fixed in the next VS Code insiders build

We're already working on implementing the API for 1.45.0 on master.

@tiagobento and @pefernan have you been able to make progress? As @mjbvz outlined above we have changed the API proposal in a few significant ways and we are looking for your feedback. Specifically

  • save-as, backup, revert don't seem to be implemented in the 0.3.x branch and we wonder why that is? Can your files not be saved under different names or is this simply not yet done?
  • I wasn't able to find the actual undo/redo implementation on the 0.3.x branch and given undo/redo has significantly changed we would like to see how you implement that. Code pointers or questions are appreciated.

@jrieken Thanks for taking some time to look int our code base! We really appreciate that.

  • save-as, backup, revert will most likely be implemented in the future. Our 0.3.x branch is considered a Development Preview to us, so it's expected that some features are not ready yet. 0.3.x will also be updated to the new API once VS Code 1.45.0 is released. We wanted users to have the opportunity to use our extension as quickly as possible, so we have this preliminary version in place. We'll share more news as this branch gets updated :)
  • On 0.3.x, we're using VS Code's 1.44.x proposed API, so undo/redo is implemented using the applyEdits and undoEdits methods. Since VS Code calls these methods when undo and redo commands are fired, we then simply warn our editor to change its contents.

@caponetto is working on implementing the 1.45.0 API on our master and so far the results are good. These are the issues we were able to find:

When we revert changes using the version control tab, the revert method is not called.

Yeah - revert isn't the SCM revert (and I now see how that is confusing) but it is File > Revert File. It's used when having unsaved changes and when you don't want to save such changes.

Since VS Code calls these methods when undo and redo commands are fired, we then simply warn our editor to change its contents.

Ok, but this is also where the biggest changes happened, e.g the "source of truth" used to be with us and is now with extensions. We aren't talking about concrete edits anymore but simply about undo/redo-functions which must implement undo and redo:

  • before: undo happened, we have updated the undo/redo stack already, and now the editor must catch up with the truth
  • now: undo happening, we are in middle of updating the undo/redo stack, and now the editor must perform the actual undo

April 24, 2020

We've decided to keep the binary custom editor API proposed one more iteration. This lets us better align the custom editor API with the notebook API proposal, while also giving more time for you to provide feedback on it.

The currently version of the API checked into vscode.proposal.d.ts is what we plan to ship with VS Code 1.45. Here's an overview of recent changes:

Moved edit, save, revert, backup from documents back to the provider

In order to align with the notebook API proposal, we've moved the edit, save, revert, and backup function out of EditableCustomDocument and back onto the custom editor provider:

export interface CustomEditorProvider<T extends CustomDocument = CustomDocument> extends CustomReadonlyEditorProvider<T> {

    readonly onDidChangeCustomDocument: Event<CustomDocumentEditEvent> | Event<CustomDocumentContentChangeEvent>;

    saveCustomDocument(document: CustomDocument, cancellation: CancellationToken): Thenable<void>;

    saveCustomDocumentAs(document: CustomDocument, destination: Uri, cancellation: CancellationToken): Thenable<void>;

    revertCustomDocument(document: CustomDocument, cancellation: CancellationToken): Thenable<void>;

    backupCustomDocument(document: CustomDocument, context: CustomDocumentBackupContext, cancellation: CancellationToken): Thenable<CustomDocumentBackup>;
}

We've gone back and forth a few times about where these methods should live, but ultimately it is important to align with the notebook api here. Extension authors can also easily have the provider call into methods on their custom document.

CustomReadonlyEditorProvider

The CustomReadonlyEditorProvider interface is now the base for a custom editors provider. It has the same two methods the previous proposal did:

export interface CustomReadonlyEditorProvider<T extends CustomDocument = CustomDocument> {

    openCustomDocument(uri: Uri, openContext: CustomDocumentOpenContext, token: CancellationToken): Thenable<T> | T;

    resolveCustomEditor(document: T, webviewPanel: WebviewPanel, token: CancellationToken): Thenable<void> | void;
}

If your binary custom editors are editable, you would instead implement CustomEditorProvider which defines all the required editing hooks.

CustomDocumentBackupContext.destination

The CustomDocumentBackupContext.destination now suggests a specific file name to use for a backup. You extension does not have to write to this file, it is simply a suggestion.


Again, the current API in master is what plan to ship in VS Code 1.45. Provided no major issues are discovered next month, this is api is also what we plan to finalize.

If you have an extension in mind that would use binary custom editors, please give the new API a try and let us know if it works for you. We're specifically interested in understanding if you could implement all the required functions and if you ran into any confusion while doing so.

Hi @mjbvz @jrieken,

I've been catching up with the daily updates, thanks for them btw.
While working on our stuff, I've been noticing some issues on VSCode.
Probably they're known issues but I'm sharing here in case they've been missed somehow.
I'm able to reproduce them on both our code and this code.

Linux and Windows

  • [File -> Auto Save] Auto save enabled: "Save" is not triggered if you quickly (less than 1s) close the editor after making a change.

Linux-only

  • [File -> Auto Save] The "check" symbol is not shown when you enable "auto save" within the custom editor; however, the operation works.
  • [File -> Revert File] Sometimes the menu button is kept disabled when you start VSCode, even after changes.
  • [File -> Save All] Same behavior ^
  • [File -> Save] Same behavior ^; Ctrl+S works.
  • [File -> Save As] Same behavior ^; Ctrl+Shift+S works.
    _note: the menu buttons become enabled when I open a text editor_

Windows-only

  • [File -> Revert File] Can't use; always disabled.

Please let me know if you need any further information.

@mjbvz one thing to note is that auto-save for text editors is not editor bound, so it will continue after an editor closes. Maybe for custom editors this is different and you need to wire this in properly.

Regarding the backup feature.
I played a little bit more with the sample code but the restore part doesn't seem to be working.
I can see that the backup file is being created/deleted when appropriated,
but the backupId received on the openCustomDocument method is always undefined.

I understood that if I close the VSCode with unsaved changes,
then these changes will be restored once I open VSCode again.
Here's a gif where I do these steps.
Peek 2020-04-29 08-00

Am I missing something here?
Thanks in advance.

@caponetto See #96484

I'd like my custom text editor to have a different UI when a document is open normally versus in the source control diff view. (There are features that are unneeded in a diff.) However, given the arguments to CustomTextEditorProvider.resolveCustomTextEditor, I don't see what logic I can write to tell that I'm in a diff view. In fact, for my use case, I'd prefer that Code use a regular text editor for diffs rather than creating my custom text editor. Maybe there's a way to specify that?

Hi all. I’m looking for some advice to address several of my concerns implementing a custom text editor.

I’m working on a WYSIWYG markdown editor -- you can play with the technical prototype here: https://editor-v2-arch.netlify.app. I’ve been working on making the extension-version for VSCode, but I’m honestly pretty confused. I’ve followed the abc (textarea) and catScratch demos for reference.

What I’m most confused about is undo and cursor selection.

So when my editor is an extension, the undo stack actually appears to live _outside_ of my editor, right? The reason this confuses me is because I _already_ implemented all of the undo functionality myself, but now I need to let VSCode handle that for me? Am I simply not allowed to intercept undo/redo events myself and handle them? Is there a way I can change the relationship so my editor is in charge of handling undo/redo? Having this work the other way around is seriously confusing me.

I understand that I can emit edits on every event, and message-pass those edits to VSCode, but when a user hits undo, from what I can tell, the way my editor is made _aware_ of this is by VSCode message-passing me the entire document as a string (naive implementation) or VSCode passing individual edits I can respond to. If I respond to individual edits, this seems fine (I would need to rearchitect some things to make this work) but this leads me to my second point: cursor selection.

When a user presses undo, they expect the cursor to revert to where it was. So assuming my editor responds to edits (as mentioned in the previous paragraph), does this mean I need to _compute_ the cursor? Right now, my undo history stack tracks the cursor positions, so there’s no need for me to compute anything during an undo, redo event. But this _seems_ like something else I would need to architect in order to get even the most basic editor behaviors working.

All things considered, making my editor VSCode-ready is proving to be way more difficult than I would have imagined. I hope I’ve got some things wrong here and there’s a much simpler way for me to port my editor.

@codex-zaydek You are on the wrong issue, this is about custom _binary_ editors, please create a new one.

@mjbvz I was wondering how I would create an untitled editor with the new API? I believe we used to do this with a uri of "untitled:Untitled-1.ipynb", but this gives an error now:

image

Adding a '/' to the front of the file name seems to allow the creation of the file, but it's not treated as untitled (it's just treated as a real file then and closing doesn't ask to save as)

```

Ah it seems there's already a discussion of this here:
https://github.com/microsoft/vscode/issues/93441

@rchiodo yes #93441 tracks a better solution. Until then, you should be able to create untitled files by using an untitled uri that points to a specific file path:

untitled:/absolute/path/to/file/in/workspace.png

The binary custom editor API has been merged into vscode.d.ts.

If you were testing this proposed API previously, please make sure to update your code to call registerCustomEditorProvider instead of registerCustomEditorProvider2. Other than that, there should be no changes to the API

Is there a way to change the title of a custom editor?
Right now my extension uses a webview witha custom title when editing a certain file, but I need the save/close features of the custom editor API.

@DemonOne No, custom editors currently always use the 'resource' as their title (they try to match what we do for normal text editors)

The binary custom editor API will ship with VS Code 1.46 šŸŽ‰ Thanks to everyone who helped test this API and provided feedback along the way

Follow up resources:

Going forward, please open any bugs/feature-requests/questions as new issues.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

lukehoban picture lukehoban  Ā·  3Comments

trstringer picture trstringer  Ā·  3Comments

mrkiley picture mrkiley  Ā·  3Comments

borekb picture borekb  Ā·  3Comments

chrisdias picture chrisdias  Ā·  3Comments