diff --git a/packages/console/src/browser/console-keybinding-contexts.ts b/packages/console/src/browser/console-keybinding-contexts.ts index 431ecee22307b..76acae8d204a8 100644 --- a/packages/console/src/browser/console-keybinding-contexts.ts +++ b/packages/console/src/browser/console-keybinding-contexts.ts @@ -83,7 +83,7 @@ export class ConsoleNavigationBackEnabled extends ConsoleInputFocusContext { return false; } const editor = console.input.getControl(); - return editor.getPosition().equals({ lineNumber: 1, column: 1 }); + return editor.getPosition()!.equals({ lineNumber: 1, column: 1 }); } } @@ -98,10 +98,10 @@ export class ConsoleNavigationForwardEnabled extends ConsoleInputFocusContext { return false; } const editor = console.input.getControl(); - const model = console.input.getControl().getModel(); + const model = console.input.getControl().getModel()!; const lineNumber = model.getLineCount(); const column = model.getLineMaxColumn(lineNumber); - return editor.getPosition().equals({ lineNumber, column }); + return editor.getPosition()!.equals({ lineNumber, column }); } } diff --git a/packages/console/src/browser/console-widget.ts b/packages/console/src/browser/console-widget.ts index ca1ed3b33bb83..48b7a9cf27a26 100644 --- a/packages/console/src/browser/console-widget.ts +++ b/packages/console/src/browser/console-widget.ts @@ -193,8 +193,8 @@ export class ConsoleWidget extends BaseWidget implements StatefulWidget { const value = this.history.next || ''; const editor = this.input.getControl(); editor.setValue(value); - const lineNumber = editor.getModel().getLineCount(); - const column = editor.getModel().getLineMaxColumn(lineNumber); + const lineNumber = editor.getModel()!.getLineCount(); + const column = editor.getModel()!.getLineMaxColumn(lineNumber); editor.setPosition({ lineNumber, column }); } diff --git a/packages/debug/src/browser/debug-configuration-manager.ts b/packages/debug/src/browser/debug-configuration-manager.ts index bcbaeca4548e6..ce25e327a44ac 100644 --- a/packages/debug/src/browser/debug-configuration-manager.ts +++ b/packages/debug/src/browser/debug-configuration-manager.ts @@ -208,7 +208,7 @@ export class DebugConfigurationManager { }, onArrayBegin: offset => { if (lastProperty === 'configurations' && depthInArray === 0) { - position = editor.getModel().getPositionAt(offset + 1); + position = editor.getModel()!.getPositionAt(offset + 1); } depthInArray++; }, @@ -220,12 +220,12 @@ export class DebugConfigurationManager { return; } // Check if there are more characters on a line after a "configurations": [, if yes enter a newline - if (editor.getModel().getLineLastNonWhitespaceColumn(position.lineNumber) > position.column) { + if (editor.getModel()!.getLineLastNonWhitespaceColumn(position.lineNumber) > position.column) { editor.setPosition(position); editor.trigger('debug', 'lineBreakInsert', undefined); } // Check if there is already an empty line to insert suggest, if yes just place the cursor - if (editor.getModel().getLineLastNonWhitespaceColumn(position.lineNumber + 1) === 0) { + if (editor.getModel()!.getLineLastNonWhitespaceColumn(position.lineNumber + 1) === 0) { editor.setPosition({ lineNumber: position.lineNumber + 1, column: 1 << 30 }); await commandService.executeCommand('editor.action.deleteLines'); } diff --git a/packages/debug/src/browser/editor/debug-breakpoint-widget.tsx b/packages/debug/src/browser/editor/debug-breakpoint-widget.tsx index b346f0a35c302..57d645d470c2d 100644 --- a/packages/debug/src/browser/editor/debug-breakpoint-widget.tsx +++ b/packages/debug/src/browser/editor/debug-breakpoint-widget.tsx @@ -89,20 +89,31 @@ export class DebugBreakpointWidget implements Disposable { return; } this.toDispose.push(input); - this.toDispose.push(monaco.modes.SuggestRegistry.register({ scheme: input.uri.scheme }, { + this.toDispose.push(monaco.modes.CompletionProviderRegistry.register({ scheme: input.uri.scheme }, { provideCompletionItems: async (model, position, context, token) => { const suggestions = []; if ((this.context === 'condition' || this.context === 'logMessage') && input.uri.toString() === model.uri.toString()) { const editor = this.editor.getControl(); const items = await monaco.suggest.provideSuggestionItems( - editor.getModel(), - new monaco.Position(editor.getPosition().lineNumber, 1), - 'none', undefined, context, token); - for (const { suggestion } of items) { - suggestion.overwriteAfter = 0; - suggestion.overwriteBefore = position.column - 1; - suggestions.push(suggestion); + editor.getModel()!, + new monaco.Position(editor.getPosition()!.lineNumber, 1), + new monaco.suggest.CompletionOptions(undefined, new Set().add(monaco.languages.CompletionItemKind.Snippet)), + context, token); + let overwriteBefore = 0; + if (this.context === 'condition') { + overwriteBefore = position.column - 1; + } else { + // Inside the currly brackets, need to count how many useful characters are behind the position so they would all be taken into account + const value = editor.getModel()!.getValue(); + while ((position.column - 2 - overwriteBefore >= 0) + && value[position.column - 2 - overwriteBefore] !== '{' && value[position.column - 2 - overwriteBefore] !== ' ') { + overwriteBefore++; + } + } + for (const { completion } of items) { + completion.range = monaco.Range.fromPositions(position.delta(0, -overwriteBefore), position); + suggestions.push(completion); } } return { suggestions }; @@ -110,7 +121,7 @@ export class DebugBreakpointWidget implements Disposable { })); this.toDispose.push(this.zone.onDidLayoutChange(dimension => this.layout(dimension))); this.toDispose.push(input.getControl().onDidChangeModelContent(() => { - const heightInLines = input.getControl().getModel().getLineCount() + 1; + const heightInLines = input.getControl().getModel()!.getLineCount() + 1; this.zone.layout(heightInLines); this.updatePlaceholder(); })); @@ -152,9 +163,9 @@ export class DebugBreakpointWidget implements Disposable { const afterLineNumber = breakpoint ? breakpoint.line : position!.lineNumber; const afterColumn = breakpoint ? breakpoint.column : position!.column; const editor = this._input.getControl(); - const heightInLines = editor.getModel().getLineCount() + 1; + const heightInLines = editor.getModel()!.getLineCount() + 1; this.zone.show({ afterLineNumber, afterColumn, heightInLines, frameWidth: 1 }); - editor.setPosition(editor.getModel().getPositionAt(editor.getModel().getValueLength())); + editor.setPosition(editor.getModel()!.getPositionAt(editor.getModel()!.getValueLength())); this._input.focus(); } @@ -207,6 +218,7 @@ export class DebugBreakpointWidget implements Disposable { } const value = this._input.getControl().getValue(); const decorations: monaco.editor.IDecorationOptions[] = !!value ? [] : [{ + color: undefined, range: { startLineNumber: 0, endLineNumber: 0, diff --git a/packages/debug/src/browser/editor/debug-editor-model.ts b/packages/debug/src/browser/editor/debug-editor-model.ts index 672ef4d500f14..090b2cbcd49e5 100644 --- a/packages/debug/src/browser/editor/debug-editor-model.ts +++ b/packages/debug/src/browser/editor/debug-editor-model.ts @@ -79,7 +79,7 @@ export class DebugEditorModel implements Disposable { @postConstruct() protected init(): void { - this.uri = new URI(this.editor.getControl().getModel().uri.toString()); + this.uri = new URI(this.editor.getControl().getModel()!.uri.toString()); this.toDispose.pushAll([ this.hover, this.breakpointWidget, @@ -87,7 +87,7 @@ export class DebugEditorModel implements Disposable { this.editor.getControl().onMouseMove(event => this.handleMouseMove(event)), this.editor.getControl().onMouseLeave(event => this.handleMouseLeave(event)), this.editor.getControl().onKeyDown(() => this.hover.hide({ immediate: false })), - this.editor.getControl().getModel().onDidChangeDecorations(() => this.updateBreakpoints()), + this.editor.getControl().getModel()!.onDidChangeDecorations(() => this.updateBreakpoints()), this.sessions.onDidChange(() => this.renderFrames()) ]); this.renderFrames(); @@ -176,7 +176,7 @@ export class DebugEditorModel implements Disposable { protected updateBreakpointRanges(): void { this.breakpointRanges.clear(); for (const decoration of this.breakpointDecorations) { - const range = this.editor.getControl().getModel().getDecorationRange(decoration); + const range = this.editor.getControl().getModel()!.getDecorationRange(decoration)!; this.breakpointRanges.set(decoration, range); } } @@ -214,7 +214,7 @@ export class DebugEditorModel implements Disposable { return false; } for (const decoration of this.breakpointDecorations) { - const range = this.editor.getControl().getModel().getDecorationRange(decoration); + const range = this.editor.getControl().getModel()!.getDecorationRange(decoration); const oldRange = this.breakpointRanges.get(decoration)!; if (!range || !range.equalsRange(oldRange)) { return true; @@ -227,7 +227,7 @@ export class DebugEditorModel implements Disposable { const lines = new Set(); const breakpoints: SourceBreakpoint[] = []; for (const decoration of this.breakpointDecorations) { - const range = this.editor.getControl().getModel().getDecorationRange(decoration); + const range = this.editor.getControl().getModel()!.getDecorationRange(decoration); if (range && !lines.has(range.startLineNumber)) { const line = range.startLineNumber; const oldRange = this.breakpointRanges.get(decoration); @@ -242,7 +242,7 @@ export class DebugEditorModel implements Disposable { protected _position: monaco.Position | undefined; get position(): monaco.Position { - return this._position || this.editor.getControl().getPosition(); + return this._position || this.editor.getControl().getPosition()!; } get breakpoint(): DebugBreakpoint | undefined { return this.getBreakpoint(); @@ -285,12 +285,12 @@ export class DebugEditorModel implements Disposable { protected handleMouseDown(event: monaco.editor.IEditorMouseEvent): void { if (event.target && event.target.type === monaco.editor.MouseTargetType.GUTTER_GLYPH_MARGIN) { if (event.event.rightButton) { - this._position = event.target.position; + this._position = event.target.position!; this.contextMenu.render(DebugEditorModel.CONTEXT_MENU, event.event.browserEvent, () => setTimeout(() => this._position = undefined) ); } else { - this.doToggleBreakpoint(event.target.position); + this.doToggleBreakpoint(event.target.position!); } } this.hintBreakpoint(event); @@ -299,7 +299,7 @@ export class DebugEditorModel implements Disposable { this.showHover(event); this.hintBreakpoint(event); } - protected handleMouseLeave(event: monaco.editor.IEditorMouseEvent): void { + protected handleMouseLeave(event: monaco.editor.IPartialEditorMouseEvent): void { this.hideHover(event); this.deltaHintDecorations([]); } @@ -313,7 +313,7 @@ export class DebugEditorModel implements Disposable { this.hintDecorations = this.deltaDecorations(this.hintDecorations, hintDecorations); } protected createHintDecorations(event: monaco.editor.IEditorMouseEvent): monaco.editor.IModelDeltaDecoration[] { - if (event.target && event.target.type === monaco.editor.MouseTargetType.GUTTER_GLYPH_MARGIN) { + if (event.target && event.target.type === monaco.editor.MouseTargetType.GUTTER_GLYPH_MARGIN && event.target.position) { const lineNumber = event.target.position.lineNumber; if (!!this.sessions.getBreakpoint(this.uri, lineNumber)) { return []; @@ -337,14 +337,14 @@ export class DebugEditorModel implements Disposable { } if (targetType === monaco.editor.MouseTargetType.CONTENT_TEXT) { this.hover.show({ - selection: mouseEvent.target.range, + selection: mouseEvent.target.range!, immediate: false }); } else { this.hover.hide({ immediate: false }); } } - protected hideHover({ event }: monaco.editor.IEditorMouseEvent): void { + protected hideHover({ event }: monaco.editor.IPartialEditorMouseEvent): void { const rect = this.hover.getDomNode().getBoundingClientRect(); if (event.posx < rect.left || event.posx > rect.right || event.posy < rect.top || event.posy > rect.bottom) { this.hover.hide({ immediate: false }); @@ -354,7 +354,7 @@ export class DebugEditorModel implements Disposable { protected deltaDecorations(oldDecorations: string[], newDecorations: monaco.editor.IModelDeltaDecoration[]): string[] { this.updatingDecorations = true; try { - return this.editor.getControl().getModel().deltaDecorations(oldDecorations, newDecorations); + return this.editor.getControl().getModel()!.deltaDecorations(oldDecorations, newDecorations); } finally { this.updatingDecorations = false; } diff --git a/packages/debug/src/browser/editor/debug-editor-service.ts b/packages/debug/src/browser/editor/debug-editor-service.ts index 5275d3d9880dc..9b4ea52ad4908 100644 --- a/packages/debug/src/browser/editor/debug-editor-service.ts +++ b/packages/debug/src/browser/editor/debug-editor-service.ts @@ -62,7 +62,7 @@ export class DebugEditorService { if (!(editor instanceof MonacoEditor)) { return; } - const uri = editor.getControl().getModel().uri.toString(); + const uri = editor.getControl().getModel()!.uri.toString(); const debugModel = this.factory(editor); this.models.set(uri, debugModel); editor.getControl().onDidDispose(() => { @@ -122,15 +122,15 @@ export class DebugEditorService { showHover(): void { const { model } = this; if (model) { - const selection = model.editor.getControl().getSelection(); + const selection = model.editor.getControl().getSelection()!; model.hover.show({ selection, focus: true }); } } canShowHover(): boolean { const { model } = this; if (model) { - const selection = model.editor.getControl().getSelection(); - return !!model.editor.getControl().getModel().getWordAtPosition(selection.getStartPosition()); + const selection = model.editor.getControl().getSelection()!; + return !!model.editor.getControl().getModel()!.getWordAtPosition(selection.getStartPosition()); } return false; } diff --git a/packages/debug/src/browser/editor/debug-hover-widget.ts b/packages/debug/src/browser/editor/debug-hover-widget.ts index 9a3a766821557..1ed6dca22593c 100644 --- a/packages/debug/src/browser/editor/debug-hover-widget.ts +++ b/packages/debug/src/browser/editor/debug-hover-widget.ts @@ -159,7 +159,7 @@ export class DebugHoverWidget extends SourceTreeWidget implements monaco.editor. } super.show(); this.options = options; - const expression = this.expressionProvider.get(this.editor.getControl().getModel(), options.selection); + const expression = this.expressionProvider.get(this.editor.getControl().getModel()!, options.selection); if (!expression) { this.hide(); return; @@ -181,7 +181,7 @@ export class DebugHoverWidget extends SourceTreeWidget implements monaco.editor. protected isEditorFrame(): boolean { const { currentFrame } = this.sessions; return !!currentFrame && !!currentFrame.source && - this.editor.getControl().getModel().uri.toString() === currentFrame.source.uri.toString(); + this.editor.getControl().getModel()!.uri.toString() === currentFrame.source.uri.toString(); } getPosition(): monaco.editor.IContentWidgetPosition { @@ -189,7 +189,7 @@ export class DebugHoverWidget extends SourceTreeWidget implements monaco.editor. return undefined!; } const position = this.options && this.options.selection.getStartPosition(); - const word = position && this.editor.getControl().getModel().getWordAtPosition(position); + const word = position && this.editor.getControl().getModel()!.getWordAtPosition(position); return position && word ? { position: new monaco.Position(position.lineNumber, word.startColumn), preference: [ diff --git a/packages/editor-preview/src/browser/editor-preview-manager.ts b/packages/editor-preview/src/browser/editor-preview-manager.ts index 8337e37db3c54..2079e1ac257b1 100644 --- a/packages/editor-preview/src/browser/editor-preview-manager.ts +++ b/packages/editor-preview/src/browser/editor-preview-manager.ts @@ -22,7 +22,6 @@ import { EditorPreviewWidget } from './editor-preview-widget'; import { EditorPreviewWidgetFactory, EditorPreviewWidgetOptions } from './editor-preview-factory'; import { EditorPreviewPreferences } from './editor-preview-preferences'; import { WidgetOpenHandler, WidgetOpenerOptions } from '@theia/core/lib/browser'; -import { Deferred } from '@theia/core/lib/common/promise-util'; /** * Opener options containing an optional preview flag. @@ -109,38 +108,40 @@ export class EditorPreviewManager extends WidgetOpenHandler { - options = { ...options, mode: 'open' }; - - const deferred = new Deferred(); - const previousPreview = await this.currentEditorPreview; - this.currentEditorPreview = deferred.promise; + async open(uri: URI, options: PreviewEditorOpenerOptions = {}): Promise { + let widget = await this.pinCurrentEditor(uri, options); + if (widget) { + return widget; + } + widget = await this.replaceCurrentPreview(uri, options) || await this.openNewPreview(uri, options); + await this.editorManager.open(uri, options); + return widget; + } + protected async pinCurrentEditor(uri: URI, options: PreviewEditorOpenerOptions): Promise { if (await this.editorManager.getByUri(uri)) { - let widget: EditorWidget | EditorPreviewWidget = await this.editorManager.open(uri, options); - if (widget.parent instanceof EditorPreviewWidget) { + const editorWidget = await this.editorManager.open(uri, options); + if (editorWidget.parent instanceof EditorPreviewWidget) { if (!options.preview) { - widget.parent.pinEditorWidget(); + editorWidget.parent.pinEditorWidget(); } - widget = widget.parent; + return editorWidget.parent; } - this.shell.revealWidget(widget.id); - deferred.resolve(previousPreview); - return widget; + return editorWidget; } + } - if (!previousPreview) { - this.currentEditorPreview = super.open(uri, options) as Promise; - } else { - const childWidget = await this.editorManager.getOrCreateByUri(uri); - previousPreview.replaceEditorWidget(childWidget); - this.currentEditorPreview = Promise.resolve(previousPreview); + protected async replaceCurrentPreview(uri: URI, options: PreviewEditorOpenerOptions): Promise { + const currentPreview = await this.currentEditorPreview; + if (currentPreview) { + const editorWidget = await this.editorManager.getOrCreateByUri(uri); + currentPreview.replaceEditorWidget(editorWidget); + return currentPreview; } + } - const preview = await this.currentEditorPreview as EditorPreviewWidget; - this.editorManager.open(uri, options); - this.shell.revealWidget(preview.id); - return preview; + protected openNewPreview(uri: URI, options: PreviewEditorOpenerOptions): Promise { + return this.currentEditorPreview = super.open(uri, options) as Promise; } protected createWidgetOptions(uri: URI, options?: WidgetOpenerOptions): EditorPreviewWidgetOptions { diff --git a/packages/editor/src/browser/editor.ts b/packages/editor/src/browser/editor.ts index 34adec58bd53c..54bb9562261ee 100644 --- a/packages/editor/src/browser/editor.ts +++ b/packages/editor/src/browser/editor.ts @@ -119,7 +119,7 @@ export interface MouseTarget { /** * The target element */ - readonly element: Element; + readonly element?: Element; /** * The target type */ diff --git a/packages/languages/package.json b/packages/languages/package.json index c726873c9bfdc..7fb8d327fa46c 100644 --- a/packages/languages/package.json +++ b/packages/languages/package.json @@ -7,7 +7,7 @@ "@theia/output": "^0.9.0", "@theia/process": "^0.9.0", "@theia/workspace": "^0.9.0", - "@typefox/monaco-editor-core": "^0.14.6", + "@typefox/monaco-editor-core": "next", "@types/uuid": "^3.4.3", "monaco-languageclient": "next", "uuid": "^3.2.1" diff --git a/packages/monaco/src/browser/monaco-bulk-edit-service.ts b/packages/monaco/src/browser/monaco-bulk-edit-service.ts index ebfb6478aee2b..5a248ea2bd900 100644 --- a/packages/monaco/src/browser/monaco-bulk-edit-service.ts +++ b/packages/monaco/src/browser/monaco-bulk-edit-service.ts @@ -23,7 +23,7 @@ export class MonacoBulkEditService implements monaco.editor.IBulkEditService { @inject(MonacoWorkspace) protected readonly workspace: MonacoWorkspace; - apply(edit: monaco.languages.WorkspaceEdit): monaco.Promise { + apply(edit: monaco.languages.WorkspaceEdit): Promise { return this.workspace.applyBulkEdit(edit); } diff --git a/packages/monaco/src/browser/monaco-command-service.ts b/packages/monaco/src/browser/monaco-command-service.ts index c220874495621..9821595d55861 100644 --- a/packages/monaco/src/browser/monaco-command-service.ts +++ b/packages/monaco/src/browser/monaco-command-service.ts @@ -51,20 +51,16 @@ export class MonacoCommandService implements ICommandService { } // tslint:disable-next-line:no-any - executeCommand(commandId: any, ...args: any[]): monaco.Promise { + async executeCommand(commandId: any, ...args: any[]): Promise { const handler = this.commandRegistry.getActiveHandler(commandId, ...args); if (handler) { - try { - this._onWillExecuteCommand.fire({ commandId }); - return monaco.Promise.wrap(handler.execute(...args)); - } catch (err) { - return monaco.Promise.wrapError(err); - } + this._onWillExecuteCommand.fire({ commandId }); + return handler.execute(...args); } if (this.delegate) { return this.delegate.executeCommand(commandId, ...args); } - return monaco.Promise.wrapError(new Error(`command '${commandId}' not found`)); + throw new Error(`command '${commandId}' not found`); } } diff --git a/packages/monaco/src/browser/monaco-command.ts b/packages/monaco/src/browser/monaco-command.ts index 86771baf42838..18890b4eab16f 100644 --- a/packages/monaco/src/browser/monaco-command.ts +++ b/packages/monaco/src/browser/monaco-command.ts @@ -25,7 +25,6 @@ import { EditorCommands } from '@theia/editor/lib/browser'; import { MonacoEditor } from './monaco-editor'; import { MonacoCommandRegistry, MonacoEditorCommandHandler } from './monaco-command-registry'; import MenuRegistry = monaco.actions.MenuRegistry; -import MenuId = monaco.actions.MenuId; export type MonacoCommand = Command & { delegate?: string }; export namespace MonacoCommands { @@ -72,7 +71,7 @@ export namespace MonacoCommands { 'find.history.showPrevious', ]); const iconClasses = new Map(); - for (const menuItem of MenuRegistry.getMenuItems(MenuId.EditorContext)) { + for (const menuItem of MenuRegistry.getMenuItems(7)) { if (menuItem.command.iconClass) { iconClasses.set(menuItem.command.id, menuItem.command.iconClass); } @@ -251,7 +250,7 @@ export class MonacoEditorCommandHandlers implements CommandContribution { protected newKeyboardHandler(action: string): MonacoEditorCommandHandler { return { - execute: (editor, ...args) => editor.getControl().cursor.trigger('keyboard', action, args) + execute: (editor, ...args) => editor.getControl()._modelData.cursor.trigger('keyboard', action, args) }; } protected newCommandHandler(action: string): MonacoEditorCommandHandler { diff --git a/packages/monaco/src/browser/monaco-context-menu.ts b/packages/monaco/src/browser/monaco-context-menu.ts index 35cd1b6c7f483..44361aba3144e 100644 --- a/packages/monaco/src/browser/monaco-context-menu.ts +++ b/packages/monaco/src/browser/monaco-context-menu.ts @@ -25,7 +25,7 @@ import { CommandRegistry } from '@phosphor/commands'; @injectable() export class MonacoContextMenuService implements IContextMenuService { - constructor( @inject(ContextMenuRenderer) protected readonly contextMenuRenderer: ContextMenuRenderer) { + constructor(@inject(ContextMenuRenderer) protected readonly contextMenuRenderer: ContextMenuRenderer) { } showContextMenu(delegate: IContextMenuDelegate): void { @@ -35,29 +35,28 @@ export class MonacoContextMenuService implements IContextMenuService { if (delegate.hasOwnProperty('getKeyBinding')) { this.contextMenuRenderer.render(EDITOR_CONTEXT_MENU, anchor, () => delegate.onHide(false)); } else { - delegate.getActions().then(actions => { - const commands = new CommandRegistry(); - const menu = new Menu({ - commands - }); - - for (const action of actions) { - const commandId = 'quickfix_' + actions.indexOf(action); - commands.addCommand(commandId, { - label: action.label, - className: action.class, - isToggled: () => action.checked, - isEnabled: () => action.enabled, - execute: () => action.run() - }); - menu.addItem({ - type: 'command', - command: commandId - }); - } - menu.aboutToClose.connect(() => delegate.onHide(false)); - menu.open(anchor.x, anchor.y); + const actions = delegate.getActions(); + const commands = new CommandRegistry(); + const menu = new Menu({ + commands }); + + for (const action of actions) { + const commandId = 'quickfix_' + actions.indexOf(action); + commands.addCommand(commandId, { + label: action.label, + className: action.class, + isToggled: () => action.checked, + isEnabled: () => action.enabled, + execute: () => action.run() + }); + menu.addItem({ + type: 'command', + command: commandId + }); + } + menu.aboutToClose.connect(() => delegate.onHide(false)); + menu.open(anchor.x, anchor.y); } } diff --git a/packages/monaco/src/browser/monaco-diff-editor.ts b/packages/monaco/src/browser/monaco-diff-editor.ts index 7571ba446d744..45819e2bda2c7 100644 --- a/packages/monaco/src/browser/monaco-diff-editor.ts +++ b/packages/monaco/src/browser/monaco-diff-editor.ts @@ -14,12 +14,11 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import { MonacoToProtocolConverter, ProtocolToMonacoConverter } from 'monaco-languageclient'; import URI from '@theia/core/lib/common/uri'; import { Disposable } from '@theia/core/lib/common'; import { Dimension, DiffNavigator, DeltaDecorationParams } from '@theia/editor/lib/browser'; import { MonacoEditorModel } from './monaco-editor-model'; -import { MonacoEditor } from './monaco-editor'; +import { MonacoEditor, MonacoEditorServices } from './monaco-editor'; import { MonacoDiffNavigatorFactory } from './monaco-diff-navigator-factory'; import { DiffUris } from '@theia/core/lib/browser/diff-uris'; @@ -42,13 +41,12 @@ export class MonacoDiffEditor extends MonacoEditor { readonly node: HTMLElement, readonly originalModel: MonacoEditorModel, readonly modifiedModel: MonacoEditorModel, - protected readonly m2p: MonacoToProtocolConverter, - protected readonly p2m: ProtocolToMonacoConverter, + services: MonacoEditorServices, protected readonly diffNavigatorFactory: MonacoDiffNavigatorFactory, options?: MonacoDiffEditor.IOptions, override?: IEditorOverrideServices, ) { - super(uri, modifiedModel, node, m2p, p2m, options, override); + super(uri, modifiedModel, node, services, options, override); this.documents.add(originalModel); const original = originalModel.textEditorModel; const modified = modifiedModel.textEditorModel; diff --git a/packages/monaco/src/browser/monaco-editor-model.ts b/packages/monaco/src/browser/monaco-editor-model.ts index a6dd459735953..c874dd3f7e995 100644 --- a/packages/monaco/src/browser/monaco-editor-model.ts +++ b/packages/monaco/src/browser/monaco-editor-model.ts @@ -181,8 +181,9 @@ export class MonacoEditorModel implements ITextEditorModel, TextEditorDocument { return this.model; } - load(): monaco.Promise { - return monaco.Promise.wrap(this.resolveModel).then(() => this); + async load(): Promise { + await this.resolveModel; + return this; } save(): Promise { diff --git a/packages/monaco/src/browser/monaco-editor-provider.ts b/packages/monaco/src/browser/monaco-editor-provider.ts index 56cb15fdf1dec..3d5a4bd4a010a 100644 --- a/packages/monaco/src/browser/monaco-editor-provider.ts +++ b/packages/monaco/src/browser/monaco-editor-provider.ts @@ -25,7 +25,7 @@ import { MonacoCommandServiceFactory } from './monaco-command-service'; import { MonacoContextMenuService } from './monaco-context-menu'; import { MonacoDiffEditor } from './monaco-diff-editor'; import { MonacoDiffNavigatorFactory } from './monaco-diff-navigator-factory'; -import { MonacoEditor } from './monaco-editor'; +import { MonacoEditor, MonacoEditorServices } from './monaco-editor'; import { MonacoEditorModel, WillSaveMonacoModelEvent } from './monaco-editor-model'; import { MonacoEditorService } from './monaco-editor-service'; import { MonacoQuickOpenService } from './monaco-quick-open-service'; @@ -43,6 +43,9 @@ export class MonacoEditorProvider { @inject(MonacoBulkEditService) protected readonly bulkEditService: MonacoBulkEditService; + @inject(MonacoEditorServices) + protected readonly services: MonacoEditorServices; + private isWindowsBackend = false; private hookedConfigService: any = undefined; @@ -142,7 +145,7 @@ export class MonacoEditorProvider { protected async createMonacoEditor(uri: URI, override: IEditorOverrideServices, toDispose: DisposableCollection): Promise { const model = await this.getModel(uri, toDispose); const options = this.createMonacoEditorOptions(model); - const editor = new MonacoEditor(uri, model, document.createElement('div'), this.m2p, this.p2m, options, override); + const editor = new MonacoEditor(uri, model, document.createElement('div'), this.services, options, override); toDispose.push(this.editorPreferences.onPreferenceChanged(event => { if (event.affects(uri.toString(), model.languageId)) { this.updateMonacoEditorOptions(editor, event); @@ -202,7 +205,7 @@ export class MonacoEditorProvider { uri, document.createElement('div'), originalModel, modifiedModel, - this.m2p, this.p2m, + this.services, this.diffNavigatorFactory, options, override); @@ -299,18 +302,22 @@ export class MonacoEditorProvider { referencesController._ignoreModelChangeEvent = true; const range = monaco.Range.lift(ref.range).collapseToStart(); + // prerse the model that it does not get disposed if an editor preview replaces an editor + const model = referencesController._model; + referencesController._model = undefined; + referencesController._editorService.openCodeEditor({ resource: ref.uri, options: { selection: range } - }, control).done(openedEditor => { + }, control).then(openedEditor => { + referencesController._model = model; referencesController._ignoreModelChangeEvent = false; if (!openedEditor) { referencesController.closeWidget(); return; } if (openedEditor !== control) { - const model = referencesController._model; - // to preserve the references model + // preserve the model that it does not get disposed in `referencesController.closeWidget` referencesController._model = undefined; // to preserve the active editor @@ -360,8 +367,7 @@ export class MonacoEditorProvider { uri, document, node, - this.m2p, - this.p2m, + this.services, Object.assign({ model, isSimpleWidget: true, diff --git a/packages/monaco/src/browser/monaco-editor-service.ts b/packages/monaco/src/browser/monaco-editor-service.ts index 000f0c10b9f22..e42bcdfd4116d 100644 --- a/packages/monaco/src/browser/monaco-editor-service.ts +++ b/packages/monaco/src/browser/monaco-editor-service.ts @@ -56,15 +56,33 @@ export class MonacoEditorService extends monaco.services.CodeEditorServiceImpl { return editor && editor.getControl(); } - openCodeEditor(input: IResourceInput, source?: ICodeEditor, sideBySide?: boolean): monaco.Promise { + async openCodeEditor(input: IResourceInput, source?: ICodeEditor, sideBySide?: boolean): Promise { const uri = new URI(input.resource.toString()); const openerOptions = this.createEditorOpenerOptions(input, source, sideBySide); - return monaco.Promise.wrap(open(this.openerService, uri, openerOptions).then(widget => { - if (widget instanceof EditorWidget && widget.editor instanceof MonacoEditor) { - return widget.editor.getControl(); + const widget = await open(this.openerService, uri, openerOptions); + const editorWidget = await this.findEditorWidgetByUri(widget, uri.toString()); + if (editorWidget && editorWidget.editor instanceof MonacoEditor) { + return editorWidget.editor.getControl(); + } + return undefined; + } + + protected async findEditorWidgetByUri(widget: object | undefined, uriAsString: string): Promise { + if (widget instanceof EditorWidget) { + if (widget.editor.uri.toString() === uriAsString) { + return widget; } return undefined; - })); + } + if (ApplicationShell.TrackableWidgetProvider.is(widget)) { + for (const childWidget of await widget.getTrackableWidgets()) { + const editorWidget = await this.findEditorWidgetByUri(childWidget, uriAsString); + if (editorWidget) { + return editorWidget; + } + } + } + return undefined; } protected createEditorOpenerOptions(input: IResourceInput, source?: ICodeEditor, sideBySide?: boolean): EditorOpenerOptions { diff --git a/packages/monaco/src/browser/monaco-editor.ts b/packages/monaco/src/browser/monaco-editor.ts index b2fa80a8e2d0b..510ab7e64a1f6 100644 --- a/packages/monaco/src/browser/monaco-editor.ts +++ b/packages/monaco/src/browser/monaco-editor.ts @@ -14,9 +14,11 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ +import { injectable, inject, unmanaged } from 'inversify'; import { MonacoToProtocolConverter, ProtocolToMonacoConverter, TextEdit } from 'monaco-languageclient'; import { ElementExt } from '@phosphor/domutils'; import URI from '@theia/core/lib/common/uri'; +import { ContextKeyService } from '@theia/core/lib/browser/context-key-service'; import { DisposableCollection, Disposable, Emitter, Event } from '@theia/core/lib/common'; import { Dimension, @@ -42,11 +44,25 @@ import IEditorOverrideServices = monaco.editor.IEditorOverrideServices; import IStandaloneCodeEditor = monaco.editor.IStandaloneCodeEditor; import IIdentifiedSingleEditOperation = monaco.editor.IIdentifiedSingleEditOperation; import IBoxSizing = ElementExt.IBoxSizing; -import SuggestController = monaco.suggestController.SuggestController; -import CommonFindController = monaco.findController.CommonFindController; -import RenameController = monaco.rename.RenameController; -export class MonacoEditor implements TextEditor { +@injectable() +export class MonacoEditorServices { + + @inject(MonacoToProtocolConverter) + protected readonly m2p: MonacoToProtocolConverter; + + @inject(ProtocolToMonacoConverter) + protected readonly p2m: ProtocolToMonacoConverter; + + @inject(ContextKeyService) + protected readonly contextKeyService: ContextKeyService; + + constructor(@unmanaged() services: MonacoEditorServices) { + Object.assign(this, services); + } +} + +export class MonacoEditor extends MonacoEditorServices implements TextEditor { protected readonly toDispose = new DisposableCollection(); @@ -70,11 +86,11 @@ export class MonacoEditor implements TextEditor { readonly uri: URI, readonly document: MonacoEditorModel, readonly node: HTMLElement, - protected readonly m2p: MonacoToProtocolConverter, - protected readonly p2m: ProtocolToMonacoConverter, + services: MonacoEditorServices, options?: MonacoEditor.IOptions, - override?: IEditorOverrideServices, + override?: IEditorOverrideServices ) { + super(services); this.toDispose.pushAll([ this.onCursorPositionChangedEmitter, this.onSelectionChangedEmitter, @@ -130,13 +146,14 @@ export class MonacoEditor implements TextEditor { this.onFocusChangedEmitter.fire(this.isFocused()) )); this.toDispose.push(codeEditor.onMouseDown(e => { - const { position, range } = e.target; + const { element, position, range } = e.target; this.onMouseDownEmitter.fire({ target: { ...e.target, + element: element || undefined, mouseColumn: this.m2p.asPosition(undefined, e.target.mouseColumn).character, - range: range && this.m2p.asRange(range), - position: position && this.m2p.asPosition(position.lineNumber, position.column) + range: range && this.m2p.asRange(range) || undefined, + position: position && this.m2p.asPosition(position.lineNumber, position.column) || undefined }, event: e.event.browserEvent }); @@ -167,7 +184,7 @@ export class MonacoEditor implements TextEditor { } get cursor(): Position { - const { lineNumber, column } = this.editor.getPosition(); + const { lineNumber, column } = this.editor.getPosition()!; return this.m2p.asPosition(lineNumber, column); } @@ -181,7 +198,7 @@ export class MonacoEditor implements TextEditor { } get selection(): Range { - return this.m2p.asRange(this.editor.getSelection()); + return this.m2p.asRange(this.editor.getSelection()!); } set selection(selection: Range) { @@ -236,8 +253,10 @@ export class MonacoEditor implements TextEditor { blur(): void { const node = this.editor.getDomNode(); - const textarea = node.querySelector('textarea') as HTMLElement; - textarea.blur(); + if (node) { + const textarea = node.querySelector('textarea') as HTMLElement; + textarea.blur(); + } } isFocused({ strict }: { strict: boolean } = { strict: false }): boolean { @@ -262,22 +281,21 @@ export class MonacoEditor implements TextEditor { * `true` if the suggest widget is visible in the editor. Otherwise, `false`. */ isSuggestWidgetVisible(): boolean { - const widget = this.editor.getContribution('editor.contrib.suggestController')._widget; - return widget ? widget.suggestWidgetVisible.get() : false; + return this.contextKeyService.match('suggestWidgetVisible', this.editor.getDomNode() || this.node); } /** * `true` if the find (and replace) widget is visible in the editor. Otherwise, `false`. */ isFindWidgetVisible(): boolean { - return this.editor.getContribution('editor.contrib.findController')._findWidgetVisible.get(); + return this.contextKeyService.match('findWidgetVisible', this.editor.getDomNode() || this.node); } /** * `true` if the name rename refactoring input HTML element is visible. Otherwise, `false`. */ isRenameInputVisible(): boolean { - return this.editor.getContribution('editor.contrib.renameController')._renameInputVisible.get(); + return this.contextKeyService.match('renameInputVisible', this.editor.getDomNode() || this.node); } dispose(): void { @@ -342,7 +360,7 @@ export class MonacoEditor implements TextEditor { const configuration = this.editor.getConfiguration(); const lineHeight = configuration.lineHeight; - const lineCount = this.editor.getModel().getLineCount(); + const lineCount = this.editor.getModel()!.getLineCount(); const contentHeight = lineHeight * lineCount; const horizontalScrollbarHeight = configuration.layoutInfo.horizontalScrollbarHeight; @@ -366,12 +384,11 @@ export class MonacoEditor implements TextEditor { return !!action && action.isSupported(); } - runAction(id: string): monaco.Promise { + async runAction(id: string): Promise { const action = this.editor.getAction(id); if (action && action.isSupported()) { - return action.run(); + await action.run(); } - return monaco.Promise.as(undefined); } get commandService(): monaco.commands.ICommandService { @@ -400,7 +417,7 @@ export class MonacoEditor implements TextEditor { const start = toPosition(startLineNumber).lineNumber; const end = toPosition(endLineNumber).lineNumber; return this.editor - .getModel() + .getModel()! .getLinesDecorations(start, end) .map(this.toEditorDecoration.bind(this)); } @@ -440,7 +457,7 @@ export class MonacoEditor implements TextEditor { } storeViewState(): object { - return this.editor.saveViewState(); + return this.editor.saveViewState()!; } restoreViewState(state: object): void { diff --git a/packages/monaco/src/browser/monaco-frontend-module.ts b/packages/monaco/src/browser/monaco-frontend-module.ts index 668e6e85cbf1e..10fd88cff58ce 100644 --- a/packages/monaco/src/browser/monaco-frontend-module.ts +++ b/packages/monaco/src/browser/monaco-frontend-module.ts @@ -57,6 +57,7 @@ import { MonacoMimeService } from './monaco-mime-service'; import { MimeService } from '@theia/core/lib/browser/mime-service'; import debounce = require('lodash.debounce'); +import { MonacoEditorServices } from './monaco-editor'; const deepmerge: (args: object[]) => object = require('deepmerge').default.all; @@ -91,6 +92,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(MonacoEditorService).toSelf().inSingletonScope(); bind(MonacoTextModelService).toSelf().inSingletonScope(); bind(MonacoContextMenuService).toSelf().inSingletonScope(); + bind(MonacoEditorServices).toSelf().inSingletonScope(); bind(MonacoEditorProvider).toSelf().inSingletonScope(); bind(MonacoCommandService).toSelf().inTransientScope(); bind(MonacoCommandServiceFactory).toAutoFactory(MonacoCommandService); @@ -152,7 +154,7 @@ export function createMonacoConfigurationService(container: interfaces.Container const initFromConfiguration = debounce(() => { const event = new monaco.services.ConfigurationChangeEvent(); - event._source = monaco.services.ConfigurationTarget.DEFAULT; + event._source = 6 /* DEFAULT */; service._onDidChangeConfiguration.fire(event); }); preferences.onPreferenceChanged(e => { diff --git a/packages/monaco/src/browser/monaco-keybinding.ts b/packages/monaco/src/browser/monaco-keybinding.ts index 816bfe908e435..a6cb7a2b784f1 100644 --- a/packages/monaco/src/browser/monaco-keybinding.ts +++ b/packages/monaco/src/browser/monaco-keybinding.ts @@ -43,8 +43,8 @@ export class MonacoKeybindingContribution implements KeybindingContribution { const command = this.commands.validate(item.command); if (command) { const raw = item.keybinding; - const keybinding = raw.type === monaco.keybindings.KeybindingType.Simple - ? this.keyCode(raw as monaco.keybindings.SimpleKeybinding).toString() + const keybinding = raw instanceof monaco.keybindings.SimpleKeybinding + ? this.keyCode(raw).toString() : this.keySequence(raw as monaco.keybindings.ChordKeybinding).join(' '); const isInDiffEditor = item.when && /(^|[^!])\bisInDiffEditor\b/gm.test(item.when.serialize()); const context = isInDiffEditor @@ -91,9 +91,6 @@ export class MonacoKeybindingContribution implements KeybindingContribution { } protected keySequence(keybinding: monaco.keybindings.ChordKeybinding): KeySequence { - return [ - this.keyCode(keybinding.firstPart), - this.keyCode(keybinding.chordPart) - ]; + return keybinding.parts.map(part => this.keyCode(part)); } } diff --git a/packages/monaco/src/browser/monaco-languages.ts b/packages/monaco/src/browser/monaco-languages.ts index e7f4aaf78ea05..be13a36088fda 100644 --- a/packages/monaco/src/browser/monaco-languages.ts +++ b/packages/monaco/src/browser/monaco-languages.ts @@ -17,7 +17,12 @@ import { injectable, inject, decorate } from 'inversify'; import { MonacoLanguages as BaseMonacoLanguages, ProtocolToMonacoConverter, - MonacoToProtocolConverter + MonacoToProtocolConverter, + DocumentSelector, + SignatureHelpProvider, + MonacoModelIdentifier, + CodeActionProvider, + CodeLensProvider } from 'monaco-languageclient'; import { Languages, Diagnostic, DiagnosticCollection, Language, WorkspaceSymbolProvider } from '@theia/languages/lib/browser'; import { ProblemManager } from '@theia/markers/lib/browser/problem/problem-manager'; @@ -138,4 +143,67 @@ export class MonacoLanguages extends BaseMonacoLanguages implements Languages { return languages; } + protected createSignatureHelpProvider(selector: DocumentSelector, provider: SignatureHelpProvider, ...triggerCharacters: string[]): monaco.languages.SignatureHelpProvider { + const signatureHelpTriggerCharacters = [...(provider.triggerCharacters || triggerCharacters || [])]; + const signatureHelpRetriggerCharacters = [...(provider.retriggerCharacters || [])]; + return { + signatureHelpTriggerCharacters, + signatureHelpRetriggerCharacters, + provideSignatureHelp: async (model, position, token) => { + if (!this.matchModel(selector, MonacoModelIdentifier.fromModel(model))) { + return undefined!; + } + const params = this.m2p.asTextDocumentPositionParams(model, position); + const help = await provider.provideSignatureHelp(params, token, undefined! /* not used by LC */); + return { + value: this.p2m.asSignatureHelp(help), + dispose: () => { } + }; + } + }; + } + + protected createCodeActionProvider(selector: DocumentSelector, provider: CodeActionProvider): monaco.languages.CodeActionProvider { + return { + provideCodeActions: async (model, range, context, token) => { + if (!this.matchModel(selector, MonacoModelIdentifier.fromModel(model))) { + return undefined!; + } + const params = this.m2p.asCodeActionParams(model, range, context); + const actions = await provider.provideCodeActions(params, token); + return { + actions: this.p2m.asCodeActions(actions), + dispose: () => { } + }; + } + }; + } + + protected createCodeLensProvider(selector: DocumentSelector, provider: CodeLensProvider): monaco.languages.CodeLensProvider { + return { + provideCodeLenses: async (model, token) => { + if (!this.matchModel(selector, MonacoModelIdentifier.fromModel(model))) { + return undefined!; + } + const params = this.m2p.asCodeLensParams(model); + const lenses = await provider.provideCodeLenses(params, token); + return { + lenses: this.p2m.asCodeLenses(lenses), + dispose: () => { } + }; + }, + resolveCodeLens: provider.resolveCodeLens ? (model, codeLens, token) => { + if (!this.matchModel(selector, MonacoModelIdentifier.fromModel(model))) { + return codeLens; + } + const protocolCodeLens = this.m2p.asCodeLens(codeLens); + return provider.resolveCodeLens!(protocolCodeLens, token).then(result => { + const resolvedCodeLens = this.p2m.asCodeLens(result); + Object.assign(codeLens, resolvedCodeLens); + return codeLens; + }); + } : ((m, codeLens, t) => codeLens) + }; + } + } diff --git a/packages/monaco/src/browser/monaco-loader.ts b/packages/monaco/src/browser/monaco-loader.ts index b6e7fd0814474..b18b827eccc48 100644 --- a/packages/monaco/src/browser/monaco-loader.ts +++ b/packages/monaco/src/browser/monaco-loader.ts @@ -57,7 +57,6 @@ export function loadMonaco(vsRequire: any): Promise { 'vs/editor/browser/editorExtensions', 'vs/editor/standalone/browser/simpleServices', 'vs/editor/standalone/browser/standaloneServices', - 'vs/base/parts/quickopen/common/quickOpen', 'vs/base/parts/quickopen/browser/quickOpenWidget', 'vs/base/parts/quickopen/browser/quickOpenModel', 'vs/base/common/filters', @@ -65,9 +64,6 @@ export function loadMonaco(vsRequire: any): Promise { 'vs/base/common/platform', 'vs/editor/common/modes', 'vs/editor/contrib/suggest/suggest', - 'vs/editor/contrib/suggest/suggestController', - 'vs/editor/contrib/find/findController', - 'vs/editor/contrib/rename/rename', 'vs/editor/contrib/snippet/snippetParser', 'vs/platform/configuration/common/configuration', 'vs/platform/configuration/common/configurationModels', @@ -77,8 +73,8 @@ export function loadMonaco(vsRequire: any): Promise { 'vs/platform/contextkey/browser/contextKeyService' ], (css: any, html: any, commands: any, actions: any, keybindingsRegistry: any, keybindingResolver: any, resolvedKeybinding: any, keybindingLabels: any, - keyCodes: any, mime: any, editorExtensions: any, simpleServices: any, standaloneServices: any, quickOpen: any, quickOpenWidget: any, quickOpenModel: any, - filters: any, styler: any, platform: any, modes: any, suggest: any, suggestController: any, findController: any, rename: any, snippetParser: any, + keyCodes: any, mime: any, editorExtensions: any, simpleServices: any, standaloneServices: any, quickOpenWidget: any, quickOpenModel: any, + filters: any, styler: any, platform: any, modes: any, suggest: any, snippetParser: any, configuration: any, configurationModels: any, codeEditorService: any, codeEditorServiceImpl: any, contextKey: any, contextKeyService: any) => { @@ -87,16 +83,13 @@ export function loadMonaco(vsRequire: any): Promise { global.monaco.actions = actions; global.monaco.keybindings = Object.assign({}, keybindingsRegistry, keybindingResolver, resolvedKeybinding, keybindingLabels, keyCodes); global.monaco.services = Object.assign({}, simpleServices, standaloneServices, configuration, configurationModels, codeEditorService, codeEditorServiceImpl); - global.monaco.quickOpen = Object.assign({}, quickOpen, quickOpenWidget, quickOpenModel); + global.monaco.quickOpen = Object.assign({}, quickOpenWidget, quickOpenModel); global.monaco.filters = filters; global.monaco.theme = styler; global.monaco.platform = platform; global.monaco.editorExtensions = editorExtensions; global.monaco.modes = modes; global.monaco.suggest = suggest; - global.monaco.suggestController = suggestController; - global.monaco.findController = findController; - global.monaco.rename = rename; global.monaco.snippetParser = snippetParser; global.monaco.contextkey = contextKey; global.monaco.contextKeyService = contextKeyService; diff --git a/packages/monaco/src/browser/monaco-menu.ts b/packages/monaco/src/browser/monaco-menu.ts index c499b614455cb..cd75810a79b75 100644 --- a/packages/monaco/src/browser/monaco-menu.ts +++ b/packages/monaco/src/browser/monaco-menu.ts @@ -20,7 +20,6 @@ import { EDITOR_CONTEXT_MENU } from '@theia/editor/lib/browser'; import { MonacoCommands } from './monaco-command'; import { MonacoCommandRegistry } from './monaco-command-registry'; import MenuRegistry = monaco.actions.MenuRegistry; -import MenuId = monaco.actions.MenuId; export interface MonacoActionGroup { id: string; @@ -75,7 +74,7 @@ export class MonacoEditorMenuContribution implements MenuContribution { ) { } registerMenus(registry: MenuModelRegistry): void { - for (const item of MenuRegistry.getMenuItems(MenuId.EditorContext)) { + for (const item of MenuRegistry.getMenuItems(7)) { const commandId = this.commands.validate(item.command.id); if (commandId) { const menuPath = [...EDITOR_CONTEXT_MENU, (item.group || '')]; diff --git a/packages/monaco/src/browser/monaco-outline-contribution.ts b/packages/monaco/src/browser/monaco-outline-contribution.ts index 816c5898d7a25..2ebcbda6bf3db 100644 --- a/packages/monaco/src/browser/monaco-outline-contribution.ts +++ b/packages/monaco/src/browser/monaco-outline-contribution.ts @@ -97,10 +97,12 @@ export class MonacoOutlineContribution implements FrontendApplicationContributio const editor = this.editorManager.currentEditor; if (editor) { const model = MonacoEditor.get(editor)!.getControl().getModel(); - this.toDisposeOnEditor.push(model.onDidChangeContent(() => { - this.roots = undefined; // Invalidate the previously resolved roots. - this.updateOutline(); - })); + if (model) { + this.toDisposeOnEditor.push(model.onDidChangeContent(() => { + this.roots = undefined; // Invalidate the previously resolved roots. + this.updateOutline(); + })); + } this.toDisposeOnEditor.push(editor.editor.onSelectionChanged(selection => this.updateOutline(selection))); } this.updateOutline(); @@ -145,7 +147,7 @@ export class MonacoOutlineContribution implements FrontendApplicationContributio if (token.isCancellationRequested) { return []; } - const nodes = this.createNodes(uri, symbols); + const nodes = this.createNodes(uri, symbols || []); this.roots.push(...nodes); } catch { /* collect symbols from other providers */ diff --git a/packages/monaco/src/browser/monaco-quick-open-service.ts b/packages/monaco/src/browser/monaco-quick-open-service.ts index a971abf5bfc7e..6035a52be84e5 100644 --- a/packages/monaco/src/browser/monaco-quick-open-service.ts +++ b/packages/monaco/src/browser/monaco-quick-open-service.ts @@ -17,10 +17,11 @@ import { injectable, inject, postConstruct } from 'inversify'; import { MessageType } from '@theia/core/lib/common/message-service-protocol'; import { - QuickOpenService, QuickOpenModel, QuickOpenOptions, QuickOpenItem, QuickOpenGroupItem, - QuickOpenMode, KeySequence, QuickOpenActionProvider, QuickOpenAction, ResolvedKeybinding, + QuickOpenService, QuickOpenOptions, QuickOpenItem, QuickOpenGroupItem, + QuickOpenMode, KeySequence, ResolvedKeybinding, KeyCode, Key, KeybindingRegistry } from '@theia/core/lib/browser'; +import { QuickOpenModel, QuickOpenActionProvider, QuickOpenAction } from '@theia/core/lib/common/quick-open-model'; import { KEY_CODE_MAP } from './monaco-keycode-map'; import { ContextKey } from '@theia/core/lib/browser/context-key-service'; import { MonacoContextKeyService } from './monaco-context-key-service'; @@ -439,13 +440,13 @@ export class QuickOpenEntry extends monaco.quickOpen.QuickOpenEntry { } run(mode: monaco.quickOpen.Mode): boolean { - if (mode === monaco.quickOpen.Mode.OPEN) { + if (mode === 1) { return this.item.run(QuickOpenMode.OPEN); } - if (mode === monaco.quickOpen.Mode.OPEN_IN_BACKGROUND) { + if (mode === 2) { return this.item.run(QuickOpenMode.OPEN_IN_BACKGROUND); } - if (mode === monaco.quickOpen.Mode.PREVIEW) { + if (mode === 0) { return this.item.run(QuickOpenMode.PREVIEW); } return false; @@ -527,18 +528,17 @@ export class MonacoQuickOpenActionProvider implements monaco.quickOpen.IActionPr } // tslint:disable-next-line:no-any - async getActions(element: any, entry: QuickOpenEntry | QuickOpenEntryGroup): monaco.Promise { + async getActions(element: any, entry: QuickOpenEntry | QuickOpenEntryGroup): Promise { const actions = await this.provider.getActions(entry.item); - const monacoActions = actions.map(action => new MonacoQuickOpenAction(action)); - return monaco.Promise.wrap(monacoActions); + return actions.map(action => new MonacoQuickOpenAction(action)); } hasSecondaryActions(): boolean { return false; } - getSecondaryActions(): monaco.Promise { - return monaco.Promise.wrap([]); + async getSecondaryActions(): Promise { + return []; } getActionItem(): undefined { @@ -555,62 +555,44 @@ interface TheiaKeybindingService { class TheiaResolvedKeybinding extends monaco.keybindings.ResolvedKeybinding { - protected readonly parts: { key: string | null, modifiers: monaco.keybindings.Modifiers }[]; + protected readonly parts: monaco.keybindings.ResolvedKeybindingPart[]; constructor(protected readonly keySequence: KeySequence, keybindingService: TheiaKeybindingService) { super(); - this.parts = keySequence.map(keyCode => ({ + this.parts = keySequence.map(keyCode => { // tslint:disable-next-line:no-null-keyword - key: keyCode.key ? keybindingService.acceleratorForKey(keyCode.key) : null, - modifiers: { - ctrlKey: keyCode.ctrl, - shiftKey: keyCode.shift, - altKey: keyCode.alt, - metaKey: keyCode.meta - } - })); - } - - private getKeyAndModifiers(index: number): { - key: string | null; - modifiers: monaco.keybindings.Modifiers; - } | { - key: null; - modifiers: null; - } { - if (index >= this.parts.length) { - // tslint:disable-next-line:no-null-keyword - return { key: null, modifiers: null }; - } - return this.parts[index]; + const keyLabel = keyCode.key ? keybindingService.acceleratorForKey(keyCode.key) : null; + const keyAriaLabel = keyLabel; + return new monaco.keybindings.ResolvedKeybindingPart( + keyCode.ctrl, + keyCode.shift, + keyCode.alt, + keyCode.meta, + keyLabel, + keyAriaLabel + ); + }); } - public getLabel(): string { - const firstPart = this.getKeyAndModifiers(0); - const chordPart = this.getKeyAndModifiers(1); - return monaco.keybindings.UILabelProvider.toLabel(firstPart.modifiers, firstPart.key, - chordPart.modifiers, chordPart.key, monaco.platform.OS); + public getLabel(): string | null { + return monaco.keybindings.UILabelProvider.toLabel(monaco.platform.OS, this.parts, p => p.keyLabel); } - public getAriaLabel(): string { - const firstPart = this.getKeyAndModifiers(0); - const chordPart = this.getKeyAndModifiers(1); - return monaco.keybindings.AriaLabelProvider.toLabel(firstPart.modifiers, firstPart.key, - chordPart.modifiers, chordPart.key, monaco.platform.OS); + public getAriaLabel(): string | null { + return monaco.keybindings.UILabelProvider.toLabel(monaco.platform.OS, this.parts, p => p.keyAriaLabel); } - public getElectronAccelerator(): string { - const firstPart = this.getKeyAndModifiers(0); - return monaco.keybindings.ElectronAcceleratorLabelProvider.toLabel(firstPart.modifiers, firstPart.key, + public getElectronAccelerator(): string | null { + if (this.isChord) { + // Electron cannot handle chords // tslint:disable-next-line:no-null-keyword - null, null, monaco.platform.OS); + return null; + } + return monaco.keybindings.ElectronAcceleratorLabelProvider.toLabel(monaco.platform.OS, this.parts, p => p.keyLabel); } - public getUserSettingsLabel(): string { - const firstPart = this.getKeyAndModifiers(0); - const chordPart = this.getKeyAndModifiers(1); - return monaco.keybindings.UserSettingsLabelProvider.toLabel(firstPart.modifiers, firstPart.key, - chordPart.modifiers, chordPart.key, monaco.platform.OS); + public getUserSettingsLabel(): string | null { + return monaco.keybindings.UserSettingsLabelProvider.toLabel(monaco.platform.OS, this.parts, p => p.keyLabel); } public isWYSIWYG(): boolean { @@ -618,24 +600,14 @@ class TheiaResolvedKeybinding extends monaco.keybindings.ResolvedKeybinding { } public isChord(): boolean { - return this.parts.length >= 2; + return this.parts.length > 1; } - public getDispatchParts(): [string | null, string | null] { - const firstKeybinding = this.toKeybinding(0)!; - const firstPart = monaco.keybindings.USLayoutResolvedKeybinding.getDispatchStr(firstKeybinding); - const chordKeybinding = this.toKeybinding(1); - // tslint:disable-next-line:no-null-keyword - const chordPart = chordKeybinding ? monaco.keybindings.USLayoutResolvedKeybinding.getDispatchStr(chordKeybinding) : null; - return [firstPart, chordPart]; + public getDispatchParts(): (string | null)[] { + return this.keySequence.map(keyCode => monaco.keybindings.USLayoutResolvedKeybinding.getDispatchStr(this.toKeybinding(keyCode))); } - private toKeybinding(index: number): monaco.keybindings.SimpleKeybinding | null { - if (index >= this.keySequence.length) { - // tslint:disable-next-line:no-null-keyword - return null; - } - const keyCode = this.keySequence[index]; + private toKeybinding(keyCode: KeyCode): monaco.keybindings.SimpleKeybinding { return new monaco.keybindings.SimpleKeybinding( keyCode.ctrl, keyCode.shift, @@ -645,27 +617,8 @@ class TheiaResolvedKeybinding extends monaco.keybindings.ResolvedKeybinding { ); } - public getParts(): [monaco.keybindings.ResolvedKeybindingPart | null, monaco.keybindings.ResolvedKeybindingPart | null] { - return [ - this.toResolvedKeybindingPart(0), - this.toResolvedKeybindingPart(1) - ]; - } - - private toResolvedKeybindingPart(index: number): monaco.keybindings.ResolvedKeybindingPart | null { - if (index >= this.parts.length) { - // tslint:disable-next-line:no-null-keyword - return null; - } - const part = this.parts[index]; - return new monaco.keybindings.ResolvedKeybindingPart( - part.modifiers.ctrlKey, - part.modifiers.shiftKey, - part.modifiers.altKey, - part.modifiers.metaKey, - part.key!, - part.key! - ); + public getParts(): monaco.keybindings.ResolvedKeybindingPart[] { + return this.parts; } } diff --git a/packages/monaco/src/browser/monaco-semantic-highlighting-service.ts b/packages/monaco/src/browser/monaco-semantic-highlighting-service.ts index db162f29dc13f..bb6db918b4953 100644 --- a/packages/monaco/src/browser/monaco-semantic-highlighting-service.ts +++ b/packages/monaco/src/browser/monaco-semantic-highlighting-service.ts @@ -79,7 +79,7 @@ export class MonacoSemanticHighlightingService extends SemanticHighlightingServi protected async model(uri: string | URI): Promise { const editor = await this.editor(uri); if (editor) { - return editor.getControl().getModel(); + return editor.getControl().getModel() || undefined; } return undefined; } diff --git a/packages/monaco/src/browser/monaco-snippet-suggest-provider.ts b/packages/monaco/src/browser/monaco-snippet-suggest-provider.ts index eac9538cca13e..e7c3ea405d9aa 100644 --- a/packages/monaco/src/browser/monaco-snippet-suggest-provider.ts +++ b/packages/monaco/src/browser/monaco-snippet-suggest-provider.ts @@ -19,22 +19,23 @@ import URI from '@theia/core/lib/common/uri'; import { FileSystem, FileSystemError } from '@theia/filesystem/lib/common'; @injectable() -export class MonacoSnippetSuggestProvider implements monaco.modes.ISuggestSupport { +export class MonacoSnippetSuggestProvider implements monaco.languages.CompletionItemProvider { @inject(FileSystem) protected readonly filesystem: FileSystem; - protected readonly snippets = new Map(); + protected readonly snippets = new Map(); protected readonly pendingSnippets = new Map[]>(); - async provideCompletionItems(model: monaco.editor.ITextModel): Promise { + async provideCompletionItems(model: monaco.editor.ITextModel, position: monaco.Position): Promise { const languageId = model.getModeId(); // TODO: look up a language id at the position await this.loadSnippets(languageId); - const suggestions = this.snippets.get(languageId) || []; + const range = monaco.Range.fromPositions(position); + const suggestions = (this.snippets.get(languageId) || []).map(snippet => new MonacoSnippetSuggestion(snippet, range)); return { suggestions }; } - resolveCompletionItem(_: monaco.editor.ITextModel, __: monaco.Position, item: monaco.modes.ISuggestion): monaco.modes.ISuggestion { + resolveCompletionItem(_: monaco.editor.ITextModel, __: monaco.Position, item: monaco.languages.CompletionItem): monaco.languages.CompletionItem { return item instanceof MonacoSnippetSuggestion ? item.resolve() : item; } @@ -125,7 +126,7 @@ export class MonacoSnippetSuggestProvider implements monaco.modes.ISuggestSuppor for (const snippet of snippets) { for (const scope of snippet.scopes) { const languageSnippets = this.snippets.get(scope) || []; - languageSnippets.push(new MonacoSnippetSuggestion(snippet)); + languageSnippets.push(snippet); this.snippets.set(scope, languageSnippets); } } @@ -162,19 +163,22 @@ export interface Snippet { readonly source: string } -export class MonacoSnippetSuggestion implements monaco.modes.ISuggestion { +export class MonacoSnippetSuggestion implements monaco.languages.CompletionItem { readonly label: string; readonly detail: string; readonly sortText: string; readonly noAutoAccept = true; - readonly type: 'snippet' = 'snippet'; - readonly snippetType: 'textmate' = 'textmate'; + readonly kind = monaco.languages.CompletionItemKind.Snippet; + readonly insertTextRules = monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet; insertText: string; documentation?: monaco.IMarkdownString; - constructor(protected readonly snippet: Snippet) { + constructor( + protected readonly snippet: Snippet, + readonly range: monaco.Range + ) { this.label = snippet.prefix; this.detail = `${snippet.description || snippet.name} (${snippet.source})`; this.insertText = snippet.body; diff --git a/packages/monaco/src/browser/monaco-status-bar-contribution.ts b/packages/monaco/src/browser/monaco-status-bar-contribution.ts index 455b0148491f5..1e77eaf77f1c6 100644 --- a/packages/monaco/src/browser/monaco-status-bar-contribution.ts +++ b/packages/monaco/src/browser/monaco-status-bar-contribution.ts @@ -99,6 +99,6 @@ export class MonacoStatusBarContribution implements FrontendApplicationContribut protected getModel(editor: EditorWidget | undefined): monaco.editor.IModel | undefined { const monacoEditor = MonacoEditor.get(editor); - return monacoEditor && monacoEditor.getControl().getModel(); + return monacoEditor && monacoEditor.getControl().getModel() || undefined; } } diff --git a/packages/monaco/src/browser/monaco-text-model-service.ts b/packages/monaco/src/browser/monaco-text-model-service.ts index e4dcef0e0b0dd..594128662a09e 100644 --- a/packages/monaco/src/browser/monaco-text-model-service.ts +++ b/packages/monaco/src/browser/monaco-text-model-service.ts @@ -52,8 +52,8 @@ export class MonacoTextModelService implements monaco.editor.ITextModelService { return this._models.onDidCreate; } - createModelReference(raw: monaco.Uri | URI): monaco.Promise> { - return monaco.Promise.wrap(this._models.acquire(raw.toString())); + createModelReference(raw: monaco.Uri | URI): Promise> { + return this._models.acquire(raw.toString()); } protected async loadModel(uri: URI): Promise { diff --git a/packages/monaco/src/browser/monaco-workspace.ts b/packages/monaco/src/browser/monaco-workspace.ts index 3456b470f76b4..dc054c618b9f9 100644 --- a/packages/monaco/src/browser/monaco-workspace.ts +++ b/packages/monaco/src/browser/monaco-workspace.ts @@ -268,7 +268,7 @@ export class MonacoWorkspace implements lang.Workspace { return true; } - async applyBulkEdit(workspaceEdit: monaco.languages.WorkspaceEdit, options?: EditorOpenerOptions): monaco.Promise { + async applyBulkEdit(workspaceEdit: monaco.languages.WorkspaceEdit, options?: EditorOpenerOptions): Promise { try { const unresolvedEditorEdits = this.groupEdits(workspaceEdit); const editorEdits = await this.openEditors(unresolvedEditorEdits, options); @@ -278,7 +278,7 @@ export class MonacoWorkspace implements lang.Workspace { editorEdits.forEach(editorEdit => { const editor = editorEdit.editor!; const model = editor!.document.textEditorModel; - const currentSelections = editor!.getControl().getSelections(); + const currentSelections = editor!.getControl().getSelections() || []; const editOperations: monaco.editor.IIdentifiedSingleEditOperation[] = editorEdit.textEdits.map(edit => ({ identifier: undefined!, forceMoveMarkers: false, diff --git a/packages/monaco/src/browser/textmate/textmate-snippet-completion-provider.ts b/packages/monaco/src/browser/textmate/textmate-snippet-completion-provider.ts index a675197aabb07..d443f99c88109 100644 --- a/packages/monaco/src/browser/textmate/textmate-snippet-completion-provider.ts +++ b/packages/monaco/src/browser/textmate/textmate-snippet-completion-provider.ts @@ -33,9 +33,8 @@ export class TextmateSnippetCompletionProvider implements monaco.languages.Compl documentation: { value: '```' + this.mdLanguage + '\n' + this.replaceVariables(insertText) + '```' }, - insertText: { - value: insertText - } + insertText: insertText, + range: undefined! }); } } @@ -46,9 +45,11 @@ export class TextmateSnippetCompletionProvider implements monaco.languages.Compl provideCompletionItems(document: monaco.editor.ITextModel, position: monaco.Position, - token: monaco.CancellationToken, - context: monaco.languages.CompletionContext): monaco.languages.CompletionItem[] { - return this.items; + context: monaco.languages.CompletionContext, + token: monaco.CancellationToken): monaco.languages.CompletionList { + return { + suggestions: this.items + }; } } diff --git a/packages/monaco/src/typings/monaco/index.d.ts b/packages/monaco/src/typings/monaco/index.d.ts index f4de5a3e3704a..f70268c265a63 100644 --- a/packages/monaco/src/typings/monaco/index.d.ts +++ b/packages/monaco/src/typings/monaco/index.d.ts @@ -29,7 +29,7 @@ declare module monaco.editor { } export interface IBulkEditService { - apply(edit: monaco.languages.WorkspaceEdit): monaco.Promise; + apply(edit: monaco.languages.WorkspaceEdit): Promise; } export interface IDiffNavigator { @@ -48,6 +48,7 @@ declare module monaco.editor { setDecorationsFast(decorationTypeKey: string, ranges: IRange[]): void; } + // https://github.com/TypeFox/vscode/blob/monaco/0.18.0/src/vs/editor/browser/widget/codeEditorWidget.ts#L107 export interface CommonCodeEditor { readonly _commandService: monaco.commands.ICommandService; readonly _instantiationService: monaco.instantiation.IInstantiationService; @@ -55,7 +56,9 @@ declare module monaco.editor { 'editor.controller.quickOpenController': monaco.quickOpen.QuickOpenController 'editor.contrib.referencesController': monaco.referenceSearch.ReferencesController } - readonly cursor: ICursor; + readonly _modelData: { + cursor: ICursor + }; } export interface ICursor { @@ -106,7 +109,7 @@ declare module monaco.editor { export interface ICodeEditorService { getActiveCodeEditor(): monaco.editor.ICodeEditor | undefined; - openCodeEditor(input: monaco.editor.IResourceInput, source?: monaco.editor.ICodeEditor, sideBySide?: boolean): monaco.Promise; + openCodeEditor(input: monaco.editor.IResourceInput, source?: monaco.editor.ICodeEditor, sideBySide?: boolean): Promise; registerDecorationType(key: string, options: IDecorationRenderOptions, parentTypeKey?: string): void; removeDecorationType(key: string): void; resolveDecorationOptions(typeKey: string, writable: boolean): IModelDecorationOptions; @@ -121,7 +124,7 @@ declare module monaco.editor { * Provided a resource URI, it will return a model reference * which should be disposed once not needed anymore. */ - createModelReference(resource: monaco.Uri): monaco.Promise>; + createModelReference(resource: monaco.Uri): Promise>; /** * Registers a specific `scheme` content provider. @@ -133,7 +136,7 @@ declare module monaco.editor { /** * Given a resource, return the content of the resource as IModel. */ - provideTextContent(resource: monaco.Uri): monaco.Promise; + provideTextContent(resource: monaco.Uri): Promise; } export interface ITextEditorModel { @@ -141,7 +144,7 @@ declare module monaco.editor { /** * Loads the model. */ - load(): monaco.Promise; + load(): Promise; /** * Dispose associated resources @@ -162,7 +165,7 @@ declare module monaco.editor { /** * Returns the actions for the menu */ - getActions(): monaco.Promise + getActions(): ReadonlyArray; /** * Needs to be called with the context menu closes again. @@ -178,9 +181,10 @@ declare module monaco.editor { enabled: boolean; checked: boolean; radio: boolean; - run(event?: any): monaco.Promise; + run(event?: any): Promise; } + // https://github.com/TypeFox/vscode/blob/monaco/0.18.0/src/vs/platform/contextview/browser/contextView.ts#L38 export interface IContextMenuService { /** * Shows the native Monaco context menu in the editor. @@ -273,21 +277,14 @@ declare module monaco.commands { export interface ICommandService { readonly _onWillExecuteCommand: monaco.Emitter; - executeCommand(commandId: string, ...args: any[]): monaco.Promise; - executeCommand(commandId: string, ...args: any[]): monaco.Promise; + executeCommand(commandId: string, ...args: any[]): Promise; + executeCommand(commandId: string, ...args: any[]): Promise; } } declare module monaco.actions { - export class MenuId { - /** - * The unique ID of the editor's context menu. - */ - public static readonly EditorContext: MenuId; - } - export interface ICommandAction { id: string; title: string @@ -304,8 +301,9 @@ declare module monaco.actions { export interface IMenuRegistry { /** * Retrieves all the registered menu items for the given menu. + * @param menuId - see https://github.com/TypeFox/vscode/blob/monaco/0.18.0/src/vs/platform/actions/common/actions.ts#L66 */ - getMenuItems(menuId: MenuId | { id: string }): IMenuItem[]; + getMenuItems(menuId: 7 /* EditorContext */): IMenuItem[]; } /** @@ -326,18 +324,13 @@ declare module monaco.platform { declare module monaco.keybindings { + // https://github.com/TypeFox/vscode/blob/monaco/0.18.0/src/vs/platform/keybinding/common/keybindingResolver.ts#L20 export class KeybindingResolver { static contextMatchesRules(context: monaco.contextKeyService.IContext, rules: monaco.contextkey.ContextKeyExpr): boolean; } - export const enum KeybindingType { - Simple = 1, - Chord = 2 - } - + // https://github.com/TypeFox/vscode/blob/monaco/0.18.0/src/vs/base/common/keyCodes.ts#L443 export class SimpleKeybinding { - public readonly type: KeybindingType; - public readonly ctrlKey: boolean; public readonly shiftKey: boolean; public readonly altKey: boolean; @@ -347,13 +340,9 @@ declare module monaco.keybindings { constructor(ctrlKey: boolean, shiftKey: boolean, altKey: boolean, metaKey: boolean, keyCode: KeyCode); } + // https://github.com/TypeFox/vscode/blob/monaco/0.18.0/src/vs/base/common/keyCodes.ts#L503 export class ChordKeybinding { - public readonly type: KeybindingType; - - public readonly firstPart: SimpleKeybinding; - public readonly chordPart: SimpleKeybinding; - - constructor(firstPart: SimpleKeybinding, chordPart: SimpleKeybinding); + readonly parts: SimpleKeybinding[]; } export type Keybinding = SimpleKeybinding | ChordKeybinding; @@ -365,20 +354,9 @@ declare module monaco.keybindings { } export interface ContextKeyExpr { - getType(): ContextKeyExprType; - keys(): string[]; serialize(): string; } - export enum ContextKeyExprType { - Defined = 1, - Not = 2, - Equals = 3, - NotEquals = 4, - And = 5, - Regex = 6 - } - export interface IKeybindingsRegistry { /** * Returns with all the default, static keybindings. @@ -386,73 +364,36 @@ declare module monaco.keybindings { getDefaultKeybindings(): IKeybindingItem[]; } + // https://github.com/TypeFox/vscode/blob/monaco/0.18.0/src/vs/platform/keybinding/common/keybindingsRegistry.ts#L75 export const KeybindingsRegistry: IKeybindingsRegistry; - export namespace KeyCodeUtils { - export function toString(key: any): string; - } - + // https://github.com/TypeFox/vscode/blob/monaco/0.18.0/src/vs/base/common/keyCodes.ts#L542 export class ResolvedKeybindingPart { readonly ctrlKey: boolean; readonly shiftKey: boolean; readonly altKey: boolean; readonly metaKey: boolean; - readonly keyLabel: string; - readonly keyAriaLabel: string; + readonly keyLabel: string | null; + readonly keyAriaLabel: string | null; - constructor(ctrlKey: boolean, shiftKey: boolean, altKey: boolean, metaKey: boolean, kbLabel: string, kbAriaLabel: string); + constructor(ctrlKey: boolean, shiftKey: boolean, altKey: boolean, metaKey: boolean, kbLabel: string | null, kbAriaLabel: string | null); } + // https://github.com/TypeFox/vscode/blob/monaco/0.18.0/src/vs/base/common/keyCodes.ts#L564 export abstract class ResolvedKeybinding { - /** - * This prints the binding in a format suitable for displaying in the UI. - */ - public abstract getLabel(): string; - /** - * This prints the binding in a format suitable for ARIA. - */ - public abstract getAriaLabel(): string; - /** - * This prints the binding in a format suitable for electron's accelerators. - * See https://github.com/electron/electron/blob/master/docs/api/accelerator.md - */ - public abstract getElectronAccelerator(): string; - /** - * This prints the binding in a format suitable for user settings. - */ - public abstract getUserSettingsLabel(): string; - /** - * Is the user settings label reflecting the label? - */ + public abstract getLabel(): string | null; + public abstract getAriaLabel(): string | null; + public abstract getElectronAccelerator(): string | null; + public abstract getUserSettingsLabel(): string | null; public abstract isWYSIWYG(): boolean; - /** - * Is the binding a chord? - */ public abstract isChord(): boolean; - /** - * Returns the firstPart, chordPart that should be used for dispatching. - */ - public abstract getDispatchParts(): [string | null, string | null]; - /** - * Returns the firstPart, chordPart of the keybinding. - * For simple keybindings, the second element will be null. - */ - public abstract getParts(): [ResolvedKeybindingPart | null, ResolvedKeybindingPart | null]; + public abstract getParts(): ResolvedKeybindingPart[]; + public abstract getDispatchParts(): (string | null)[]; } - export class USLayoutResolvedKeybinding extends ResolvedKeybinding { - constructor(actual: Keybinding, OS: monaco.platform.OperatingSystem); - - public getLabel(): string; - public getAriaLabel(): string; - public getElectronAccelerator(): string; - public getUserSettingsLabel(): string; - public isWYSIWYG(): boolean; - public isChord(): boolean; - public getDispatchParts(): [string, string]; - public getParts(): [ResolvedKeybindingPart, ResolvedKeybindingPart]; - + // https://github.com/TypeFox/vscode/blob/monaco/0.18.0/src/vs/platform/keybinding/common/usLayoutResolvedKeybinding.ts#L13 + export class USLayoutResolvedKeybinding { public static getDispatchStr(keybinding: SimpleKeybinding): string; } @@ -463,23 +404,13 @@ declare module monaco.keybindings { readonly metaKey: boolean; } - export interface ModifierLabels { - readonly ctrlKey: string; - readonly shiftKey: string; - readonly altKey: string; - readonly metaKey: string; - readonly separator: string; + export interface KeyLabelProvider { + (keybinding: T): string | null; } - export class ModifierLabelProvider { - - public readonly modifierLabels: ModifierLabels[]; - - constructor(mac: ModifierLabels, windows: ModifierLabels, linux?: ModifierLabels); - - public toLabel(firstPartMod: Modifiers | null, firstPartKey: string | null, - chordPartMod: Modifiers | null, chordPartKey: string | null, - OS: monaco.platform.OperatingSystem): string; + // https://github.com/TypeFox/vscode/blob/monaco/0.18.0/src/vs/base/common/keybindingLabels.ts#L28 + export interface ModifierLabelProvider { + toLabel(OS: monaco.platform.OperatingSystem, parts: T[], keyLabelProvider: KeyLabelProvider): string | null; } export const UILabelProvider: ModifierLabelProvider; @@ -498,12 +429,9 @@ declare module monaco.services { getValue(section: string, overrides: any, workspace: any): any; } - export enum ConfigurationTarget { - DEFAULT - } - export class ConfigurationChangeEvent { - _source?: ConfigurationTarget; + // https://github.com/TypeFox/vscode/blob/monaco/0.18.0/src/vs/platform/configuration/common/configuration.ts#L30-L38 + _source?: number; change(keys: string[]): ConfigurationChangeEvent; } @@ -516,7 +444,7 @@ declare module monaco.services { constructor(themeService: IStandaloneThemeService); abstract getActiveCodeEditor(): monaco.editor.ICodeEditor | undefined; abstract openCodeEditor(input: monaco.editor.IResourceInput, source?: monaco.editor.ICodeEditor, - sideBySide?: boolean): monaco.Promise; + sideBySide?: boolean): Promise; registerDecorationType: monaco.editor.ICodeEditorService['registerDecorationType']; removeDecorationType: monaco.editor.ICodeEditorService['removeDecorationType']; resolveDecorationOptions: monaco.editor.ICodeEditorService['resolveDecorationOptions']; @@ -525,8 +453,8 @@ declare module monaco.services { export class StandaloneCommandService implements monaco.commands.ICommandService { constructor(instantiationService: monaco.instantiation.IInstantiationService); readonly _onWillExecuteCommand: monaco.Emitter; - executeCommand(commandId: string, ...args: any[]): monaco.Promise; - executeCommand(commandId: string, ...args: any[]): monaco.Promise; + executeCommand(commandId: string, ...args: any[]): Promise; + executeCommand(commandId: string, ...args: any[]): Promise; } export class LazyStaticService { @@ -576,7 +504,7 @@ declare module monaco.services { } export interface IModeService { - getOrCreateModeByFilenameOrFirstLine(filename: string, firstLine?: string): monaco.Promise; + getOrCreateModeByFilenameOrFirstLine(filename: string, firstLine?: string): Promise; } export interface IMode { @@ -734,11 +662,8 @@ declare module monaco.quickOpen { */ autoFocusPrefixMatch?: string; } - export enum Mode { - PREVIEW, - OPEN, - OPEN_IN_BACKGROUND - } + // https://github.com/TypeFox/vscode/blob/monaco/0.18.0/src/vs/base/parts/quickopen/common/quickOpen.ts#L43-L48 + export type Mode = 0 /* PREVIEW */ | 1 /* OPEN */ | 2 /* OPEN_IN_BACKGROUND */; export interface IEntryRunContext { event: any; keymods: number[]; @@ -827,9 +752,9 @@ declare module monaco.quickOpen { export interface IActionProvider { hasActions(element: any, item: any): boolean; - getActions(element: any, item: any): monaco.Promise; + getActions(element: any, item: any): Promise; hasSecondaryActions(element: any, item: any): boolean; - getSecondaryActions(element: any, item: any): monaco.Promise; + getSecondaryActions(element: any, item: any): Promise; getActionItem(element: any, item: any, action: IAction): any; } @@ -899,80 +824,6 @@ declare module monaco.modes { public static getInlineStyleFromMetadata(metadata: number, colorMap: string[]): string; } - export type SuggestionType = 'method' - | 'function' - | 'constructor' - | 'field' - | 'variable' - | 'class' - | 'struct' - | 'interface' - | 'module' - | 'property' - | 'event' - | 'operator' - | 'unit' - | 'value' - | 'constant' - | 'enum' - | 'enum-member' - | 'keyword' - | 'snippet' - | 'text' - | 'color' - | 'file' - | 'reference' - | 'customcolor' - | 'folder' - | 'type-parameter'; - - export type SnippetType = 'internal' | 'textmate'; - - export interface ISuggestion { - label: string; - insertText: string; - type: SuggestionType; - detail?: string; - documentation?: string | IMarkdownString; - filterText?: string; - sortText?: string; - preselect?: boolean; - noAutoAccept?: boolean; - commitCharacters?: string[]; - overwriteBefore?: number; - overwriteAfter?: number; - additionalTextEdits?: editor.ISingleEditOperation[]; - command?: monaco.languages.Command; - snippetType?: SnippetType; - } - - export interface ISuggestResult { - suggestions: ISuggestion[]; - incomplete?: boolean; - dispose?(): void; - } - - export enum CompletionTriggerKind { - Invoke = 0, - TriggerCharacter = 1, - TriggerForIncompleteCompletions = 2, - } - - export interface SuggestContext { - triggerKind: CompletionTriggerKind; - triggerCharacter?: string; - } - - export interface ISuggestSupport { - - triggerCharacters?: string[]; - - // tslint:disable-next-line:max-line-length - provideCompletionItems(model: monaco.editor.ITextModel, position: Position, context: SuggestContext, token: CancellationToken): ISuggestResult | Thenable | undefined; - - resolveCompletionItem?(model: monaco.editor.ITextModel, position: Position, item: ISuggestion, token: CancellationToken): ISuggestion | Thenable; - } - export interface IRelativePattern { base: string; pattern: string; @@ -1000,85 +851,38 @@ declare module monaco.modes { export const DocumentSymbolProviderRegistry: LanguageFeatureRegistry; - export const SuggestRegistry: LanguageFeatureRegistry; + export const CompletionProviderRegistry: LanguageFeatureRegistry; } declare module monaco.suggest { - export type SnippetConfig = 'top' | 'bottom' | 'inline' | 'none'; - - export interface ISuggestionItem { - suggestion: monaco.modes.ISuggestion; - } - - export function provideSuggestionItems( - model: monaco.editor.ITextModel, - position: Position, - snippetConfig?: SnippetConfig, - onlyFrom?: monaco.modes.ISuggestSupport[], - context?: monaco.modes.SuggestContext, - token?: monaco.CancellationToken - ): Promise; - - export function setSnippetSuggestSupport(support: monaco.modes.ISuggestSupport): monaco.modes.ISuggestSupport; - -} - -declare module monaco.suggestController { - - export class SuggestWidget { - suggestWidgetVisible: { - get(): boolean; - }; + export const enum SnippetSortOrder { + Top, Inline, Bottom } - export class SuggestController { - - getId(): string; - dispose(): void; - - /** - * This is a hack. The widget has a `private` visibility in the VSCode source. - */ - readonly _widget: SuggestWidget | undefined; - + export interface CompletionItem { + completion: monaco.languages.CompletionItem; } -} - -declare module monaco.findController { - - export class CommonFindController { - - getId(): string; - dispose(): void; + export class CompletionOptions { - /** - * Hack for checking whether the find (and replace) widget is visible in code editor or not. - */ - readonly _findWidgetVisible: { - get(): boolean; - }; + constructor( + snippetSortOrder?: SnippetSortOrder, + kindFilter?: Set, + providerFilter?: Set, + ); } -} - -declare module monaco.rename { - - export class RenameController { - - getId(): string; - dispose(): void; - - /** - * Hack for checking whether the rename input HTML element is visible in the code editor or not. In VSCode source this is has `private` visibility. - */ - readonly _renameInputVisible: { - get(): boolean; - }; + export function provideSuggestionItems( + model: monaco.editor.ITextModel, + position: Position, + options?: CompletionOptions, + context?: monaco.languages.CompletionContext, + token?: monaco.CancellationToken + ): Promise; - } + export function setSnippetSuggestSupport(support: monaco.languages.CompletionItemProvider): monaco.languages.CompletionItemProvider; } @@ -1134,7 +938,7 @@ declare module monaco.mime { readonly userConfigured?: boolean; } - export function registerTextMime(association: monaco.mime.ITextMimeAssociation, warnOnOverwrite: boolean): void + export function registerTextMime(association: monaco.mime.ITextMimeAssociation, warnOnOverwrite: boolean): void; export function clearTextMimes(onlyUserConfigured?: boolean): void; } diff --git a/packages/plugin-ext/src/common/plugin-api-rpc-model.ts b/packages/plugin-ext/src/common/plugin-api-rpc-model.ts index 4938d87486706..c8bd3d071c0f5 100644 --- a/packages/plugin-ext/src/common/plugin-api-rpc-model.ts +++ b/packages/plugin-ext/src/common/plugin-api-rpc-model.ts @@ -71,6 +71,9 @@ export interface Range { export interface MarkdownString { value: string; isTrusted?: boolean; + uris?: { + [href: string]: UriComponents; + }; } export interface SerializedDocumentFilter { @@ -118,26 +121,30 @@ export interface CompletionContext { triggerCharacter?: string; } +export enum CompletionItemInsertTextRule { + KeepWhitespace = 1, + InsertAsSnippet = 4 +} + export interface Completion { label: string; - insertText: string; - type: CompletionType; + kind: CompletionItemKind; detail?: string; documentation?: string | MarkdownString; - filterText?: string; sortText?: string; + filterText?: string; preselect?: boolean; - noAutoAccept?: boolean; + insertText: string; + insertTextRules?: CompletionItemInsertTextRule; + range: Range; commitCharacters?: string[]; - overwriteBefore?: number; - overwriteAfter?: number; additionalTextEdits?: SingleEditOperation[]; command?: Command; - snippetType?: SnippetType; } + export interface SingleEditOperation { range: Range; - text: string; + text: string | null; /** * This indicates that this operation has "insert" semantics. * i.e. forceMoveMarkers = true => if `range` is collapsed, all markers at the position will be moved. @@ -145,8 +152,6 @@ export interface SingleEditOperation { forceMoveMarkers?: boolean; } -export type SnippetType = 'internal' | 'textmate'; - export interface Command { id: string; title: string; @@ -155,32 +160,34 @@ export interface Command { arguments?: any[]; } -export type CompletionType = 'method' - | 'function' - | 'constructor' - | 'field' - | 'variable' - | 'class' - | 'struct' - | 'interface' - | 'module' - | 'property' - | 'event' - | 'operator' - | 'unit' - | 'value' - | 'constant' - | 'enum' - | 'enum-member' - | 'keyword' - | 'snippet' - | 'text' - | 'color' - | 'file' - | 'reference' - | 'customcolor' - | 'folder' - | 'type-parameter'; +export enum CompletionItemKind { + Method = 0, + Function = 1, + Constructor = 2, + Field = 3, + Variable = 4, + Class = 5, + Struct = 6, + Interface = 7, + Module = 8, + Property = 9, + Event = 10, + Operator = 11, + Unit = 12, + Value = 13, + Constant = 14, + Enum = 15, + EnumMember = 16, + Keyword = 17, + Text = 18, + Color = 19, + File = 20, + Reference = 21, + Customcolor = 22, + Folder = 23, + TypeParameter = 24, + Snippet = 25 +} export class IdObject { id?: number; @@ -312,7 +319,8 @@ export interface ReferenceContext { export interface DocumentLink { range: Range; - url?: string; + url?: UriComponents | string; + tooltip?: string; } export interface DocumentLinkProvider { diff --git a/packages/plugin-ext/src/common/plugin-api-rpc.ts b/packages/plugin-ext/src/common/plugin-api-rpc.ts index 558d0fda7faa8..3639fcd19b53a 100644 --- a/packages/plugin-ext/src/common/plugin-api-rpc.ts +++ b/packages/plugin-ext/src/common/plugin-api-rpc.ts @@ -43,7 +43,6 @@ import { Hover, DocumentHighlight, FormattingOptions, - SingleEditOperation as ModelSingleEditOperation, Definition, DefinitionLink, DocumentLink, @@ -1089,9 +1088,9 @@ export interface LanguagesExt { $provideHover(handle: number, resource: UriComponents, position: Position, token: CancellationToken): Promise; $provideDocumentHighlights(handle: number, resource: UriComponents, position: Position, token: CancellationToken): Promise; $provideDocumentFormattingEdits(handle: number, resource: UriComponents, - options: FormattingOptions, token: CancellationToken): Promise; + options: FormattingOptions, token: CancellationToken): Promise; $provideDocumentRangeFormattingEdits(handle: number, resource: UriComponents, range: Range, - options: FormattingOptions, token: CancellationToken): Promise; + options: FormattingOptions, token: CancellationToken): Promise; $provideOnTypeFormattingEdits( handle: number, resource: UriComponents, @@ -1099,7 +1098,7 @@ export interface LanguagesExt { ch: string, options: FormattingOptions, token: CancellationToken - ): Promise; + ): Promise; $provideDocumentLinks(handle: number, resource: UriComponents, token: CancellationToken): Promise; $resolveDocumentLink(handle: number, link: DocumentLink, token: CancellationToken): Promise; $provideCodeLenses(handle: number, resource: UriComponents, token: CancellationToken): Promise; diff --git a/packages/plugin-ext/src/main/browser/editors-and-documents-main.ts b/packages/plugin-ext/src/main/browser/editors-and-documents-main.ts index df5dc96425a24..b04bb0f4c6693 100644 --- a/packages/plugin-ext/src/main/browser/editors-and-documents-main.ts +++ b/packages/plugin-ext/src/main/browser/editors-and-documents-main.ts @@ -94,7 +94,7 @@ export class EditorsAndDocumentsMain { const removedDocuments = delta.removedDocuments.map(d => d.textEditorModel.uri); for (const editor of delta.addedEditors) { - const textEditorMain = new TextEditorMain(editor.id, editor.editor.getControl().getModel(), editor.editor); + const textEditorMain = new TextEditorMain(editor.id, editor.editor.getControl().getModel()!, editor.editor); this.textEditors.set(editor.id, textEditorMain); addedEditors.push(textEditorMain); } @@ -339,7 +339,7 @@ class EditorAndDocumentState { class EditorSnapshot { readonly id: string; constructor(readonly editor: MonacoEditor) { - this.id = `${editor.getControl().getId()},${editor.getControl().getModel().id}`; + this.id = `${editor.getControl().getId()},${editor.getControl().getModel()!.id}`; } } diff --git a/packages/plugin-ext/src/main/browser/languages-main.ts b/packages/plugin-ext/src/main/browser/languages-main.ts index 6fc0e487cb800..7ea6a13be1a97 100644 --- a/packages/plugin-ext/src/main/browser/languages-main.ts +++ b/packages/plugin-ext/src/main/browser/languages-main.ts @@ -35,7 +35,7 @@ import { ResourceFileEditDto, } from '../../common/plugin-api-rpc'; import { interfaces } from 'inversify'; -import { SerializedDocumentFilter, MarkerData, Range, WorkspaceSymbolProvider, RelatedInformation, MarkerSeverity } from '../../common/plugin-api-rpc-model'; +import { SerializedDocumentFilter, MarkerData, Range, WorkspaceSymbolProvider, RelatedInformation, MarkerSeverity, DocumentLink } from '../../common/plugin-api-rpc-model'; import { RPCProtocol } from '../../common/rpc-protocol'; import { fromLanguageSelector } from '../../plugin/type-converters'; import { LanguageSelector } from '../../plugin/languages'; @@ -99,12 +99,12 @@ export class LanguagesMainImpl implements LanguagesMain { } $registerCompletionSupport(handle: number, selector: SerializedDocumentFilter[], triggerCharacters: string[], supportsResolveDetails: boolean): void { - this.disposables.set(handle, monaco.modes.SuggestRegistry.register(fromLanguageSelector(selector)!, { + this.disposables.set(handle, monaco.modes.CompletionProviderRegistry.register(fromLanguageSelector(selector)!, { triggerCharacters, provideCompletionItems: (model: monaco.editor.ITextModel, position: monaco.Position, - context: monaco.modes.SuggestContext, - token: monaco.CancellationToken): Thenable => + context: monaco.languages.CompletionContext, + token: monaco.CancellationToken): Thenable => Promise.resolve(this.proxy.$provideCompletionItems(handle, model.uri, position, context, token)).then(result => { if (!result) { return undefined!; @@ -221,7 +221,7 @@ export class LanguagesMainImpl implements LanguagesMain { if (Array.isArray(result)) { // using DefinitionLink because Location is mandatory part of DefinitionLink - const definitionLinks: monaco.languages.DefinitionLink[] = []; + const definitionLinks: monaco.languages.LocationLink[] = []; for (const item of result) { definitionLinks.push({ ...item, uri: monaco.Uri.revive(item.uri) }); } @@ -263,7 +263,7 @@ export class LanguagesMainImpl implements LanguagesMain { if (Array.isArray(result)) { // using DefinitionLink because Location is mandatory part of DefinitionLink - const definitionLinks: monaco.languages.DefinitionLink[] = []; + const definitionLinks: monaco.languages.LocationLink[] = []; for (const item of result) { definitionLinks.push({ ...item, uri: monaco.Uri.revive(item.uri) }); } @@ -373,14 +373,32 @@ export class LanguagesMainImpl implements LanguagesMain { protected createLinkProvider(handle: number, selector: LanguageSelector | undefined): monaco.languages.LinkProvider { return { - provideLinks: (model, token) => { + provideLinks: async (model, token) => { if (!this.matchModel(selector, MonacoModelIdentifier.fromModel(model))) { return undefined!; } - return this.proxy.$provideDocumentLinks(handle, model.uri, token).then(v => v!); + const links = await this.proxy.$provideDocumentLinks(handle, model.uri, token); + if (!links) { + return undefined; + } + return { + links: links.map(link => this.toMonacoLink(link)), + dispose: () => { + // TODO this.proxy.$releaseDocumentLinks(handle, links.cacheId); + } + }; }, - resolveLink: (link: monaco.languages.ILink, token) => - this.proxy.$resolveDocumentLink(handle, link, token).then(v => v!) + resolveLink: async (link, token) => { + const resolved = await this.proxy.$resolveDocumentLink(handle, link, token); + return resolved && this.toMonacoLink(resolved); + } + }; + } + + protected toMonacoLink(link: DocumentLink): monaco.languages.ILink { + return { + ...link, + url: !!link.url && typeof link.url !== 'string' ? monaco.Uri.revive(link.url) : link.url }; } @@ -405,9 +423,18 @@ export class LanguagesMainImpl implements LanguagesMain { protected createCodeLensProvider(handle: number, selector: LanguageSelector | undefined): monaco.languages.CodeLensProvider { return { - provideCodeLenses: (model, token) => - this.proxy.$provideCodeLenses(handle, model.uri, token).then(v => v!) - , + provideCodeLenses: async (model, token) => { + if (!this.matchModel(selector, MonacoModelIdentifier.fromModel(model))) { + return undefined!; + } + const lenses = await this.proxy.$provideCodeLenses(handle, model.uri, token); + return { + lenses: lenses!, + dispose: () => { + // TODO this.proxy.$releaseCodeLenses + } + }; + }, resolveCodeLens: (model, codeLens, token) => this.proxy.$resolveCodeLens(handle, model.uri, codeLens, token).then(v => v!) }; @@ -454,7 +481,7 @@ export class LanguagesMainImpl implements LanguagesMain { if (Array.isArray(result)) { // using DefinitionLink because Location is mandatory part of DefinitionLink - const definitionLinks: monaco.languages.DefinitionLink[] = []; + const definitionLinks: monaco.languages.LocationLink[] = []; for (const item of result) { definitionLinks.push({ ...item, uri: monaco.Uri.revive(item.uri) }); } @@ -474,11 +501,17 @@ export class LanguagesMainImpl implements LanguagesMain { protected createSignatureHelpProvider(handle: number, selector: LanguageSelector | undefined, triggerCharacters: string[]): monaco.languages.SignatureHelpProvider { return { signatureHelpTriggerCharacters: triggerCharacters, - provideSignatureHelp: (model, position, token) => { + provideSignatureHelp: async (model, position, token) => { if (!this.matchModel(selector, MonacoModelIdentifier.fromModel(model))) { return undefined!; } - return this.proxy.$provideSignatureHelp(handle, model.uri, position, token).then(v => v!); + const value = await this.proxy.$provideSignatureHelp(handle, model.uri, position, token); + return { + value: value!, + dispose: () => { + // TDOO ? + } + }; } }; } @@ -642,11 +675,17 @@ export class LanguagesMainImpl implements LanguagesMain { protected createQuickFixProvider(handle: number, selector: LanguageSelector | undefined, providedCodeActionKinds?: string[]): monaco.languages.CodeActionProvider { return { - provideCodeActions: (model, rangeOrSelection, monacoContext, token) => { + provideCodeActions: async (model, rangeOrSelection, monacoContext, token) => { if (!this.matchModel(selector, MonacoModelIdentifier.fromModel(model))) { return undefined!; } - return this.proxy.$provideCodeActions(handle, model.uri, rangeOrSelection, monacoContext, token); + const actions = await this.proxy.$provideCodeActions(handle, model.uri, rangeOrSelection, monacoContext, token); + return { + actions, + dispose: () => { + // TODO this.proxy.$releaseCodeActions(handle, cacheId); + } + }; } }; } diff --git a/packages/plugin-ext/src/main/browser/text-editor-main.ts b/packages/plugin-ext/src/main/browser/text-editor-main.ts index 05da61af2114f..f0e4afb45c3e7 100644 --- a/packages/plugin-ext/src/main/browser/text-editor-main.ts +++ b/packages/plugin-ext/src/main/browser/text-editor-main.ts @@ -270,7 +270,7 @@ export class TextEditorMain { if (!this.editor) { return; } - this.editor.getControl().setDecorations(key, ranges); + this.editor.getControl().setDecorations(key, ranges.map(option => Object.assign(option, { color: undefined }))); } setDecorationsFast(key: string, _ranges: number[]): void { @@ -349,7 +349,7 @@ export class TextEditorPropertiesMain { private static getSelectionsFromEditor(prevProperties: TextEditorPropertiesMain | undefined, editor: MonacoEditor): monaco.Selection[] { let result: monaco.Selection[] | undefined = undefined; if (editor) { - result = editor.getControl().getSelections(); + result = editor.getControl().getSelections() || undefined; } if (!result && prevProperties) { diff --git a/packages/plugin-ext/src/plugin/languages.ts b/packages/plugin-ext/src/plugin/languages.ts index 42bc2b79487a5..da7e416332b83 100644 --- a/packages/plugin-ext/src/plugin/languages.ts +++ b/packages/plugin-ext/src/plugin/languages.ts @@ -44,7 +44,7 @@ import { Hover, DocumentHighlight, Range, - SingleEditOperation, + TextEdit, FormattingOptions, Definition, DefinitionLink, @@ -337,7 +337,7 @@ export class LanguagesExtImpl implements LanguagesExt { } $provideDocumentFormattingEdits(handle: number, resource: UriComponents, - options: FormattingOptions, token: theia.CancellationToken): Promise { + options: FormattingOptions, token: theia.CancellationToken): Promise { return this.withAdapter(handle, DocumentFormattingAdapter, adapter => adapter.provideDocumentFormattingEdits(URI.revive(resource), options, token)); } // ### Document Formatting Edit end @@ -350,7 +350,7 @@ export class LanguagesExtImpl implements LanguagesExt { } $provideDocumentRangeFormattingEdits(handle: number, resource: UriComponents, range: Range, - options: FormattingOptions, token: theia.CancellationToken): Promise { + options: FormattingOptions, token: theia.CancellationToken): Promise { return this.withAdapter(handle, RangeFormattingAdapter, adapter => adapter.provideDocumentRangeFormattingEdits(URI.revive(resource), range, options, token)); } // ### Document Range Formatting Edit end @@ -367,7 +367,7 @@ export class LanguagesExtImpl implements LanguagesExt { } $provideOnTypeFormattingEdits(handle: number, resource: UriComponents, position: Position, ch: string, - options: FormattingOptions, token: theia.CancellationToken): Promise { + options: FormattingOptions, token: theia.CancellationToken): Promise { return this.withAdapter(handle, OnTypeFormattingAdapter, adapter => adapter.provideOnTypeFormattingEdits(URI.revive(resource), position, ch, options, token)); } // ### On Type Formatting Edit end diff --git a/packages/plugin-ext/src/plugin/languages/completion.ts b/packages/plugin-ext/src/plugin/languages/completion.ts index 6f83655dfb250..1c3872b05c837 100644 --- a/packages/plugin-ext/src/plugin/languages/completion.ts +++ b/packages/plugin-ext/src/plugin/languages/completion.ts @@ -21,7 +21,7 @@ import { DocumentsExtImpl } from '../documents'; import * as Converter from '../type-converters'; import { mixin } from '../../common/types'; import { Position } from '../../common/plugin-api-rpc'; -import { CompletionContext, CompletionResultDto, Completion, CompletionDto } from '../../common/plugin-api-rpc-model'; +import { CompletionContext, CompletionResultDto, Completion, CompletionDto, CompletionItemInsertTextRule } from '../../common/plugin-api-rpc-model'; export class CompletionAdapter { private cacheId = 0; @@ -113,50 +113,40 @@ export class CompletionAdapter { return undefined; } - const result: CompletionDto = { + const range = item.textEdit ? item.textEdit.range : item.range || defaultRange; + if (range && (!range.isSingleLine || range.start.line !== position.line)) { + console.warn('Invalid Completion Item -> must be single line and on the same line'); + return undefined; + } + + let insertText = item.label; + let insertTextRules = item.keepWhitespace ? CompletionItemInsertTextRule.KeepWhitespace : 0; + if (item.textEdit) { + insertText = item.textEdit.newText; + } else if (typeof item.insertText === 'string') { + insertText = item.insertText; + } else if (item.insertText instanceof SnippetString) { + insertText = item.insertText.value; + insertTextRules |= CompletionItemInsertTextRule.InsertAsSnippet; + } + + return { id, parentId, label: item.label, - type: Converter.fromCompletionItemKind(item.kind), + kind: Converter.fromCompletionItemKind(item.kind), detail: item.detail, documentation: item.documentation, filterText: item.filterText, sortText: item.sortText, preselect: item.preselect, - insertText: '', + insertText, + insertTextRules, + range: Converter.fromRange(range), additionalTextEdits: item.additionalTextEdits && item.additionalTextEdits.map(Converter.fromTextEdit), command: undefined, // TODO: implement this: this.commands.toInternal(item.command), commitCharacters: item.commitCharacters }; - - if (typeof item.insertText === 'string') { - result.insertText = item.insertText; - result.snippetType = 'internal'; - - } else if (item.insertText instanceof SnippetString) { - result.insertText = item.insertText.value; - result.snippetType = 'textmate'; - - } else { - result.insertText = item.label; - result.snippetType = 'internal'; - } - - let range: theia.Range; - if (item.range) { - range = item.range; - } else { - range = defaultRange; - } - result.overwriteBefore = position.character - range.start.character; - result.overwriteAfter = range.end.character - position.character; - - if (!range.isSingleLine || range.start.line !== position.line) { - console.warn('Invalid Completion Item -> must be single line and on the same line'); - return undefined; - } - - return result; } static hasResolveSupport(provider: theia.CompletionItemProvider): boolean { diff --git a/packages/plugin-ext/src/plugin/languages/document-formatting.ts b/packages/plugin-ext/src/plugin/languages/document-formatting.ts index 0e6f113f62bf3..49c7c2491eef6 100644 --- a/packages/plugin-ext/src/plugin/languages/document-formatting.ts +++ b/packages/plugin-ext/src/plugin/languages/document-formatting.ts @@ -18,7 +18,7 @@ import * as theia from '@theia/plugin'; import { DocumentsExtImpl } from '../documents'; import * as Converter from '../type-converters'; import URI from 'vscode-uri/lib/umd'; -import { FormattingOptions, SingleEditOperation } from '../../common/plugin-api-rpc-model'; +import { FormattingOptions, TextEdit } from '../../common/plugin-api-rpc-model'; export class DocumentFormattingAdapter { @@ -27,7 +27,7 @@ export class DocumentFormattingAdapter { private readonly documents: DocumentsExtImpl ) { } - provideDocumentFormattingEdits(resource: URI, options: FormattingOptions, token: theia.CancellationToken): Promise { + provideDocumentFormattingEdits(resource: URI, options: FormattingOptions, token: theia.CancellationToken): Promise { const document = this.documents.getDocumentData(resource); if (!document) { return Promise.reject(new Error(`There are no document for ${resource}`)); diff --git a/packages/plugin-ext/src/plugin/languages/on-type-formatting.ts b/packages/plugin-ext/src/plugin/languages/on-type-formatting.ts index f4c5f19ad5704..79f33a0a2ac80 100644 --- a/packages/plugin-ext/src/plugin/languages/on-type-formatting.ts +++ b/packages/plugin-ext/src/plugin/languages/on-type-formatting.ts @@ -18,7 +18,7 @@ import * as theia from '@theia/plugin'; import { DocumentsExtImpl } from '../documents'; import * as Converter from '../type-converters'; import URI from 'vscode-uri/lib/umd'; -import { FormattingOptions, SingleEditOperation } from '../../common/plugin-api-rpc-model'; +import { FormattingOptions, TextEdit } from '../../common/plugin-api-rpc-model'; import { Position } from '../../common/plugin-api-rpc'; export class OnTypeFormattingAdapter { @@ -29,7 +29,7 @@ export class OnTypeFormattingAdapter { ) { } provideOnTypeFormattingEdits(resource: URI, position: Position, ch: string, - options: FormattingOptions, token: theia.CancellationToken): Promise { + options: FormattingOptions, token: theia.CancellationToken): Promise { const document = this.documents.getDocumentData(resource); if (!document) { return Promise.reject(new Error(`There are no document for ${resource}`)); diff --git a/packages/plugin-ext/src/plugin/languages/range-formatting.ts b/packages/plugin-ext/src/plugin/languages/range-formatting.ts index acd53da03dcd8..8872faafac673 100644 --- a/packages/plugin-ext/src/plugin/languages/range-formatting.ts +++ b/packages/plugin-ext/src/plugin/languages/range-formatting.ts @@ -18,7 +18,7 @@ import * as theia from '@theia/plugin'; import { DocumentsExtImpl } from '../documents'; import * as Converter from '../type-converters'; import URI from 'vscode-uri/lib/umd'; -import { FormattingOptions, SingleEditOperation, Range } from '../../common/plugin-api-rpc-model'; +import { FormattingOptions, TextEdit, Range } from '../../common/plugin-api-rpc-model'; export class RangeFormattingAdapter { @@ -27,7 +27,7 @@ export class RangeFormattingAdapter { private readonly documents: DocumentsExtImpl ) { } - provideDocumentRangeFormattingEdits(resource: URI, range: Range, options: FormattingOptions, token: theia.CancellationToken): Promise { + provideDocumentRangeFormattingEdits(resource: URI, range: Range, options: FormattingOptions, token: theia.CancellationToken): Promise { const document = this.documents.getDocumentData(resource); if (!document) { return Promise.reject(new Error(`There are no document for ${resource}`)); diff --git a/packages/plugin-ext/src/plugin/type-converters.ts b/packages/plugin-ext/src/plugin/type-converters.ts index 8b3b3ec1ba8c4..4b7139e2ecdfd 100644 --- a/packages/plugin-ext/src/plugin/type-converters.ts +++ b/packages/plugin-ext/src/plugin/type-converters.ts @@ -121,6 +121,9 @@ export function toRange(range: model.Range): types.Range { return new types.Range(startLineNumber - 1, startColumn - 1, endLineNumber - 1, endColumn - 1); } +export function fromRange(range: undefined): undefined; +export function fromRange(range: theia.Range): model.Range; +export function fromRange(range: theia.Range | undefined): model.Range | undefined; export function fromRange(range: theia.Range | undefined): model.Range | undefined { if (!range) { return undefined; @@ -244,72 +247,70 @@ function isRelativePattern(obj: {}): obj is theia.RelativePattern { return rp && typeof rp.base === 'string' && typeof rp.pattern === 'string'; } -export function fromCompletionItemKind(kind?: types.CompletionItemKind): model.CompletionType { +export function fromCompletionItemKind(kind?: types.CompletionItemKind): model.CompletionItemKind { switch (kind) { - case types.CompletionItemKind.Method: return 'method'; - case types.CompletionItemKind.Function: return 'function'; - case types.CompletionItemKind.Constructor: return 'constructor'; - case types.CompletionItemKind.Field: return 'field'; - case types.CompletionItemKind.Variable: return 'variable'; - case types.CompletionItemKind.Class: return 'class'; - case types.CompletionItemKind.Interface: return 'interface'; - case types.CompletionItemKind.Struct: return 'struct'; - case types.CompletionItemKind.Module: return 'module'; - case types.CompletionItemKind.Property: return 'property'; - case types.CompletionItemKind.Unit: return 'unit'; - case types.CompletionItemKind.Value: return 'value'; - case types.CompletionItemKind.Constant: return 'constant'; - case types.CompletionItemKind.Enum: return 'enum'; - case types.CompletionItemKind.EnumMember: return 'enum-member'; - case types.CompletionItemKind.Keyword: return 'keyword'; - case types.CompletionItemKind.Snippet: return 'snippet'; - case types.CompletionItemKind.Text: return 'text'; - case types.CompletionItemKind.Color: return 'color'; - case types.CompletionItemKind.File: return 'file'; - case types.CompletionItemKind.Reference: return 'reference'; - case types.CompletionItemKind.Folder: return 'folder'; - case types.CompletionItemKind.Event: return 'event'; - case types.CompletionItemKind.Operator: return 'operator'; - case types.CompletionItemKind.TypeParameter: return 'type-parameter'; - } - return 'property'; -} - -export function toCompletionItemKind(type?: model.CompletionType): types.CompletionItemKind { - if (type) { - switch (type) { - case 'method': return types.CompletionItemKind.Method; - case 'function': return types.CompletionItemKind.Function; - case 'constructor': return types.CompletionItemKind.Constructor; - case 'field': return types.CompletionItemKind.Field; - case 'variable': return types.CompletionItemKind.Variable; - case 'class': return types.CompletionItemKind.Class; - case 'interface': return types.CompletionItemKind.Interface; - case 'struct': return types.CompletionItemKind.Struct; - case 'module': return types.CompletionItemKind.Module; - case 'property': return types.CompletionItemKind.Property; - case 'unit': return types.CompletionItemKind.Unit; - case 'value': return types.CompletionItemKind.Value; - case 'constant': return types.CompletionItemKind.Constant; - case 'enum': return types.CompletionItemKind.Enum; - case 'enum-member': return types.CompletionItemKind.EnumMember; - case 'keyword': return types.CompletionItemKind.Keyword; - case 'snippet': return types.CompletionItemKind.Snippet; - case 'text': return types.CompletionItemKind.Text; - case 'color': return types.CompletionItemKind.Color; - case 'file': return types.CompletionItemKind.File; - case 'reference': return types.CompletionItemKind.Reference; - case 'folder': return types.CompletionItemKind.Folder; - case 'event': return types.CompletionItemKind.Event; - case 'operator': return types.CompletionItemKind.Operator; - case 'type-parameter': return types.CompletionItemKind.TypeParameter; - } + case types.CompletionItemKind.Method: return model.CompletionItemKind.Method; + case types.CompletionItemKind.Function: return model.CompletionItemKind.Function; + case types.CompletionItemKind.Constructor: return model.CompletionItemKind.Constructor; + case types.CompletionItemKind.Field: return model.CompletionItemKind.Field; + case types.CompletionItemKind.Variable: return model.CompletionItemKind.Variable; + case types.CompletionItemKind.Class: return model.CompletionItemKind.Class; + case types.CompletionItemKind.Interface: return model.CompletionItemKind.Interface; + case types.CompletionItemKind.Struct: return model.CompletionItemKind.Struct; + case types.CompletionItemKind.Module: return model.CompletionItemKind.Module; + case types.CompletionItemKind.Property: return model.CompletionItemKind.Property; + case types.CompletionItemKind.Unit: return model.CompletionItemKind.Unit; + case types.CompletionItemKind.Value: return model.CompletionItemKind.Value; + case types.CompletionItemKind.Constant: return model.CompletionItemKind.Constant; + case types.CompletionItemKind.Enum: return model.CompletionItemKind.Enum; + case types.CompletionItemKind.EnumMember: return model.CompletionItemKind.EnumMember; + case types.CompletionItemKind.Keyword: return model.CompletionItemKind.Keyword; + case types.CompletionItemKind.Snippet: return model.CompletionItemKind.Snippet; + case types.CompletionItemKind.Text: return model.CompletionItemKind.Text; + case types.CompletionItemKind.Color: return model.CompletionItemKind.Color; + case types.CompletionItemKind.File: return model.CompletionItemKind.File; + case types.CompletionItemKind.Reference: return model.CompletionItemKind.Reference; + case types.CompletionItemKind.Folder: return model.CompletionItemKind.Folder; + case types.CompletionItemKind.Event: return model.CompletionItemKind.Event; + case types.CompletionItemKind.Operator: return model.CompletionItemKind.Operator; + case types.CompletionItemKind.TypeParameter: return model.CompletionItemKind.TypeParameter; + } + return model.CompletionItemKind.Property; +} + +export function toCompletionItemKind(kind?: model.CompletionItemKind): types.CompletionItemKind { + switch (kind) { + case model.CompletionItemKind.Method: return types.CompletionItemKind.Method; + case model.CompletionItemKind.Function: return types.CompletionItemKind.Function; + case model.CompletionItemKind.Constructor: return types.CompletionItemKind.Constructor; + case model.CompletionItemKind.Field: return types.CompletionItemKind.Field; + case model.CompletionItemKind.Variable: return types.CompletionItemKind.Variable; + case model.CompletionItemKind.Class: return types.CompletionItemKind.Class; + case model.CompletionItemKind.Interface: return types.CompletionItemKind.Interface; + case model.CompletionItemKind.Struct: return types.CompletionItemKind.Struct; + case model.CompletionItemKind.Module: return types.CompletionItemKind.Module; + case model.CompletionItemKind.Property: return types.CompletionItemKind.Property; + case model.CompletionItemKind.Unit: return types.CompletionItemKind.Unit; + case model.CompletionItemKind.Value: return types.CompletionItemKind.Value; + case model.CompletionItemKind.Constant: return types.CompletionItemKind.Constant; + case model.CompletionItemKind.Enum: return types.CompletionItemKind.Enum; + case model.CompletionItemKind.EnumMember: return types.CompletionItemKind.EnumMember; + case model.CompletionItemKind.Keyword: return types.CompletionItemKind.Keyword; + case model.CompletionItemKind.Snippet: return types.CompletionItemKind.Snippet; + case model.CompletionItemKind.Text: return types.CompletionItemKind.Text; + case model.CompletionItemKind.Color: return types.CompletionItemKind.Color; + case model.CompletionItemKind.File: return types.CompletionItemKind.File; + case model.CompletionItemKind.Reference: return types.CompletionItemKind.Reference; + case model.CompletionItemKind.Folder: return types.CompletionItemKind.Folder; + case model.CompletionItemKind.Event: return types.CompletionItemKind.Event; + case model.CompletionItemKind.Operator: return types.CompletionItemKind.Operator; + case model.CompletionItemKind.TypeParameter: return types.CompletionItemKind.TypeParameter; } return types.CompletionItemKind.Property; } -export function fromTextEdit(edit: theia.TextEdit): model.SingleEditOperation { - return { +export function fromTextEdit(edit: theia.TextEdit): model.TextEdit { + return { text: edit.newText, range: fromRange(edit.range) }; diff --git a/packages/plugin/src/theia.d.ts b/packages/plugin/src/theia.d.ts index d6037cfebaa5e..e0f5ec7926927 100644 --- a/packages/plugin/src/theia.d.ts +++ b/packages/plugin/src/theia.d.ts @@ -5649,6 +5649,13 @@ declare module '@theia/plugin' { */ commitCharacters?: string[]; + /** + * Keep whitespace of the [insertText](#CompletionItem.insertText) as is. By default, the editor adjusts leading + * whitespace of new lines so that they match the indentation of the line for which the item is accepted - setting + * this to `true` will prevent that. + */ + keepWhitespace?: boolean; + /** * An optional array of additional [text edits](#TextEdit) that are applied when * selecting this completion. Edits must not overlap with the main [edit](#CompletionItem.textEdit) @@ -8330,17 +8337,17 @@ declare module '@theia/plugin' { /** * Comment mode of a [comment](#Comment) */ - export enum CommentMode { + export enum CommentMode { /** - * Displays the comment editor - */ - Editing = 0, + * Displays the comment editor + */ + Editing = 0, /** - * Displays the preview of the comment - */ - Preview = 1 - } + * Displays the preview of the comment + */ + Preview = 1 + } /** * A collection of [comments](#Comment) representing a conversation at a particular range in a document. @@ -8534,78 +8541,78 @@ declare module '@theia/plugin' { /** * Author information of a [comment](#Comment) */ - export interface CommentAuthorInformation { + export interface CommentAuthorInformation { /** - * The display name of the author of the comment - */ - name: string; + * The display name of the author of the comment + */ + name: string; /** - * The optional icon path for the author - */ - iconPath?: Uri; - } + * The optional icon path for the author + */ + iconPath?: Uri; + } /** * Reactions of a [comment](#Comment) */ - export interface CommentReaction { + export interface CommentReaction { /** - * The human-readable label for the reaction - */ - readonly label: string; + * The human-readable label for the reaction + */ + readonly label: string; /** - * Icon for the reaction shown in UI. - */ - readonly iconPath: string | Uri; + * Icon for the reaction shown in UI. + */ + readonly iconPath: string | Uri; /** - * The number of users who have reacted to this reaction - */ - readonly count: number; + * The number of users who have reacted to this reaction + */ + readonly count: number; /** - * Whether the [author](CommentAuthorInformation) of the comment has reacted to this reaction - */ - readonly authorHasReacted: boolean; - } + * Whether the [author](CommentAuthorInformation) of the comment has reacted to this reaction + */ + readonly authorHasReacted: boolean; + } /** * A comment is displayed within the editor or the Comments Panel, depending on how it is provided. */ export interface Comment { /** - * The human-readable comment body - */ + * The human-readable comment body + */ body: string | MarkdownString; /** - * [Comment mode](#CommentMode) of the comment - */ + * [Comment mode](#CommentMode) of the comment + */ mode: CommentMode; /** - * The [author information](#CommentAuthorInformation) of the comment - */ + * The [author information](#CommentAuthorInformation) of the comment + */ author: CommentAuthorInformation; /** - * Context value of the comment. This can be used to contribute comment specific actions. - * For example, a comment is given a context value as `editable`. When contributing actions to `comments/comment/title` - * using `menus` extension point, you can specify context value for key `comment` in `when` expression like `comment == editable`. - */ + * Context value of the comment. This can be used to contribute comment specific actions. + * For example, a comment is given a context value as `editable`. When contributing actions to `comments/comment/title` + * using `menus` extension point, you can specify context value for key `comment` in `when` expression like `comment == editable`. + */ contextValue?: string; /** - * Optional reactions of the [comment](#Comment) - */ + * Optional reactions of the [comment](#Comment) + */ reactions?: CommentReaction[]; /** - * Optional label describing the [Comment](#Comment) - * Label will be rendered next to authorName if exists. - */ + * Optional label describing the [Comment](#Comment) + * Label will be rendered next to authorName if exists. + */ label?: string; } diff --git a/yarn.lock b/yarn.lock index 0d0f7a7229df1..51fb0a5c200ed 100644 --- a/yarn.lock +++ b/yarn.lock @@ -785,9 +785,10 @@ dependencies: nan "2.10.0" -"@typefox/monaco-editor-core@^0.14.6": - version "0.14.6" - resolved "https://registry.yarnpkg.com/@typefox/monaco-editor-core/-/monaco-editor-core-0.14.6.tgz#32e378f3430913504ea9c7063944444a04429892" +"@typefox/monaco-editor-core@next": + version "0.18.0-next.1" + resolved "https://registry.yarnpkg.com/@typefox/monaco-editor-core/-/monaco-editor-core-0.18.0-next.1.tgz#c31d3361215703b524065f460e1b4b6b714699db" + integrity sha512-l7uZbyLfXwh5b5YHv1U2T5KQAVrPTnUqklRd1HEI6ZBWGqw5ukXa5L17ATaVYRpCsy7ZtqyOQS2viWSS/goNFA== "@types/base64-arraybuffer@0.1.0": version "0.1.0" @@ -7347,9 +7348,9 @@ monaco-html@^2.0.2: resolved "https://registry.yarnpkg.com/monaco-html/-/monaco-html-2.2.0.tgz#435f2f4ce6e5c7f707fb57e7c8a05b41e5cd1aa0" monaco-languageclient@next: - version "0.9.0-next.1" - resolved "https://registry.yarnpkg.com/monaco-languageclient/-/monaco-languageclient-0.9.0-next.1.tgz#2a3c133f3b16c345aa3eba0738e7a0161cb2b05e" - integrity sha512-POZOxVsRBzqK14yfk66clEefTFR+Ahx77uhyhanmJG/pItBVX4E962N/uUwYZPGc4zO3FZ+Ef7tFFxbyIQXdAg== + version "0.9.1-next.23" + resolved "https://registry.yarnpkg.com/monaco-languageclient/-/monaco-languageclient-0.9.1-next.23.tgz#cb75de6c9b5ad5ab8661a635039226fd44b16850" + integrity sha512-al9kkHN8nJKu032s1Ac1+4dOgFMpejzh4VnIPem4Ct65S2/GmONK5sV4cSNj5YKQt71+Ja0PADIvoDYIo0UDtA== dependencies: glob-to-regexp "^0.3.0" vscode-jsonrpc "^4.1.0-next"