microsoft / vscode

Visual Studio Code
https://code.visualstudio.com
MIT License
164.25k stars 29.3k forks source link

Custom webview editor API #77131

Closed mjbvz closed 4 years ago

mjbvz commented 5 years ago

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:

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:

mjbvz commented 5 years ago

Two main parts of this that I see:

mjbvz commented 5 years ago

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

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

jrieken commented 5 years ago

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

TomMarius commented 5 years ago

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.

sijakret commented 5 years ago

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.. ;)

mjbvz commented 5 years ago

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

Disadvantages

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

Tyriar commented 5 years ago

Some thoughts:

mjbvz commented 5 years ago

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:

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)

jrieken commented 5 years ago

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.

bpasero commented 5 years ago

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.

jrieken commented 5 years ago

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

bpasero commented 5 years ago

To be clear, here is what I expect:

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

Tyriar commented 5 years ago

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.

KevinWuWon commented 5 years ago

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.

mjbvz commented 5 years ago

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}"
        }
      ]
    }
  ]
}

Other changes

Still todo

KevinWuWon commented 5 years ago

Fantastic, thank you! Looking forward to Save :)

bpasero commented 5 years ago

@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?

KevinWuWon commented 5 years ago

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?

jrieken commented 5 years ago

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

KevinWuWon commented 5 years ago

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

jrieken commented 5 years ago

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)

mjbvz commented 5 years ago

@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:

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

bpasero commented 5 years ago

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 👍

mjbvz commented 5 years ago

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:

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:

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:

A writable, text based editor

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

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

Questions

Other thoughts or concerns?

jmew commented 5 years ago

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!

rchiodo commented 5 years ago

@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.

mjbvz commented 5 years ago

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

rchiodo commented 5 years ago

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.

Tyriar commented 5 years ago

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.

KevinWuWon commented 5 years ago

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.

saqadri commented 5 years ago

@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:

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.

KevinWuWon commented 5 years ago

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.

Tyriar commented 5 years ago

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.

hyperfekt commented 5 years ago

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.

mjbvz commented 5 years ago

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

tiagobento commented 5 years ago

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

WebviewEditorEditingCapability#save

WebviewEditorEditingCapability#hotExit

WebviewEditorEditingCapability#onEdit

WebviewEditorEditingCapability#applyEdits and WebviewEditorEditingCapability#undoEdits

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?

reanimatedmanx commented 5 years ago

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

DonJayamanne commented 5 years ago

@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.

@kieferrm @greazer /cc

DonJayamanne commented 5 years ago

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

mjbvz commented 4 years ago

@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

mjbvz commented 4 years ago

@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

mjbvz commented 4 years ago

Update

Here's where things stand on the proposal:

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:

testforstephen commented 4 years ago

@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.

mjbvz commented 4 years ago

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

testforstephen commented 4 years ago

@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.

mjbvz commented 4 years ago

@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

KevinWuWon commented 4 years ago

@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.

idoprz commented 4 years ago

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.

ThatRendle commented 4 years ago

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.

mjbvz commented 4 years ago

@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