From a60beb9d7a8ade758f62dd12fa3e4461955554eb Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 1 Feb 2021 11:24:30 +0100 Subject: [PATCH] don't leak proxies in editor land, also remove indentSize property which isn't API --- .../workbench/api/browser/mainThreadEditor.ts | 9 - .../workbench/api/common/extHost.api.impl.ts | 2 +- .../workbench/api/common/extHost.protocol.ts | 2 - .../workbench/api/common/extHostCodeInsets.ts | 6 +- .../api/common/extHostDocumentsAndEditors.ts | 23 +- .../workbench/api/common/extHostTextEditor.ts | 432 ++++++++---------- .../api/common/extHostTextEditors.ts | 23 +- .../browser/api/extHostTextEditor.test.ts | 207 ++------- 8 files changed, 258 insertions(+), 446 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadEditor.ts b/src/vs/workbench/api/browser/mainThreadEditor.ts index f805b7f9cbe..37d4d9326d4 100644 --- a/src/vs/workbench/api/browser/mainThreadEditor.ts +++ b/src/vs/workbench/api/browser/mainThreadEditor.ts @@ -79,7 +79,6 @@ export class MainThreadTextEditorProperties { return { insertSpaces: modelOptions.insertSpaces, tabSize: modelOptions.tabSize, - indentSize: modelOptions.indentSize, cursorStyle: cursorStyle, lineNumbers: lineNumbers }; @@ -146,7 +145,6 @@ export class MainThreadTextEditorProperties { } return ( a.tabSize === b.tabSize - && a.indentSize === b.indentSize && a.insertSpaces === b.insertSpaces && a.cursorStyle === b.cursorStyle && a.lineNumbers === b.lineNumbers @@ -377,13 +375,6 @@ export class MainThreadTextEditor { if (typeof newConfiguration.tabSize !== 'undefined') { newOpts.tabSize = newConfiguration.tabSize; } - if (typeof newConfiguration.indentSize !== 'undefined') { - if (newConfiguration.indentSize === 'tabSize') { - newOpts.indentSize = newOpts.tabSize || creationOpts.tabSize; - } else { - newOpts.indentSize = newConfiguration.indentSize; - } - } this._model.updateOptions(newOpts); } diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index a32d5b80013..a21a1a708b0 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -262,7 +262,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I registerDiffInformationCommand: (id: string, callback: (diff: vscode.LineChange[], ...args: any[]) => any, thisArg?: any): vscode.Disposable => { checkProposedApiEnabled(extension); return extHostCommands.registerCommand(true, id, async (...args: any[]): Promise => { - const activeTextEditor = extHostEditors.getActiveTextEditor(); + const activeTextEditor = extHostDocumentsAndEditors.activeEditor(true); if (!activeTextEditor) { extHostLogService.warn('Cannot execute ' + id + ' because there is no active text editor.'); return undefined; diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index be7a14f8859..1be08c2c007 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -237,7 +237,6 @@ export interface MainThreadDocumentsShape extends IDisposable { export interface ITextEditorConfigurationUpdate { tabSize?: number | 'auto'; - indentSize?: number | 'tabSize'; insertSpaces?: boolean | 'auto'; cursorStyle?: TextEditorCursorStyle; lineNumbers?: RenderLineNumbersType; @@ -245,7 +244,6 @@ export interface ITextEditorConfigurationUpdate { export interface IResolvedTextEditorConfiguration { tabSize: number; - indentSize: number; insertSpaces: boolean; cursorStyle: TextEditorCursorStyle; lineNumbers: RenderLineNumbersType; diff --git a/src/vs/workbench/api/common/extHostCodeInsets.ts b/src/vs/workbench/api/common/extHostCodeInsets.ts index 138e4950ae2..7db034cd81f 100644 --- a/src/vs/workbench/api/common/extHostCodeInsets.ts +++ b/src/vs/workbench/api/common/extHostCodeInsets.ts @@ -44,8 +44,8 @@ export class ExtHostEditorInsets implements ExtHostEditorInsetsShape { createWebviewEditorInset(editor: vscode.TextEditor, line: number, height: number, options: vscode.WebviewOptions | undefined, extension: IExtensionDescription): vscode.WebviewEditorInset { let apiEditor: ExtHostTextEditor | undefined; - for (const candidate of this._editors.getVisibleTextEditors()) { - if (candidate === editor) { + for (const candidate of this._editors.getVisibleTextEditors(true)) { + if (candidate.value === editor) { apiEditor = candidate; break; } @@ -121,7 +121,7 @@ export class ExtHostEditorInsets implements ExtHostEditorInsetsShape { } }; - this._proxy.$createEditorInset(handle, apiEditor.id, apiEditor.document.uri, line + 1, height, options || {}, extension.identifier, extension.extensionLocation); + this._proxy.$createEditorInset(handle, apiEditor.id, apiEditor.value.document.uri, line + 1, height, options || {}, extension.identifier, extension.extensionLocation); this._insets.set(handle, { editor, inset, onDidReceiveMessage }); return inset; diff --git a/src/vs/workbench/api/common/extHostDocumentsAndEditors.ts b/src/vs/workbench/api/common/extHostDocumentsAndEditors.ts index e378b49a198..bd80952ee53 100644 --- a/src/vs/workbench/api/common/extHostDocumentsAndEditors.ts +++ b/src/vs/workbench/api/common/extHostDocumentsAndEditors.ts @@ -18,6 +18,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { ResourceMap } from 'vs/base/common/map'; import { Schemas } from 'vs/base/common/network'; import { Iterable } from 'vs/base/common/iterator'; +import { Lazy } from 'vs/base/common/lazy'; class Reference { private _count = 0; @@ -49,13 +50,13 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha private readonly _onDidAddDocuments = new Emitter(); private readonly _onDidRemoveDocuments = new Emitter(); - private readonly _onDidChangeVisibleTextEditors = new Emitter(); - private readonly _onDidChangeActiveTextEditor = new Emitter(); + private readonly _onDidChangeVisibleTextEditors = new Emitter(); + private readonly _onDidChangeActiveTextEditor = new Emitter(); readonly onDidAddDocuments: Event = this._onDidAddDocuments.event; readonly onDidRemoveDocuments: Event = this._onDidRemoveDocuments.event; - readonly onDidChangeVisibleTextEditors: Event = this._onDidChangeVisibleTextEditors.event; - readonly onDidChangeActiveTextEditor: Event = this._onDidChangeActiveTextEditor.event; + readonly onDidChangeVisibleTextEditors: Event = this._onDidChangeVisibleTextEditors.event; + readonly onDidChangeActiveTextEditor: Event = this._onDidChangeActiveTextEditor.event; constructor( @IExtHostRpcService private readonly _extHostRpc: IExtHostRpcService, @@ -135,7 +136,7 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha data.id, this._extHostRpc.getProxy(MainContext.MainThreadTextEditors), this._logService, - documentData, + new Lazy(() => documentData.document), data.selections.map(typeConverters.Selection.to), data.options, data.visibleRanges.map(range => typeConverters.Range.to(range)), @@ -162,7 +163,7 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha } if (delta.removedEditors || delta.addedEditors) { - this._onDidChangeVisibleTextEditors.fire(this.allEditors()); + this._onDidChangeVisibleTextEditors.fire(this.allEditors().map(editor => editor.value)); } if (delta.newActiveEditor !== undefined) { this._onDidChangeActiveTextEditor.fire(this.activeEditor()); @@ -181,11 +182,17 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha return this._editors.get(id); } - activeEditor(): ExtHostTextEditor | undefined { + activeEditor(): vscode.TextEditor | undefined; + activeEditor(internal: true): ExtHostTextEditor | undefined; + activeEditor(internal?: true): vscode.TextEditor | ExtHostTextEditor | undefined { if (!this._activeEditorId) { return undefined; + } + const editor = this._editors.get(this._activeEditorId); + if (internal) { + return editor; } else { - return this._editors.get(this._activeEditorId); + return editor?.value; } } diff --git a/src/vs/workbench/api/common/extHostTextEditor.ts b/src/vs/workbench/api/common/extHostTextEditor.ts index 635a5c6c253..6e2a3e4e324 100644 --- a/src/vs/workbench/api/common/extHostTextEditor.ts +++ b/src/vs/workbench/api/common/extHostTextEditor.ts @@ -10,11 +10,11 @@ import { TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions'; import { IRange } from 'vs/editor/common/core/range'; import { ISingleEditOperation } from 'vs/editor/common/model'; import { IResolvedTextEditorConfiguration, ITextEditorConfigurationUpdate, MainThreadTextEditorsShape } from 'vs/workbench/api/common/extHost.protocol'; -import { ExtHostDocumentData } from 'vs/workbench/api/common/extHostDocumentData'; import * as TypeConverters from 'vs/workbench/api/common/extHostTypeConverters'; import { EndOfLine, Position, Range, Selection, SnippetString, TextEditorLineNumbersStyle, TextEditorRevealType } from 'vs/workbench/api/common/extHostTypes'; import type * as vscode from 'vscode'; import { ILogService } from 'vs/platform/log/common/log'; +import { Lazy } from 'vs/base/common/lazy'; export class TextEditorDecorationType implements vscode.TextEditorDecorationType { @@ -134,36 +134,63 @@ export class TextEditorEdit { } } -export class ExtHostTextEditorOptions implements vscode.TextEditorOptions { +export class ExtHostTextEditorOptions { private _proxy: MainThreadTextEditorsShape; private _id: string; private _logService: ILogService; private _tabSize!: number; - private _indentSize!: number; private _insertSpaces!: boolean; private _cursorStyle!: TextEditorCursorStyle; private _lineNumbers!: TextEditorLineNumbersStyle; + readonly value: vscode.TextEditorOptions; + constructor(proxy: MainThreadTextEditorsShape, id: string, source: IResolvedTextEditorConfiguration, logService: ILogService) { this._proxy = proxy; this._id = id; this._accept(source); this._logService = logService; + + const that = this; + + this.value = { + get tabSize(): number | string { + return that._tabSize; + }, + set tabSize(value: number | string) { + that._setTabSize(value); + }, + get insertSpaces(): boolean | string { + return that._insertSpaces; + }, + set insertSpaces(value: boolean | string) { + that._setInsertSpaces(value); + }, + get cursorStyle(): TextEditorCursorStyle { + return that._cursorStyle; + }, + set cursorStyle(value: TextEditorCursorStyle) { + that._setCursorStyle(value); + }, + get lineNumbers(): TextEditorLineNumbersStyle { + return that._lineNumbers; + }, + set lineNumbers(value: TextEditorLineNumbersStyle) { + that._setLineNumbers(value); + } + }; } public _accept(source: IResolvedTextEditorConfiguration): void { this._tabSize = source.tabSize; - this._indentSize = source.indentSize; this._insertSpaces = source.insertSpaces; this._cursorStyle = source.cursorStyle; this._lineNumbers = TypeConverters.TextEditorLineNumbersStyle.to(source.lineNumbers); } - public get tabSize(): number | string { - return this._tabSize; - } + // --- internal: tabSize private _validateTabSize(value: number | string): number | 'auto' | null { if (value === 'auto') { @@ -183,7 +210,7 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions { return null; } - public set tabSize(value: number | string) { + private _setTabSize(value: number | string) { const tabSize = this._validateTabSize(value); if (tabSize === null) { // ignore invalid call @@ -202,50 +229,7 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions { })); } - public get indentSize(): number | string { - return this._indentSize; - } - - private _validateIndentSize(value: number | string): number | 'tabSize' | null { - if (value === 'tabSize') { - return 'tabSize'; - } - if (typeof value === 'number') { - const r = Math.floor(value); - return (r > 0 ? r : null); - } - if (typeof value === 'string') { - const r = parseInt(value, 10); - if (isNaN(r)) { - return null; - } - return (r > 0 ? r : null); - } - return null; - } - - public set indentSize(value: number | string) { - const indentSize = this._validateIndentSize(value); - if (indentSize === null) { - // ignore invalid call - return; - } - if (typeof indentSize === 'number') { - if (this._indentSize === indentSize) { - // nothing to do - return; - } - // reflect the new indentSize value immediately - this._indentSize = indentSize; - } - this._warnOnError(this._proxy.$trySetOptions(this._id, { - indentSize: indentSize - })); - } - - public get insertSpaces(): boolean | string { - return this._insertSpaces; - } + // --- internal: insert spaces private _validateInsertSpaces(value: boolean | string): boolean | 'auto' { if (value === 'auto') { @@ -254,7 +238,7 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions { return (value === 'false' ? false : Boolean(value)); } - public set insertSpaces(value: boolean | string) { + private _setInsertSpaces(value: boolean | string) { const insertSpaces = this._validateInsertSpaces(value); if (typeof insertSpaces === 'boolean') { if (this._insertSpaces === insertSpaces) { @@ -269,11 +253,9 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions { })); } - public get cursorStyle(): TextEditorCursorStyle { - return this._cursorStyle; - } + // --- internal: cursor style - public set cursorStyle(value: TextEditorCursorStyle) { + private _setCursorStyle(value: TextEditorCursorStyle) { if (this._cursorStyle === value) { // nothing to do return; @@ -284,11 +266,9 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions { })); } - public get lineNumbers(): TextEditorLineNumbersStyle { - return this._lineNumbers; - } + // --- internal: line number - public set lineNumbers(value: TextEditorLineNumbersStyle) { + private _setLineNumbers(value: TextEditorLineNumbersStyle) { if (this._lineNumbers === value) { // nothing to do return; @@ -368,31 +348,170 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions { } } -export class ExtHostTextEditor implements vscode.TextEditor { - - private readonly _documentData: ExtHostDocumentData; +export class ExtHostTextEditor { private _selections: Selection[]; private _options: ExtHostTextEditorOptions; private _visibleRanges: Range[]; private _viewColumn: vscode.ViewColumn | undefined; private _disposed: boolean = false; - private _hasDecorationsForKey: { [key: string]: boolean; }; + private _hasDecorationsForKey = new Set(); + + readonly value: vscode.TextEditor; constructor( readonly id: string, private readonly _proxy: MainThreadTextEditorsShape, private readonly _logService: ILogService, - document: ExtHostDocumentData, + document: Lazy, selections: Selection[], options: IResolvedTextEditorConfiguration, visibleRanges: Range[], viewColumn: vscode.ViewColumn | undefined ) { - this._documentData = document; this._selections = selections; this._options = new ExtHostTextEditorOptions(this._proxy, this.id, options, _logService); this._visibleRanges = visibleRanges; this._viewColumn = viewColumn; - this._hasDecorationsForKey = Object.create(null); + + const that = this; + + this.value = Object.freeze({ + get document(): vscode.TextDocument { + return document.getValue(); + }, + set document(_value) { + throw readonly('document'); + }, + // --- selection + get selection(): Selection { + return that._selections && that._selections[0]; + }, + set selection(value: Selection) { + if (!(value instanceof Selection)) { + throw illegalArgument('selection'); + } + that._selections = [value]; + that._trySetSelection(); + }, + get selections(): Selection[] { + return that._selections; + }, + set selections(value: Selection[]) { + if (!Array.isArray(value) || value.some(a => !(a instanceof Selection))) { + throw illegalArgument('selections'); + } + that._selections = value; + that._trySetSelection(); + }, + // --- visible ranges + get visibleRanges(): Range[] { + return that._visibleRanges; + }, + set visibleRanges(_value: Range[]) { + throw readonly('visibleRanges'); + }, + // --- options + get options(): vscode.TextEditorOptions { + return that._options.value; + }, + set options(value: vscode.TextEditorOptions) { + if (!that._disposed) { + that._options.assign(value); + } + }, + // --- view column + get viewColumn(): vscode.ViewColumn | undefined { + return that._viewColumn; + }, + set viewColumn(_value) { + throw readonly('viewColumn'); + }, + // --- edit + edit(callback: (edit: TextEditorEdit) => void, options: { undoStopBefore: boolean; undoStopAfter: boolean; } = { undoStopBefore: true, undoStopAfter: true }): Promise { + if (that._disposed) { + return Promise.reject(new Error('TextEditor#edit not possible on closed editors')); + } + const edit = new TextEditorEdit(document.getValue(), options); + callback(edit); + return that._applyEdit(edit); + }, + // --- snippet edit + insertSnippet(snippet: SnippetString, where?: Position | readonly Position[] | Range | readonly Range[], options: { undoStopBefore: boolean; undoStopAfter: boolean; } = { undoStopBefore: true, undoStopAfter: true }): Promise { + if (that._disposed) { + return Promise.reject(new Error('TextEditor#insertSnippet not possible on closed editors')); + } + let ranges: IRange[]; + + if (!where || (Array.isArray(where) && where.length === 0)) { + ranges = that._selections.map(range => TypeConverters.Range.from(range)); + + } else if (where instanceof Position) { + const { lineNumber, column } = TypeConverters.Position.from(where); + ranges = [{ startLineNumber: lineNumber, startColumn: column, endLineNumber: lineNumber, endColumn: column }]; + + } else if (where instanceof Range) { + ranges = [TypeConverters.Range.from(where)]; + } else { + ranges = []; + for (const posOrRange of where) { + if (posOrRange instanceof Range) { + ranges.push(TypeConverters.Range.from(posOrRange)); + } else { + const { lineNumber, column } = TypeConverters.Position.from(posOrRange); + ranges.push({ startLineNumber: lineNumber, startColumn: column, endLineNumber: lineNumber, endColumn: column }); + } + } + } + return _proxy.$tryInsertSnippet(id, snippet.value, ranges, options); + }, + setDecorations(decorationType: vscode.TextEditorDecorationType, ranges: Range[] | vscode.DecorationOptions[]): void { + const willBeEmpty = (ranges.length === 0); + if (willBeEmpty && !that._hasDecorationsForKey.has(decorationType.key)) { + // avoid no-op call to the renderer + return; + } + if (willBeEmpty) { + that._hasDecorationsForKey.delete(decorationType.key); + } else { + that._hasDecorationsForKey.add(decorationType.key); + } + that._runOnProxy(() => { + if (TypeConverters.isDecorationOptionsArr(ranges)) { + return _proxy.$trySetDecorations( + id, + decorationType.key, + TypeConverters.fromRangeOrRangeWithMessage(ranges) + ); + } else { + const _ranges: number[] = new Array(4 * ranges.length); + for (let i = 0, len = ranges.length; i < len; i++) { + const range = ranges[i]; + _ranges[4 * i] = range.start.line + 1; + _ranges[4 * i + 1] = range.start.character + 1; + _ranges[4 * i + 2] = range.end.line + 1; + _ranges[4 * i + 3] = range.end.character + 1; + } + return _proxy.$trySetDecorationsFast( + id, + decorationType.key, + _ranges + ); + } + }); + }, + revealRange(range: Range, revealType: vscode.TextEditorRevealType): void { + that._runOnProxy(() => _proxy.$tryRevealRange( + id, + TypeConverters.Range.from(range), + (revealType || TextEditorRevealType.Default) + )); + }, + show(column: vscode.ViewColumn) { + _proxy.$tryShowEditor(id, TypeConverters.ViewColumn.from(column)); + }, + hide() { + _proxy.$tryHideEditor(id); + } + }); } dispose() { @@ -400,164 +519,32 @@ export class ExtHostTextEditor implements vscode.TextEditor { this._disposed = true; } - show(column: vscode.ViewColumn) { - this._proxy.$tryShowEditor(this.id, TypeConverters.ViewColumn.from(column)); - } - - hide() { - this._proxy.$tryHideEditor(this.id); - } - - // ---- the document - - get document(): vscode.TextDocument { - return this._documentData.document; - } - - set document(value) { - throw readonly('document'); - } - - // ---- options - - get options(): vscode.TextEditorOptions { - return this._options; - } - - set options(value: vscode.TextEditorOptions) { - if (!this._disposed) { - this._options.assign(value); - } - } + // --- incoming: extension host MUST accept what the renderer says _acceptOptions(options: IResolvedTextEditorConfiguration): void { ok(!this._disposed); this._options._accept(options); } - // ---- visible ranges - - get visibleRanges(): Range[] { - return this._visibleRanges; - } - - set visibleRanges(value: Range[]) { - throw readonly('visibleRanges'); - } - _acceptVisibleRanges(value: Range[]): void { ok(!this._disposed); this._visibleRanges = value; } - // ---- view column - - get viewColumn(): vscode.ViewColumn | undefined { - return this._viewColumn; - } - - set viewColumn(value) { - throw readonly('viewColumn'); - } - _acceptViewColumn(value: vscode.ViewColumn) { ok(!this._disposed); this._viewColumn = value; } - // ---- selections - - get selection(): Selection { - return this._selections && this._selections[0]; - } - - set selection(value: Selection) { - if (!(value instanceof Selection)) { - throw illegalArgument('selection'); - } - this._selections = [value]; - this._trySetSelection(); - } - - get selections(): Selection[] { - return this._selections; - } - - set selections(value: Selection[]) { - if (!Array.isArray(value) || value.some(a => !(a instanceof Selection))) { - throw illegalArgument('selections'); - } - this._selections = value; - this._trySetSelection(); - } - - setDecorations(decorationType: vscode.TextEditorDecorationType, ranges: Range[] | vscode.DecorationOptions[]): void { - const willBeEmpty = (ranges.length === 0); - if (willBeEmpty && !this._hasDecorationsForKey[decorationType.key]) { - // avoid no-op call to the renderer - return; - } - if (willBeEmpty) { - delete this._hasDecorationsForKey[decorationType.key]; - } else { - this._hasDecorationsForKey[decorationType.key] = true; - } - this._runOnProxy( - () => { - if (TypeConverters.isDecorationOptionsArr(ranges)) { - return this._proxy.$trySetDecorations( - this.id, - decorationType.key, - TypeConverters.fromRangeOrRangeWithMessage(ranges) - ); - } else { - const _ranges: number[] = new Array(4 * ranges.length); - for (let i = 0, len = ranges.length; i < len; i++) { - const range = ranges[i]; - _ranges[4 * i] = range.start.line + 1; - _ranges[4 * i + 1] = range.start.character + 1; - _ranges[4 * i + 2] = range.end.line + 1; - _ranges[4 * i + 3] = range.end.character + 1; - } - return this._proxy.$trySetDecorationsFast( - this.id, - decorationType.key, - _ranges - ); - } - } - ); - } - - revealRange(range: Range, revealType: vscode.TextEditorRevealType): void { - this._runOnProxy( - () => this._proxy.$tryRevealRange( - this.id, - TypeConverters.Range.from(range), - (revealType || TextEditorRevealType.Default) - ) - ); - } - - private _trySetSelection(): Promise { - const selection = this._selections.map(TypeConverters.Selection.from); - return this._runOnProxy(() => this._proxy.$trySetSelections(this.id, selection)); - } - _acceptSelections(selections: Selection[]): void { ok(!this._disposed); this._selections = selections; } - // ---- editing - - edit(callback: (edit: TextEditorEdit) => void, options: { undoStopBefore: boolean; undoStopAfter: boolean; } = { undoStopBefore: true, undoStopAfter: true }): Promise { - if (this._disposed) { - return Promise.reject(new Error('TextEditor#edit not possible on closed editors')); - } - const edit = new TextEditorEdit(this._documentData.document, options); - callback(edit); - return this._applyEdit(edit); + private async _trySetSelection(): Promise { + const selection = this._selections.map(TypeConverters.Selection.from); + await this._runOnProxy(() => this._proxy.$trySetSelections(this.id, selection)); + return this.value; } private _applyEdit(editBuilder: TextEditorEdit): Promise { @@ -613,44 +600,12 @@ export class ExtHostTextEditor implements vscode.TextEditor { undoStopAfter: editData.undoStopAfter }); } - - insertSnippet(snippet: SnippetString, where?: Position | readonly Position[] | Range | readonly Range[], options: { undoStopBefore: boolean; undoStopAfter: boolean; } = { undoStopBefore: true, undoStopAfter: true }): Promise { - if (this._disposed) { - return Promise.reject(new Error('TextEditor#insertSnippet not possible on closed editors')); - } - let ranges: IRange[]; - - if (!where || (Array.isArray(where) && where.length === 0)) { - ranges = this._selections.map(range => TypeConverters.Range.from(range)); - - } else if (where instanceof Position) { - const { lineNumber, column } = TypeConverters.Position.from(where); - ranges = [{ startLineNumber: lineNumber, startColumn: column, endLineNumber: lineNumber, endColumn: column }]; - - } else if (where instanceof Range) { - ranges = [TypeConverters.Range.from(where)]; - } else { - ranges = []; - for (const posOrRange of where) { - if (posOrRange instanceof Range) { - ranges.push(TypeConverters.Range.from(posOrRange)); - } else { - const { lineNumber, column } = TypeConverters.Position.from(posOrRange); - ranges.push({ startLineNumber: lineNumber, startColumn: column, endLineNumber: lineNumber, endColumn: column }); - } - } - } - - return this._proxy.$tryInsertSnippet(this.id, snippet.value, ranges, options); - } - - // ---- util - private _runOnProxy(callback: () => Promise): Promise { if (this._disposed) { this._logService.warn('TextEditor is closed/disposed'); return Promise.resolve(undefined); } + return callback().then(() => this, err => { if (!(err instanceof Error && err.name === 'DISPOSED')) { this._logService.warn(err); @@ -659,4 +614,3 @@ export class ExtHostTextEditor implements vscode.TextEditor { }); } } - diff --git a/src/vs/workbench/api/common/extHostTextEditors.ts b/src/vs/workbench/api/common/extHostTextEditors.ts index 3e95b2f382e..b01d113fe2b 100644 --- a/src/vs/workbench/api/common/extHostTextEditors.ts +++ b/src/vs/workbench/api/common/extHostTextEditors.ts @@ -41,12 +41,17 @@ export class ExtHostEditors implements ExtHostEditorsShape { this._extHostDocumentsAndEditors.onDidChangeActiveTextEditor(e => this._onDidChangeActiveTextEditor.fire(e)); } - getActiveTextEditor(): ExtHostTextEditor | undefined { + getActiveTextEditor(): vscode.TextEditor | undefined { return this._extHostDocumentsAndEditors.activeEditor(); } - getVisibleTextEditors(): vscode.TextEditor[] { - return this._extHostDocumentsAndEditors.allEditors(); + getVisibleTextEditors(): vscode.TextEditor[]; + getVisibleTextEditors(internal: true): ExtHostTextEditor[]; + getVisibleTextEditors(internal?: true): ExtHostTextEditor[] | vscode.TextEditor[] { + const editors = this._extHostDocumentsAndEditors.allEditors(); + return internal + ? editors + : editors.map(editor => editor.value); } showTextDocument(document: vscode.TextDocument, column: vscode.ViewColumn, preserveFocus: boolean): Promise; @@ -75,7 +80,7 @@ export class ExtHostEditors implements ExtHostEditorsShape { const editorId = await this._proxy.$tryShowTextDocument(document.uri, options); const editor = editorId && this._extHostDocumentsAndEditors.getEditor(editorId); if (editor) { - return editor; + return editor.value; } // we have no editor... having an id means that we had an editor // on the main side and that it isn't the current editor anymore... @@ -114,7 +119,7 @@ export class ExtHostEditors implements ExtHostEditorsShape { // (2) fire change events if (data.options) { this._onDidChangeTextEditorOptions.fire({ - textEditor: textEditor, + textEditor: textEditor.value, options: { ...data.options, lineNumbers: TypeConverters.TextEditorLineNumbersStyle.to(data.options.lineNumbers) } }); } @@ -122,7 +127,7 @@ export class ExtHostEditors implements ExtHostEditorsShape { const kind = TextEditorSelectionChangeKind.fromValue(data.selections.source); const selections = data.selections.selections.map(TypeConverters.Selection.to); this._onDidChangeTextEditorSelection.fire({ - textEditor, + textEditor: textEditor.value, selections, kind }); @@ -130,7 +135,7 @@ export class ExtHostEditors implements ExtHostEditorsShape { if (data.visibleRanges) { const visibleRanges = arrays.coalesce(data.visibleRanges.map(TypeConverters.Range.to)); this._onDidChangeTextEditorVisibleRanges.fire({ - textEditor, + textEditor: textEditor.value, visibleRanges }); } @@ -143,9 +148,9 @@ export class ExtHostEditors implements ExtHostEditorsShape { throw new Error('Unknown text editor'); } const viewColumn = TypeConverters.ViewColumn.to(data[id]); - if (textEditor.viewColumn !== viewColumn) { + if (textEditor.value.viewColumn !== viewColumn) { textEditor._acceptViewColumn(viewColumn); - this._onDidChangeTextEditorViewColumn.fire({ textEditor, viewColumn }); + this._onDidChangeTextEditorViewColumn.fire({ textEditor: textEditor.value, viewColumn }); } } } diff --git a/src/vs/workbench/test/browser/api/extHostTextEditor.test.ts b/src/vs/workbench/test/browser/api/extHostTextEditor.test.ts index 6e59c522f2a..ca9a7f7999d 100644 --- a/src/vs/workbench/test/browser/api/extHostTextEditor.test.ts +++ b/src/vs/workbench/test/browser/api/extHostTextEditor.test.ts @@ -11,6 +11,7 @@ import { ExtHostDocumentData } from 'vs/workbench/api/common/extHostDocumentData import { URI } from 'vs/base/common/uri'; import { mock } from 'vs/base/test/common/mock'; import { NullLogService } from 'vs/platform/log/common/log'; +import { Lazy } from 'vs/base/common/lazy'; suite('ExtHostTextEditor', () => { @@ -20,21 +21,21 @@ suite('ExtHostTextEditor', () => { ], '\n', 1, 'text', false); setup(() => { - editor = new ExtHostTextEditor('fake', null!, new NullLogService(), doc, [], { cursorStyle: 0, insertSpaces: true, lineNumbers: 1, tabSize: 4, indentSize: 4 }, [], 1); + editor = new ExtHostTextEditor('fake', null!, new NullLogService(), new Lazy(() => doc.document), [], { cursorStyle: 0, insertSpaces: true, lineNumbers: 1, tabSize: 4 }, [], 1); }); test('disposed editor', () => { - assert.ok(editor.document); + assert.ok(editor.value.document); editor._acceptViewColumn(3); - assert.strictEqual(3, editor.viewColumn); + assert.strictEqual(3, editor.value.viewColumn); editor.dispose(); assert.throws(() => editor._acceptViewColumn(2)); - assert.strictEqual(3, editor.viewColumn); + assert.strictEqual(3, editor.value.viewColumn); - assert.ok(editor.document); + assert.ok(editor.value.document); assert.throws(() => editor._acceptOptions(null!)); assert.throws(() => editor._acceptSelections([])); }); @@ -47,15 +48,15 @@ suite('ExtHostTextEditor', () => { applyCount += 1; return Promise.resolve(true); } - }, new NullLogService(), doc, [], { cursorStyle: 0, insertSpaces: true, lineNumbers: 1, tabSize: 4, indentSize: 4 }, [], 1); + }, new NullLogService(), new Lazy(() => doc.document), [], { cursorStyle: 0, insertSpaces: true, lineNumbers: 1, tabSize: 4 }, [], 1); - await editor.edit(edit => { }); + await editor.value.edit(edit => { }); assert.strictEqual(applyCount, 0); - await editor.edit(edit => { edit.setEndOfLine(1); }); + await editor.value.edit(edit => { edit.setEndOfLine(1); }); assert.strictEqual(applyCount, 1); - await editor.edit(edit => { edit.delete(new Range(0, 0, 1, 1)); }); + await editor.value.edit(edit => { edit.delete(new Range(0, 0, 1, 1)); }); assert.strictEqual(applyCount, 2); }); }); @@ -89,7 +90,6 @@ suite('ExtHostTextEditorOptions', () => { }; opts = new ExtHostTextEditorOptions(mockProxy, '1', { tabSize: 4, - indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -103,20 +103,18 @@ suite('ExtHostTextEditorOptions', () => { function assertState(opts: ExtHostTextEditorOptions, expected: IResolvedTextEditorConfiguration): void { let actual = { - tabSize: opts.tabSize, - indentSize: opts.indentSize, - insertSpaces: opts.insertSpaces, - cursorStyle: opts.cursorStyle, - lineNumbers: opts.lineNumbers + tabSize: opts.value.tabSize, + insertSpaces: opts.value.insertSpaces, + cursorStyle: opts.value.cursorStyle, + lineNumbers: opts.value.lineNumbers }; assert.deepStrictEqual(actual, expected); } test('can set tabSize to the same value', () => { - opts.tabSize = 4; + opts.value.tabSize = 4; assertState(opts, { tabSize: 4, - indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -125,10 +123,9 @@ suite('ExtHostTextEditorOptions', () => { }); test('can change tabSize to positive integer', () => { - opts.tabSize = 1; + opts.value.tabSize = 1; assertState(opts, { tabSize: 1, - indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -137,10 +134,9 @@ suite('ExtHostTextEditorOptions', () => { }); test('can change tabSize to positive float', () => { - opts.tabSize = 2.3; + opts.value.tabSize = 2.3; assertState(opts, { tabSize: 2, - indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -149,10 +145,9 @@ suite('ExtHostTextEditorOptions', () => { }); test('can change tabSize to a string number', () => { - opts.tabSize = '2'; + opts.value.tabSize = '2'; assertState(opts, { tabSize: 2, - indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -161,10 +156,9 @@ suite('ExtHostTextEditorOptions', () => { }); test('tabSize can request indentation detection', () => { - opts.tabSize = 'auto'; + opts.value.tabSize = 'auto'; assertState(opts, { tabSize: 4, - indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -173,10 +167,9 @@ suite('ExtHostTextEditorOptions', () => { }); test('ignores invalid tabSize 1', () => { - opts.tabSize = null!; + opts.value.tabSize = null!; assertState(opts, { tabSize: 4, - indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -185,10 +178,9 @@ suite('ExtHostTextEditorOptions', () => { }); test('ignores invalid tabSize 2', () => { - opts.tabSize = -5; + opts.value.tabSize = -5; assertState(opts, { tabSize: 4, - indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -197,10 +189,9 @@ suite('ExtHostTextEditorOptions', () => { }); test('ignores invalid tabSize 3', () => { - opts.tabSize = 'hello'; + opts.value.tabSize = 'hello'; assertState(opts, { tabSize: 4, - indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -209,130 +200,9 @@ suite('ExtHostTextEditorOptions', () => { }); test('ignores invalid tabSize 4', () => { - opts.tabSize = '-17'; + opts.value.tabSize = '-17'; assertState(opts, { tabSize: 4, - indentSize: 4, - insertSpaces: false, - cursorStyle: TextEditorCursorStyle.Line, - lineNumbers: RenderLineNumbersType.On - }); - assert.deepStrictEqual(calls, []); - }); - - test('can set indentSize to the same value', () => { - opts.indentSize = 4; - assertState(opts, { - tabSize: 4, - indentSize: 4, - insertSpaces: false, - cursorStyle: TextEditorCursorStyle.Line, - lineNumbers: RenderLineNumbersType.On - }); - assert.deepStrictEqual(calls, []); - }); - - test('can change indentSize to positive integer', () => { - opts.indentSize = 1; - assertState(opts, { - tabSize: 4, - indentSize: 1, - insertSpaces: false, - cursorStyle: TextEditorCursorStyle.Line, - lineNumbers: RenderLineNumbersType.On - }); - assert.deepStrictEqual(calls, [{ indentSize: 1 }]); - }); - - test('can change indentSize to positive float', () => { - opts.indentSize = 2.3; - assertState(opts, { - tabSize: 4, - indentSize: 2, - insertSpaces: false, - cursorStyle: TextEditorCursorStyle.Line, - lineNumbers: RenderLineNumbersType.On - }); - assert.deepStrictEqual(calls, [{ indentSize: 2 }]); - }); - - test('can change indentSize to a string number', () => { - opts.indentSize = '2'; - assertState(opts, { - tabSize: 4, - indentSize: 2, - insertSpaces: false, - cursorStyle: TextEditorCursorStyle.Line, - lineNumbers: RenderLineNumbersType.On - }); - assert.deepStrictEqual(calls, [{ indentSize: 2 }]); - }); - - test('indentSize can request to use tabSize', () => { - opts.indentSize = 'tabSize'; - assertState(opts, { - tabSize: 4, - indentSize: 4, - insertSpaces: false, - cursorStyle: TextEditorCursorStyle.Line, - lineNumbers: RenderLineNumbersType.On - }); - assert.deepStrictEqual(calls, [{ indentSize: 'tabSize' }]); - }); - - test('indentSize cannot request indentation detection', () => { - opts.indentSize = 'auto'; - assertState(opts, { - tabSize: 4, - indentSize: 4, - insertSpaces: false, - cursorStyle: TextEditorCursorStyle.Line, - lineNumbers: RenderLineNumbersType.On - }); - assert.deepStrictEqual(calls, []); - }); - - test('ignores invalid indentSize 1', () => { - opts.indentSize = null!; - assertState(opts, { - tabSize: 4, - indentSize: 4, - insertSpaces: false, - cursorStyle: TextEditorCursorStyle.Line, - lineNumbers: RenderLineNumbersType.On - }); - assert.deepStrictEqual(calls, []); - }); - - test('ignores invalid indentSize 2', () => { - opts.indentSize = -5; - assertState(opts, { - tabSize: 4, - indentSize: 4, - insertSpaces: false, - cursorStyle: TextEditorCursorStyle.Line, - lineNumbers: RenderLineNumbersType.On - }); - assert.deepStrictEqual(calls, []); - }); - - test('ignores invalid indentSize 3', () => { - opts.indentSize = 'hello'; - assertState(opts, { - tabSize: 4, - indentSize: 4, - insertSpaces: false, - cursorStyle: TextEditorCursorStyle.Line, - lineNumbers: RenderLineNumbersType.On - }); - assert.deepStrictEqual(calls, []); - }); - - test('ignores invalid indentSize 4', () => { - opts.indentSize = '-17'; - assertState(opts, { - tabSize: 4, - indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -341,10 +211,9 @@ suite('ExtHostTextEditorOptions', () => { }); test('can set insertSpaces to the same value', () => { - opts.insertSpaces = false; + opts.value.insertSpaces = false; assertState(opts, { tabSize: 4, - indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -353,10 +222,9 @@ suite('ExtHostTextEditorOptions', () => { }); test('can set insertSpaces to boolean', () => { - opts.insertSpaces = true; + opts.value.insertSpaces = true; assertState(opts, { tabSize: 4, - indentSize: 4, insertSpaces: true, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -365,10 +233,9 @@ suite('ExtHostTextEditorOptions', () => { }); test('can set insertSpaces to false string', () => { - opts.insertSpaces = 'false'; + opts.value.insertSpaces = 'false'; assertState(opts, { tabSize: 4, - indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -377,10 +244,9 @@ suite('ExtHostTextEditorOptions', () => { }); test('can set insertSpaces to truey', () => { - opts.insertSpaces = 'hello'; + opts.value.insertSpaces = 'hello'; assertState(opts, { tabSize: 4, - indentSize: 4, insertSpaces: true, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -389,10 +255,9 @@ suite('ExtHostTextEditorOptions', () => { }); test('insertSpaces can request indentation detection', () => { - opts.insertSpaces = 'auto'; + opts.value.insertSpaces = 'auto'; assertState(opts, { tabSize: 4, - indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -401,10 +266,9 @@ suite('ExtHostTextEditorOptions', () => { }); test('can set cursorStyle to same value', () => { - opts.cursorStyle = TextEditorCursorStyle.Line; + opts.value.cursorStyle = TextEditorCursorStyle.Line; assertState(opts, { tabSize: 4, - indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -413,10 +277,9 @@ suite('ExtHostTextEditorOptions', () => { }); test('can change cursorStyle', () => { - opts.cursorStyle = TextEditorCursorStyle.Block; + opts.value.cursorStyle = TextEditorCursorStyle.Block; assertState(opts, { tabSize: 4, - indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Block, lineNumbers: RenderLineNumbersType.On @@ -425,10 +288,9 @@ suite('ExtHostTextEditorOptions', () => { }); test('can set lineNumbers to same value', () => { - opts.lineNumbers = TextEditorLineNumbersStyle.On; + opts.value.lineNumbers = TextEditorLineNumbersStyle.On; assertState(opts, { tabSize: 4, - indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -437,10 +299,9 @@ suite('ExtHostTextEditorOptions', () => { }); test('can change lineNumbers', () => { - opts.lineNumbers = TextEditorLineNumbersStyle.Off; + opts.value.lineNumbers = TextEditorLineNumbersStyle.Off; assertState(opts, { tabSize: 4, - indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.Off @@ -457,7 +318,6 @@ suite('ExtHostTextEditorOptions', () => { }); assertState(opts, { tabSize: 4, - indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -472,7 +332,6 @@ suite('ExtHostTextEditorOptions', () => { }); assertState(opts, { tabSize: 4, - indentSize: 4, insertSpaces: true, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -487,7 +346,6 @@ suite('ExtHostTextEditorOptions', () => { }); assertState(opts, { tabSize: 3, - indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -502,7 +360,6 @@ suite('ExtHostTextEditorOptions', () => { }); assertState(opts, { tabSize: 4, - indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Block, lineNumbers: RenderLineNumbersType.Relative