-
Notifications
You must be signed in to change notification settings - Fork 29.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
API gaps to support Liveshare in Notebook #102503
Comments
Below is a draft list of missing API/features to support co-editing in notebook after analyzing how LiveShare uses text editor APIs.
|
@rebornix Would there be work left for Live Share to do after you make the above changes? Or would notebooks magically "look" like a series of documents to Live Share? 😄 This might be a silly question, I just wanted to check, so we could plan ahead 👍 |
@lostintangent Since the notebook editor/documents are new concepts, it won't work out of the box after we make above changes. Live Share at least needs to
|
Examples of how the notebook APIs can be used together with Text Editor APIs for Live Share Active Editor changevscode.window.onDidChangeActiveTextEditor((e) => {})
vscode.notebook.onDidChangeActiveNotebookEditor((e) => {}) Document lifecyclevscode.workspace.onDidCloseTextDocument
vscode.workspace.onDidOpenTextDocument
vscode.workspace.onDidSaveTextDocument
vscode.notebook.onDidOpenNotebookDocument
vscode.notebook.onDidCloseNotebookDocument
vscode.notebook.onDidSaveNotebookDocument Content Changes of a notebook documentvscode.workspace.onDidChangeTextDocument((e) => {
const document = e.document;
const notebookDocument = document.notebook;
});
vscode.notebook.onDidChangeNotebookCells(e => {
const notebookDocument = e.document;
});
vscode.notebook.onDidChangeCellOutputs
vscode.notebook.onDidChangeCellLanguage
vscode.notebook.onDidChangeCellMetadata Participant selection changevscode.window.onDidChangeTextEditorSelection(e => {
const selections = e.selections;
});
vscode.notebook.onDidChangeNotebookEditorSelection(e => {
const selection = e.selection; // a notebook cell
}); Unfold recursively from an index in the notebook document to the topvscode.commands.executeCommand('notebook.unfold', {
"index": 1,
"direction": "up",
"levels": 65535
}); Cell EditingDecorations
Viewport information// events
vscode.notebook.onDidChangeNotebookEditorVisibleRanges
// visible ranges info
NotebookEditor.visibleRanges
// reveal a range
notebookEditor.revealRange({ start: 10, end: 10}, vscode.NotebookRevealType.InCenter); |
…tebook." This reverts commit b4b67b8.
@rebornix This is looking awesome! Tagging @daytonellwanger as an FYI, since we'll be looking into adding this Live Share support soon 👍 |
Had offline discussion with @daytonellwanger and found more API limitations:
Code snippets will be tracked in https://github.com/microsoft/notebook-extension-samples/tree/master/notebook-vsls |
While attempting to support exclusive content provider registration at runtime, I found more open questions than listed above. Thus we took one step back and re-evaluate what's the best strategy to support notebook editing in Live Share environment:
|
@rebornix 🙌 If possible, it would be awesome to enable option #2 above. Since VS Code already provides the UI/core functionality of notebooks, being able to leverage kernels from the host would enable the setup-free guest experience we already have with language services, task providers, etc. Where are you and @daytonellwanger leaning? |
@lostintangent thanks for your feedback, we are leaning towards option 2 as well (majorly for the same reason you mentioned above). option 1 is listed there to ensure us not miss any interesting scenarios and unveil all possible challenges. |
@daytonellwanger already pushed the initial notebook support in Live Share and while doing that, we figured out the LS extension may need more info from VS Code
|
It should focus the notebook in the specified column and ideally scroll to the position of the specified cell.
|
@daytonellwanger |
@rebornix perfect! I'll give it a try. |
@daytonellwanger I just added const editor = await vscode.window.showNotebookDocument(document, { viewColumn: vscode.ViewColumn.Beside, selection: { start: 10, end: 11 } }); |
This one is really challenging to fix completely so instead we pushed a workaround, if there is a decoration which has Once the |
@daytonellwanger Does your list of asks enable us to implement complete "follow" support for notebooks? In addition to being able to open a notebook to a specific scroll position and cell, are we also able to track/synchronize subsequent cell navigations and notebook scrolls? The answer is probably mentioned above, but I just wanted to double-check 👍 |
In order to allow the Live Share extension to create mirror kernels on guest side, we introduced two more API commands CommandsRegistry.registerCommand('_resolveNotebookKernelProviders', async (accessor, args): Promise<{
extensionId: string;
description?: string;
selector: INotebookDocumentFilter;
}[]>
CommandsRegistry.registerCommand('_resolveNotebookKernels', async (accessor, args: {
viewType: string;
uri: UriComponents;
}): Promise<{
id?: string;
label: string;
description?: string;
detail?: string;
isPreferred?: boolean;
preloads?: URI[];
}[]> |
If a file has multiple content providers available, the user can choose which content provider to use. It would be an awkward experience to lock the host and guest into using the same content provider. A couple examples:
I believe the correct UX is for host and guest to be allowed to independently select a content provider (note that if they're using different content providers, coediting will not be supported). Today the If different participants are using different content providers, their changes will be synced when the content provider persists them. This presents no difficulties if the file is clean for all other participants - we simply update their contents. However, if Participant A has a file open with Content Provider A, and has made changes but not persisted them, and Participant B has the same file open with Content Provider B and just persisted changes, this should be treated the same as a file being updated on disk when open with dirty changes - when Participant A attempts to persist the file, they will receive "The content of the file is newer. Please compare your version with the file contents or overwrite the content of the file with your changes." |
That will be VERY complex as internally everything is mapped by its uri and it means we need to invent some uri-format for which combines the actual uri and provider type. We could try to use a query-string for that but I have doubts that this will be easy... |
@jrieken is there another alternative to avoid the awkward situations described above? I imagine the user will be quite confused if they go to open the notebook and explicitly select a content provider and then we open it with a different one. Or if the content provider is changed out from under them in the middle of a session. I suppose we could show a notification to the user anytime we encounter one of these to give some explanation, but it still feels like a bad experience. |
I don't know if there is an alternative. The challenge is that there is one file on disk (one sequence of bytes) which is interpreted different but always referred to by the same name. So, it is good that this is on the table. It needs some thinking and code analysis to understand the full impact here. |
Thanks for the info. For Live Share's v1 then, we'll move forward with the constraint that the notebook can only be open by a single content provider at a time. For most scenarios this should be fine, as I imagine it's rare that host and guest would want to use different content providers. However, if it were simple to support multiple content providers, or there were other use cases that required it, that would of course be preferred for the Live Share scenario 😊. |
Thanks @jrieken for bringing up the potential issues of multiple content providers, I did some initial analysis of how we use the Uri. Since we have the restriction in place, when we designed the Uri for notebook document and notebook cell, they don't take view type into account. It made the Uri simple and compatible with existing workbench features. The notebook document Uri is the resource Uri, untouched. Users can open a resource in both text editor and notebook editor, since they are different editor types (users can also open the resource in a custom editor), we can always identify them. A typical notebook document uri might be like The notebook cell uri is based on the notebook uri, with one more information cell handle. Thus the notebook cell Uri will carry following info
The
The other significant way of using
Last but not least, export interface WorkspaceEdit {
replaceNotebookMetadata(uri: Uri, value: NotebookDocumentMetadata): void;
replaceNotebookCells(uri: Uri, start: number, end: number, cells: NotebookCellData[], metadata?: WorkspaceEditEntryMetadata): void;
replaceNotebookCellOutput(uri: Uri, index: number, outputs: (NotebookCellOutput | CellOutput)[], metadata?: WorkspaceEditEntryMetadata): void;
replaceNotebookCellMetadata(uri: Uri, index: number, cellMetadata: NotebookCellMetadata, metadata?: WorkspaceEditEntryMetadata): void;
} A short summary of above is to support multi content providers, we can no longer use Uri as the identifier for a notebook resource, instead we need to use a tuple Considering it's not just a few hundreds lines of changes, we may want to stick with what we currently have for v1. We can probably improve the error notification, e.g., add a button to save+close old notebook and then open the document in the new view type. |
Before migrating to serializers, Live Share needs a way to get a list of the available serializers, similar to the command |
Closing this now as @mjbvz is working with Live Share on the API update. |
Currently the editing API for Notebook is slim and may not support all features of Live Share.
The text was updated successfully, but these errors were encountered: