From d9bdf6af3960cda22e155f822a90649c1b58a403 Mon Sep 17 00:00:00 2001 From: Rich Chiodo Date: Mon, 3 Jan 2022 15:29:27 -0800 Subject: [PATCH] Generate web output (#2) * Generate web output * Update package number --- package-lock.json | 2 +- package.json | 2 +- src/common/utils.ts | 3 + src/index.ts | 4 +- src/notebookConcatDocument.ts | 6 +- src/notebookConverter.ts | 223 ++++++++++++++++------------------ webpack.config.js | 5 +- 7 files changed, 120 insertions(+), 125 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9d7d038..938c2a9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@vscode/lsp-notebook-concat", - "version": "0.1.2", + "version": "0.1.3", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index dd20ae2..27ac538 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@vscode/lsp-notebook-concat", - "version": "0.1.2", + "version": "0.1.3", "description": "Notebook cell concatentation for language servers", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/src/common/utils.ts b/src/common/utils.ts index f83caff..acc8662 100644 --- a/src/common/utils.ts +++ b/src/common/utils.ts @@ -42,6 +42,9 @@ export function isInteractiveCell(cellUri: vscodeUri.URI): boolean { cellUri.scheme.includes(InteractiveScheme) ); } +export function isNotebookCell(uri: vscodeUri.URI): boolean { + return uri.scheme.includes(NotebookCellScheme) || uri.scheme.includes(InteractiveInputScheme); +} export function splitLines(str: string): string[] { let lines = str.split(/\r?\n/g); diff --git a/src/index.ts b/src/index.ts index 966a811..570c503 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,6 +3,6 @@ import * as vscodeUri from 'vscode-uri'; import { NotebookConverter } from './notebookConverter'; -export function createConverter(getNotebookHeader: (uri: vscodeUri.URI) => string): NotebookConverter { - return new NotebookConverter(getNotebookHeader); +export function createConverter(getNotebookHeader: (uri: vscodeUri.URI) => string, platformGetter: () => string): NotebookConverter { + return new NotebookConverter(getNotebookHeader, platformGetter); } diff --git a/src/notebookConcatDocument.ts b/src/notebookConcatDocument.ts index 3dff4c9..4b4448a 100644 --- a/src/notebookConcatDocument.ts +++ b/src/notebookConcatDocument.ts @@ -93,6 +93,10 @@ export class NotebookConcatDocument implements ITextDocument { private _lines: NotebookConcatLine[] = []; private _realLines: NotebookConcatLine[] = []; + public static getConcatDocRoot(cellUri: vscodeUri.URI) { + return path.dirname(cellUri.fsPath); + } + constructor(public key: string, private readonly getNotebookHeader: (uri: vscodeUri.URI) => string) {} // Handles changes in the real cells and maps them to changes in the concat document. @@ -879,7 +883,7 @@ export class NotebookConcatDocument implements ITextDocument { private initialize(cellUri: vscodeUri.URI) { if (!this._concatUri?.fsPath) { this._interactiveWindow = isInteractiveCell(cellUri); - const dir = path.dirname(cellUri.fsPath); + const dir = NotebookConcatDocument.getConcatDocRoot(cellUri); // Path has to match no matter how many times we open it. const concatFilePath = path.join( diff --git a/src/notebookConverter.ts b/src/notebookConverter.ts index 53ef748..b6c9783 100644 --- a/src/notebookConverter.ts +++ b/src/notebookConverter.ts @@ -1,10 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import * as os from 'os'; import * as vscodeUri from 'vscode-uri'; import * as protocol from 'vscode-languageserver-protocol'; -import { InteractiveInputScheme, InteractiveScheme, NotebookCellScheme } from './common/utils'; +import { InteractiveInputScheme, InteractiveScheme, isNotebookCell } from './common/utils'; import { IDisposable, ITextDocument, RefreshNotebookEvent } from './types'; import { NotebookConcatDocument } from './notebookConcatDocument'; import { createLocation, createPosition, createRange } from './helper'; @@ -21,9 +20,9 @@ export class NotebookConverter implements IDisposable { private mapOfConcatDocumentsWithCellUris = new Map(); - constructor(private getNotebookHeader: (uri: vscodeUri.URI) => string) {} + constructor(private getNotebookHeader: (uri: vscodeUri.URI) => string, private platformGetter: () => string) {} - private static getDocumentKey(uri: vscodeUri.URI): string { + private getDocumentKey(uri: vscodeUri.URI): string { if (uri.scheme === InteractiveInputScheme) { // input const counter = /InteractiveInput-(\d+)/.exec(uri.path); @@ -37,7 +36,7 @@ export class NotebookConverter implements IDisposable { } // Use the path of the doc uri. It should be the same for all cells - if (os.platform() === 'win32') { + if (this.platformGetter() === 'win32') { return uri.fsPath.toLowerCase(); } return uri.fsPath; @@ -49,24 +48,26 @@ export class NotebookConverter implements IDisposable { public hasCell(cell: protocol.TextDocumentIdentifier): boolean { const concat = this.getConcatDocument(cell); - return concat?.contains(cell.uri) ?? false; + return concat.contains(cell.uri); } public isOpen(cell: protocol.TextDocumentIdentifier): boolean | undefined { - const concat = this.getConcatDocument(cell); - if (concat) { - return concat.isOpen; + const uri = this.toURI(cell); + const key = this.getDocumentKey(uri); + const result = this.activeConcats.get(key); + if (!result) { + return false; } - return undefined; + + return result.isOpen; } public handleOpen(ev: protocol.DidOpenTextDocumentParams) { const concat = this.getConcatDocument(ev.textDocument); const results = concat?.handleOpen(ev); - if (concat) { - // concat uri is empty until a cell is added. - this.activeConcatsOutgoingMap.set(NotebookConverter.getDocumentKey(concat.concatUri), concat); - } + + // concat uri is empty until a cell is added. + this.activeConcatsOutgoingMap.set(this.getDocumentKey(concat.concatUri), concat); return results; } @@ -78,26 +79,26 @@ export class NotebookConverter implements IDisposable { public handleClose(event: protocol.DidCloseTextDocumentParams) { const concat = this.getConcatDocument(event.textDocument.uri); - return concat?.handleClose(event); + return concat.handleClose(event); } public handleChange(event: protocol.DidChangeTextDocumentParams) { const concat = this.getConcatDocument(event.textDocument.uri); - return concat?.handleChange(event); + return concat.handleChange(event); } public toNotebookDiagnosticsMap( - uri: protocol.TextDocumentIdentifier | string, + concatUri: protocol.TextDocumentIdentifier | string, diagnostics: protocol.Diagnostic[] ): Map { - const concat = this.getConcatFromOutgoingUri(uri); + const concat = this.getConcatDocumentForUri(concatUri); const result = new Map(); if (concat) { // Diagnostics are supposed to be per file and are updated each time // Make sure to clear out old ones first const cellUris: string[] = []; - const oldCellUris = this.mapOfConcatDocumentsWithCellUris.get(uri.toString()) || []; + const oldCellUris = this.mapOfConcatDocumentsWithCellUris.get(concatUri.toString()) || []; concat.getCells().forEach((uri) => { result.set(uri.toString(), []); cellUris.push(uri.toString()); @@ -107,7 +108,7 @@ export class NotebookConverter implements IDisposable { oldCellUris .filter((cellUri) => !currentCellUris.has(cellUri)) .forEach((cellUri) => result.set(cellUri, [])); - this.mapOfConcatDocumentsWithCellUris.set(uri.toString(), cellUris); + this.mapOfConcatDocumentsWithCellUris.set(concatUri.toString(), cellUris); // Then for all the new ones, set their values. diagnostics.forEach((d) => { @@ -124,13 +125,13 @@ export class NotebookConverter implements IDisposable { list.push(this.toNotebookDiagnostic(location.uri, d)); } }); - } else if (this.mapOfConcatDocumentsWithCellUris.has(uri.toString())) { - (this.mapOfConcatDocumentsWithCellUris.get(uri.toString()) || []).forEach((cellUri) => + } else if (this.mapOfConcatDocumentsWithCellUris.has(concatUri.toString())) { + (this.mapOfConcatDocumentsWithCellUris.get(concatUri.toString()) || []).forEach((cellUri) => result.set(cellUri, []) ); - this.mapOfConcatDocumentsWithCellUris.delete(uri.toString()); + this.mapOfConcatDocumentsWithCellUris.delete(concatUri.toString()); } else { - result.set(this.toURI(uri).toString(), diagnostics); + result.set(this.toURI(concatUri).toString(), diagnostics); } return result; @@ -181,35 +182,27 @@ export class NotebookConverter implements IDisposable { public toConcatDocument(cell: protocol.TextDocumentIdentifier): protocol.TextDocumentItem { const result = this.getConcatDocument(cell); - if (result) { - return { - text: result.getText(), - uri: result.uri.toString(), - languageId: result.languageId, - version: result.version - }; - } + return { - text: '', - uri: cell.uri, - languageId: 'python', - version: 1 + text: result.getText(), + uri: result.uri.toString(), + languageId: result.languageId, + version: result.version }; } public toConcatTextDocument(cell: protocol.TextDocumentIdentifier): ITextDocument { - const result = this.getConcatDocument(cell); - return result; + return this.getConcatDocument(cell); } public toConcatUri(cell: protocol.TextDocumentIdentifier | string): string { const result = this.getConcatDocument(cell); - return result ? result.concatUri.toString() : ''; + return result.concatUri.toString(); } public toConcatPosition(cell: protocol.TextDocumentIdentifier, position: protocol.Position): protocol.Position { const concat = this.getConcatDocument(cell); - return concat ? concat.concatPositionAt(createLocation(cell.uri, createRange(position, position))) : position; + return concat.concatPositionAt(createLocation(cell.uri, createRange(position, position))); } public toConcatPositions(cell: protocol.TextDocumentIdentifier, positions: protocol.Position[]) { @@ -221,12 +214,10 @@ export class NotebookConverter implements IDisposable { cellRange: protocol.Range | undefined ): protocol.Range { const concat = this.getConcatDocument(cell); - if (concat) { - const uri = this.toURI(cell); - const range = concat.concatRangeOf(uri); - return range || cellRange || createRange(createPosition(0, 0), createPosition(0, 0)); - } - return cellRange || createRange(createPosition(0, 0), createPosition(0, 0)); + + const uri = this.toURI(cell); + const range = concat.concatRangeOf(uri); + return range || cellRange || createRange(createPosition(0, 0), createPosition(0, 0)); } public toRealRange( @@ -234,12 +225,10 @@ export class NotebookConverter implements IDisposable { cellRange: protocol.Range | undefined ): protocol.Range { const concat = this.getConcatDocument(cell); - if (concat) { - const uri = this.toURI(cell); - const range = concat.realRangeOf(uri); - return range || cellRange || createRange(createPosition(0, 0), createPosition(0, 0)); - } - return cellRange || createRange(createPosition(0, 0), createPosition(0, 0)); + + const uri = this.toURI(cell); + const range = concat.realRangeOf(uri); + return range || cellRange || createRange(createPosition(0, 0), createPosition(0, 0)); } public toConcatContext( @@ -297,7 +286,7 @@ export class NotebookConverter implements IDisposable { return (location).map(this.toNotebookLocationOrLink.bind(this)); } if (location?.range) { - return this.toNotebookLocationFromRange(location.uri, location.range); + return this.toNotebookRange(location.uri, location.range); } return location; } @@ -309,10 +298,8 @@ export class NotebookConverter implements IDisposable { if (!highlight) { return undefined; } + const concat = this.getConcatDocument(cell); - if (!concat) { - return undefined; - } const result: protocol.DocumentHighlight[] = []; for (let h of highlight) { const loc = concat.notebookLocationAt(h.range); @@ -340,12 +327,12 @@ export class NotebookConverter implements IDisposable { } public toNotebookSymbolFromSymbolInformation( - cellUri: protocol.TextDocumentIdentifier | string, + cellOrConcatUri: protocol.TextDocumentIdentifier | string, symbol: protocol.SymbolInformation ): protocol.SymbolInformation { return { ...symbol, - location: this.toNotebookLocationFromRange(cellUri, symbol.location.range) + location: this.toNotebookLocationFromRange(cellOrConcatUri, symbol.location.range) }; } @@ -424,10 +411,10 @@ export class NotebookConverter implements IDisposable { if ('range' in rangeOrRename) { return { ...rangeOrRename, - range: this.toNotebookLocationFromRange(cell, rangeOrRename.range).range + range: this.toNotebookRange(cell, rangeOrRename.range) }; } - return this.toNotebookLocationFromRange(cell, rangeOrRename).range; + return this.toNotebookRange(cell, rangeOrRename); } return rangeOrRename ?? undefined; } @@ -450,9 +437,19 @@ export class NotebookConverter implements IDisposable { return links ?? undefined; } - public toNotebookRange(cell: protocol.TextDocumentIdentifier | string, range: protocol.Range): protocol.Range { + public toNotebookRange( + cellOrConcatUri: protocol.TextDocumentIdentifier | string, + range: protocol.Range + ): protocol.Range { // This is dangerous as the URI is not remapped (location uri may be different) - return this.toNotebookLocationFromRange(cell, range).range; + const concat = this.getConcatDocumentForUri(cellOrConcatUri); + if (concat) { + const startLoc = concat.notebookLocationAt(range.start); + const endLoc = concat.notebookLocationAt(range.end); + return createRange(startLoc.range.start, endLoc.range.end); + } + + return range; } public toNotebookPosition( @@ -460,20 +457,17 @@ export class NotebookConverter implements IDisposable { position: protocol.Position ): protocol.Position { // This is dangerous as the URI is not remapped (location uri may be different) - return this.toNotebookLocationFromRange(cell, createRange(position, position)).range.start; + return this.toNotebookRange(cell, createRange(position, position)).start; } public toNotebookOffset(cell: protocol.TextDocumentIdentifier | string, offset: number): number { const uri = this.toURI(cell); - const concat = this.getConcatFromOutgoingUri(cell); - if (concat) { - return concat.notebookOffsetAt(uri, offset); - } - return offset; + const concat = this.getConcatDocument(cell); + return concat.notebookOffsetAt(uri, offset); } - public toNotebookUri(outgoingUri: string, range?: protocol.Range) { - const concat = this.getConcatFromOutgoingUri(outgoingUri); + public toNotebookUri(uri: string, range?: protocol.Range) { + const concat = this.getConcatDocumentForUri(uri); let result: string | undefined; if (concat) { if (range) { @@ -483,7 +477,8 @@ export class NotebookConverter implements IDisposable { result = concat.notebookUri.toString(); } } - return result || outgoingUri; + + return result || uri; } public toNotebookColorInformations( @@ -702,15 +697,19 @@ export class NotebookConverter implements IDisposable { public remove(cell: protocol.TextDocumentIdentifier) { const uri = this.toURI(cell); - const key = NotebookConverter.getDocumentKey(uri); + const key = this.getDocumentKey(uri); const concat = this.activeConcats.get(key); if (concat) { this.deleteConcatDocument(concat); } } - private toURI(cell: protocol.TextDocumentIdentifier | string): vscodeUri.URI { - return typeof cell === 'string' ? vscodeUri.URI.parse(cell) : vscodeUri.URI.parse(cell.uri); + private toURI(input: protocol.TextDocumentIdentifier | string | vscodeUri.URI): vscodeUri.URI { + if (vscodeUri.URI.isUri(input)) { + return input; + } + + return typeof input === 'string' ? vscodeUri.URI.parse(input) : vscodeUri.URI.parse(input.uri); } private toNotebookWorkspaceSymbol(symbol: protocol.SymbolInformation): protocol.SymbolInformation { @@ -834,33 +833,24 @@ export class NotebookConverter implements IDisposable { } private toNotebookLocationFromLocation(location: protocol.Location): protocol.Location { - if (this.locationNeedsConversion(location.uri)) { - const uri = this.toNotebookUri(location.uri, location.range); - - return { - uri, - range: this.toNotebookRange(uri, location.range) - }; - } - - return location; + const uri = this.toNotebookUri(location.uri, location.range); + return { + uri, + range: this.toNotebookRange(uri, location.range) + }; } private toNotebookLocationLinkFromLocationLink(locationLink: protocol.LocationLink): protocol.LocationLink { - if (this.locationNeedsConversion(locationLink.targetUri)) { - const uri = this.toNotebookUri(locationLink.targetUri, locationLink.targetRange); + const uri = this.toNotebookUri(locationLink.targetUri, locationLink.targetRange); - return { - originSelectionRange: locationLink.originSelectionRange - ? this.toNotebookRange(uri, locationLink.originSelectionRange) - : undefined, - targetUri: uri, - targetRange: this.toNotebookRange(uri, locationLink.targetRange), - targetSelectionRange: this.toNotebookRange(uri, locationLink.targetSelectionRange) - }; - } - - return locationLink; + return { + originSelectionRange: locationLink.originSelectionRange + ? this.toNotebookRange(uri, locationLink.originSelectionRange) + : undefined, + targetUri: uri, + targetRange: this.toNotebookRange(uri, locationLink.targetRange), + targetSelectionRange: this.toNotebookRange(uri, locationLink.targetSelectionRange) + }; } private toNotebookLocationOrLink(location: protocol.Location | protocol.LocationLink) { @@ -872,13 +862,6 @@ export class NotebookConverter implements IDisposable { return this.toNotebookLocationFromLocation(location); } - // Returns true if the given location needs conversion - // Should be if it's in a notebook cell or if it's in a notebook concat document - private locationNeedsConversion(locationUri: string): boolean { - const uri = this.toURI(locationUri); - return uri.scheme === NotebookCellScheme || this.getConcatFromOutgoingUri(locationUri) !== undefined; - } - private toNotebookCompletion( cell: protocol.TextDocumentIdentifier, item: protocol.CompletionItem @@ -905,11 +888,10 @@ export class NotebookConverter implements IDisposable { } private toNotebookLocationFromRange( - cell: protocol.TextDocumentIdentifier | string, + cellOrConcatUri: protocol.TextDocumentIdentifier | string, range: protocol.Range ): protocol.Location { - const uri = this.toURI(cell); - const concat = this.getConcatDocument(cell); + const concat = this.getConcatDocumentForUri(cellOrConcatUri); if (concat) { const startLoc = concat.notebookLocationAt(range.start); const endLoc = concat.notebookLocationAt(range.end); @@ -918,30 +900,39 @@ export class NotebookConverter implements IDisposable { range: createRange(startLoc.range.start, endLoc.range.end) }; } + return { - uri: uri.toString(), + uri: protocol.TextDocumentIdentifier.is(cellOrConcatUri) ? cellOrConcatUri.uri : cellOrConcatUri, range }; } private deleteConcatDocument(concat: NotebookConcatDocument) { // Cleanup both maps and dispose of the concat (disconnects the cell change emitter) - this.activeConcatsOutgoingMap.delete(NotebookConverter.getDocumentKey(concat.concatUri)); + this.activeConcatsOutgoingMap.delete(this.getDocumentKey(concat.concatUri)); this.activeConcats.delete(concat.key); concat.dispose(); } - private getConcatFromOutgoingUri( - outgoingUri: protocol.TextDocumentIdentifier | string + private getConcatDocumentForUri(input: protocol.TextDocumentIdentifier | vscodeUri.URI | string) { + const uri = this.toURI(input); + + return isNotebookCell(uri) ? this.getConcatDocument(uri) : this.getConcatFromOutgoingUri(uri); + } + + public getConcatFromOutgoingUri( + concatDocIdOrUri: protocol.TextDocumentIdentifier | string | vscodeUri.URI ): NotebookConcatDocument | undefined { - const uri = this.toURI(outgoingUri); - return this.activeConcatsOutgoingMap.get(NotebookConverter.getDocumentKey(uri)); + const uri = this.toURI(concatDocIdOrUri); + return this.activeConcatsOutgoingMap.get(this.getDocumentKey(uri)); } // Public for testing - public getConcatDocument(cell: protocol.TextDocumentIdentifier | string): NotebookConcatDocument { - const uri = this.toURI(cell); - const key = NotebookConverter.getDocumentKey(uri); + public getConcatDocument( + cellIdOrUri: protocol.TextDocumentIdentifier | string | vscodeUri.URI + ): NotebookConcatDocument { + const uri = this.toURI(cellIdOrUri); + const key = this.getDocumentKey(uri); let result = this.activeConcats.get(key); if (!result) { result = new NotebookConcatDocument(key, this.getNotebookHeader); diff --git a/webpack.config.js b/webpack.config.js index 12a9322..f0efc07 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -11,7 +11,7 @@ const path = require('path'); /**@type {import('webpack').Configuration}*/ const config = { - target: 'node', // vscode extensions run in a Node.js-context 📖 -> https://webpack.js.org/configuration/node/ + target: 'web', entry: './src/index.ts', // the entry point of this extension, 📖 -> https://webpack.js.org/configuration/entry-context/ output: { // the bundle is stored in the 'dist' folder (check package.json), 📖 -> https://webpack.js.org/configuration/output/ @@ -21,9 +21,6 @@ const config = { devtoolModuleFilenameTemplate: "../[resource-path]", }, devtool: 'source-map', - externals: { - vscode: "commonjs vscode" // the vscode-module is created on-the-fly and must be excluded. Add other modules that cannot be webpack'ed, 📖 -> https://webpack.js.org/configuration/externals/ - }, resolve: { // support reading TypeScript and JavaScript files, 📖 -> https://github.com/TypeStrong/ts-loader extensions: ['.ts', '.js'] },