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/core/src/browser/keybinding.ts b/packages/core/src/browser/keybinding.ts index 42c5e49e3072a..81598762eace5 100644 --- a/packages/core/src/browser/keybinding.ts +++ b/packages/core/src/browser/keybinding.ts @@ -274,7 +274,7 @@ export class KeybindingRegistry { containsKeybinding(bindings: Keybinding[], binding: Keybinding): boolean { const bindingKeySequence = this.resolveKeybinding(binding); const collisions = this.getKeySequenceCollisions(bindings, bindingKeySequence) - .filter(b => b.context === binding.context); + .filter(b => b.context === binding.context && !b.when && !binding.when); if (collisions.full.length > 0) { this.logger.warn('Collided keybinding is ignored; ', 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.spec.ts b/packages/editor-preview/src/browser/editor-preview-manager.spec.ts index ce84c4316f24e..1b8038ae4d15b 100644 --- a/packages/editor-preview/src/browser/editor-preview-manager.spec.ts +++ b/packages/editor-preview/src/browser/editor-preview-manager.spec.ts @@ -18,7 +18,7 @@ // disable no-unused-expression for chai. // tslint:disable:no-any no-unused-expression -import {enableJSDOM} from '@theia/core/lib/browser/test/jsdom'; +import { enableJSDOM } from '@theia/core/lib/browser/test/jsdom'; const disableJsDom = enableJSDOM(); import URI from '@theia/core/lib/common/uri'; @@ -38,7 +38,7 @@ sinon.stub(mockEditorWidget, 'id').get(() => 'mockEditorWidget'); const mockPreviewWidget = sinon.createStubInstance(EditorPreviewWidget); sinon.stub(mockPreviewWidget, 'id').get(() => 'mockPreviewWidget'); -sinon.stub(mockPreviewWidget, 'disposed').get(() => ({connect: () => 1})); +sinon.stub(mockPreviewWidget, 'disposed').get(() => ({ connect: () => 1 })); let onPinnedListeners: Function[] = []; sinon.stub(mockPreviewWidget, 'onPinned').get(() => (fn: Function) => onPinnedListeners.push(fn)); @@ -50,10 +50,10 @@ let onCreateListners: Function[] = []; mockWidgetManager.onDidCreateWidget = sinon.stub().callsFake((fn: Function) => onCreateListners.push(fn)); (mockWidgetManager.getOrCreateWidget as sinon.SinonStub).returns(mockPreviewWidget); -const mockShell = sinon.createStubInstance(ApplicationShell); +const mockShell = sinon.createStubInstance(ApplicationShell) as ApplicationShell; const mockPreference = sinon.createStubInstance(PreferenceServiceImpl); -mockPreference.onPreferenceChanged = sinon.stub().returns({dispose: () => {}}); +mockPreference.onPreferenceChanged = sinon.stub().returns({ dispose: () => { } }); let testContainer: Container; @@ -62,7 +62,7 @@ before(() => { // Mock out injected dependencies. testContainer.bind(EditorManager).toDynamicValue(ctx => mockEditorManager); testContainer.bind(WidgetManager).toDynamicValue(ctx => mockWidgetManager); - testContainer.bind(ApplicationShell).toDynamicValue(ctx => mockShell); + testContainer.bind(ApplicationShell).toConstantValue(mockShell); testContainer.bind(PreferenceService).toDynamicValue(ctx => mockPreference); testContainer.load(previewFrontEndModule.default); @@ -77,6 +77,8 @@ describe('editor-preview-manager', () => { beforeEach(() => { previewManager = testContainer.get(OpenHandler); + sinon.stub(previewManager as any, 'onActive').resolves(); + sinon.stub(previewManager as any, 'onReveal').resolves(); }); afterEach(() => { onCreateListners = []; @@ -86,23 +88,23 @@ describe('editor-preview-manager', () => { it('should handle preview requests if editor.enablePreview enabled', async () => { (mockPreference.get as sinon.SinonStub).returns(true); (mockPreference.validate as sinon.SinonStub).returns(true); - expect(await previewManager.canHandle(new URI(), {preview: true})).to.be.greaterThan(0); + expect(await previewManager.canHandle(new URI(), { preview: true })).to.be.greaterThan(0); }); it('should not handle preview requests if editor.enablePreview disabled', async () => { (mockPreference.get as sinon.SinonStub).returns(false); (mockPreference.validate as sinon.SinonStub).returns(true); - expect(await previewManager.canHandle(new URI(), {preview: true})).to.equal(0); + expect(await previewManager.canHandle(new URI(), { preview: true })).to.equal(0); }); it('should not handle requests that are not preview or currently being previewed', async () => { expect(await previewManager.canHandle(new URI())).to.equal(0); }); it('should create a preview editor and replace where required.', async () => { - const w = await previewManager.open(new URI(), {preview: true}); + const w = await previewManager.open(new URI(), { preview: true }); expect(w instanceof EditorPreviewWidget).to.be.true; expect((w as any).replaceEditorWidget.calledOnce).to.be.false; // Replace the EditorWidget with another open call to an editor that doesn't exist. - const afterReplace = await previewManager.open(new URI(), {preview: true}); + const afterReplace = await previewManager.open(new URI(), { preview: true }); expect((afterReplace as any).replaceEditorWidget.calledOnce).to.be.true; // Ensure the same preview widget was re-used. @@ -116,7 +118,7 @@ describe('editor-preview-manager', () => { // Activate existing preview mockEditorWidget.parent = mockPreviewWidget; - expect(await previewManager.open(new URI(), {preview: true})).to.equal(mockPreviewWidget); + expect(await previewManager.open(new URI(), { preview: true })).to.equal(mockPreviewWidget); // Ensure it is not pinned. expect((mockPreviewWidget.pinEditorWidget as sinon.SinonStub).calledOnce).to.be.false; @@ -126,9 +128,9 @@ describe('editor-preview-manager', () => { }); it('should should transition the editor to perminent on pin events.', async () => { // Fake creation call. - await onCreateListners.pop()!({factoryId: EditorPreviewWidgetFactory.ID, widget: mockPreviewWidget}); + await onCreateListners.pop()!({ factoryId: EditorPreviewWidgetFactory.ID, widget: mockPreviewWidget }); // Fake pinned call - onPinnedListeners.pop()!({preview: mockPreviewWidget, editorWidget: mockEditorWidget}); + onPinnedListeners.pop()!({ preview: mockPreviewWidget, editorWidget: mockEditorWidget }); expect(mockPreviewWidget.dispose.calledOnce).to.be.true; expect(mockEditorWidget.close.calledOnce).to.be.false; 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-preview/src/browser/editor-preview-widget.ts b/packages/editor-preview/src/browser/editor-preview-widget.ts index 332930dada533..022645c163feb 100644 --- a/packages/editor-preview/src/browser/editor-preview-widget.ts +++ b/packages/editor-preview/src/browser/editor-preview-widget.ts @@ -15,8 +15,8 @@ ********************************************************************************/ import { - ApplicationShell, BaseWidget, DockPanel, Navigatable, PanelLayout, Saveable, - StatefulWidget, Title, Widget, WidgetConstructionOptions, WidgetManager + ApplicationShell, BaseWidget, DockPanel, Navigatable, PanelLayout, Saveable, + StatefulWidget, Title, Widget, WidgetConstructionOptions, WidgetManager } from '@theia/core/lib/browser'; import { Emitter, DisposableCollection } from '@theia/core/lib/common'; import URI from '@theia/core/lib/common/uri'; @@ -70,7 +70,7 @@ export class EditorPreviewWidget extends BaseWidget implements ApplicationShell. return this.pinned_; } - get saveable(): Saveable|undefined { + get saveable(): Saveable | undefined { if (this.editorWidget_) { return this.editorWidget_.saveable; } @@ -80,14 +80,14 @@ export class EditorPreviewWidget extends BaseWidget implements ApplicationShell. return this.editorWidget_ && this.editorWidget_.getResourceUri(); } createMoveToUri(resourceUri: URI): URI | undefined { - return this.editorWidget_ && this.editorWidget_.createMoveToUri(resourceUri); + return this.editorWidget_ && this.editorWidget_.createMoveToUri(resourceUri); } pinEditorWidget(): void { this.title.className = this.title.className.replace(PREVIEW_TITLE_CLASS, ''); this.pinListeners.dispose(); this.pinned_ = true; - this.onPinnedEmitter.fire({preview: this, editorWidget: this.editorWidget_!}); + this.onPinnedEmitter.fire({ preview: this, editorWidget: this.editorWidget_! }); } replaceEditorWidget(editorWidget: EditorWidget): void { @@ -156,15 +156,15 @@ export class EditorPreviewWidget extends BaseWidget implements ApplicationShell. } }; parent.layoutModified.connect(layoutListener); - this.pinListeners.push({dispose: () => parent.layoutModified.disconnect(layoutListener)}); + this.pinListeners.push({ dispose: () => parent.layoutModified.disconnect(layoutListener) }); - const tabMovedListener = (w: Widget, args: {title: Title}) => { + const tabMovedListener = (w: Widget, args: { title: Title }) => { if (args.title === this.title) { this.pinEditorWidget(); } }; tabBar.tabMoved.connect(tabMovedListener); - this.pinListeners.push({dispose: () => tabBar.tabMoved.disconnect(tabMovedListener)}); + this.pinListeners.push({ dispose: () => tabBar.tabMoved.disconnect(tabMovedListener) }); const attachDoubleClickListener = (attempt: number): number | undefined => { const tabNode = tabBar.contentNode.children.item(tabBar.currentIndex); @@ -173,7 +173,7 @@ export class EditorPreviewWidget extends BaseWidget implements ApplicationShell. } const dblClickListener = (event: Event) => this.pinEditorWidget(); tabNode.addEventListener('dblclick', dblClickListener); - this.pinListeners.push({dispose: () => tabNode.removeEventListener('dblclick', dblClickListener)}); + this.pinListeners.push({ dispose: () => tabNode.removeEventListener('dblclick', dblClickListener) }); }; requestAnimationFrame(() => attachDoubleClickListener(0)); } @@ -190,7 +190,7 @@ export class EditorPreviewWidget extends BaseWidget implements ApplicationShell. const width = parseInt(this.node.style.width || ''); const height = parseInt(this.node.style.height || ''); if (width && height) { - this.editorWidget_.editor.setSize({width, height}); + this.editorWidget_.editor.setSize({ width, height }); } } MessageLoop.sendMessage(this.editorWidget_, msg); @@ -211,9 +211,9 @@ export class EditorPreviewWidget extends BaseWidget implements ApplicationShell. } async restoreState(state: PreviewViewState): Promise { - const {pinned, editorState, previewDescription} = state; + const { pinned, editorState, previewDescription } = state; if (!this.editorWidget_ && previewDescription) { - const {factoryId, options} = previewDescription; + const { factoryId, options } = previewDescription; const editorWidget = await this.widgetManager.getOrCreateWidget(factoryId, options) as EditorWidget; this.replaceEditorWidget(editorWidget); } diff --git a/packages/editor/src/browser/editor-preferences.ts b/packages/editor/src/browser/editor-preferences.ts index a6ec1cc91544f..1529d2ca8b575 100644 --- a/packages/editor/src/browser/editor-preferences.ts +++ b/packages/editor/src/browser/editor-preferences.ts @@ -21,15 +21,22 @@ import { PreferenceService, PreferenceContribution, PreferenceSchema, - PreferenceChangeEvent + PreferenceChangeEvent, + PreferenceSchemaProperties } from '@theia/core/lib/browser/preferences'; -import { isWindows, isOSX } from '@theia/core/lib/common/os'; +import { isWindows, isOSX, OS } from '@theia/core/lib/common/os'; import { SUPPORTED_ENCODINGS } from './supported-encodings'; const DEFAULT_WINDOWS_FONT_FAMILY = 'Consolas, \'Courier New\', monospace'; const DEFAULT_MAC_FONT_FAMILY = 'Menlo, Monaco, \'Courier New\', monospace'; const DEFAULT_LINUX_FONT_FAMILY = '\'Droid Sans Mono\', \'monospace\', monospace, \'Droid Sans Fallback\''; +const platform = { + isMacintosh: isOSX, + isLinux: OS.type() === OS.Type.Linux +}; + +// should be in sync with https://github.com/TypeFox/vscode/blob/70b8db24a37fafc77247de7f7cb5bb0195120ed0/src/vs/editor/common/config/editorOptions.ts#L2585 export const EDITOR_FONT_DEFAULTS = { fontFamily: ( isOSX ? DEFAULT_MAC_FONT_FAMILY : (isWindows ? DEFAULT_WINDOWS_FONT_FAMILY : DEFAULT_LINUX_FONT_FAMILY) @@ -42,56 +49,988 @@ export const EDITOR_FONT_DEFAULTS = { letterSpacing: 0, }; +// should be in sync with https://github.com/TypeFox/vscode/blob/70b8db24a37fafc77247de7f7cb5bb0195120ed0/src/vs/editor/common/config/editorOptions.ts#L2600 +export const EDITOR_MODEL_DEFAULTS = { + tabSize: 4, + indentSize: 4, + insertSpaces: true, + detectIndentation: true, + trimAutoWhitespace: true, + largeFileOptimizations: true +}; + +// tslint:disable:no-null-keyword +// should be in sync with https://github.com/TypeFox/vscode/blob/70b8db24a37fafc77247de7f7cb5bb0195120ed0/src/vs/editor/common/config/editorOptions.ts#L2612 +// 1. Copy +// 2. Inline values +export const EDITOR_DEFAULTS = { + inDiffEditor: false, + wordSeparators: '`~!@#$%^&*()-=+[{]}\\|;:\'",.<>/?', + lineNumbersMinChars: 5, + lineDecorationsWidth: 10, + readOnly: false, + mouseStyle: 'text', + disableLayerHinting: false, + automaticLayout: false, + wordWrap: 'off', + wordWrapColumn: 80, + wordWrapMinified: true, + wrappingIndent: 1, + wordWrapBreakBeforeCharacters: '([{‘“〈《「『【〔([{「£¥$£¥++', + wordWrapBreakAfterCharacters: ' \t})]?|/&,;¢°′″‰℃、。。、¢,.:;?!%・・ゝゞヽヾーァィゥェォッャュョヮヵヶぁぃぅぇぉっゃゅょゎゕゖㇰㇱㇲㇳㇴㇵㇶㇷㇸㇹㇺㇻㇼㇽㇾㇿ々〻ァィゥェォャュョッー”〉》」』】〕)]}」', + wordWrapBreakObtrusiveCharacters: '.', + autoClosingBrackets: 'languageDefined', + autoClosingQuotes: 'languageDefined', + autoSurround: 'languageDefined', + autoIndent: true, + dragAndDrop: true, + emptySelectionClipboard: true, + copyWithSyntaxHighlighting: true, + useTabStops: true, + multiCursorModifier: 'altKey', + multiCursorMergeOverlapping: true, + accessibilitySupport: 'auto', + showUnused: true, + + viewInfo: { + extraEditorClassName: '', + disableMonospaceOptimizations: false, + rulers: [], + ariaLabel: 'Editor content', + renderLineNumbers: 1, + renderCustomLineNumbers: null, + cursorSurroundingLines: 0, + renderFinalNewline: true, + selectOnLineNumbers: true, + glyphMargin: true, + revealHorizontalRightPadding: 30, + roundedSelection: true, + overviewRulerLanes: 2, + overviewRulerBorder: true, + cursorBlinking: 1, + mouseWheelZoom: false, + cursorSmoothCaretAnimation: false, + cursorStyle: 1, + cursorWidth: 0, + hideCursorInOverviewRuler: false, + scrollBeyondLastLine: true, + scrollBeyondLastColumn: 5, + smoothScrolling: false, + stopRenderingLineAfter: 10000, + renderWhitespace: 'none', + renderControlCharacters: false, + fontLigatures: false, + renderIndentGuides: true, + highlightActiveIndentGuide: true, + renderLineHighlight: 'line', + scrollbar: { + vertical: 1, + horizontal: 1, + arrowSize: 11, + useShadows: true, + verticalHasArrows: false, + horizontalHasArrows: false, + horizontalScrollbarSize: 10, + horizontalSliderSize: 10, + verticalScrollbarSize: 14, + verticalSliderSize: 14, + handleMouseWheel: true, + mouseWheelScrollSensitivity: 1, + fastScrollSensitivity: 5, + }, + minimap: { + enabled: true, + side: 'right', + showSlider: 'mouseover', + renderCharacters: true, + maxColumn: 120 + }, + fixedOverflowWidgets: false, + }, + + contribInfo: { + selectionClipboard: true, + hover: { + enabled: true, + delay: 300, + sticky: true + }, + links: true, + contextmenu: true, + quickSuggestions: { other: true, comments: false, strings: false }, + quickSuggestionsDelay: 10, + parameterHints: { + enabled: true, + cycle: false + }, + formatOnType: false, + formatOnPaste: false, + suggestOnTriggerCharacters: true, + acceptSuggestionOnEnter: 'on', + acceptSuggestionOnCommitCharacter: true, + wordBasedSuggestions: true, + suggestSelection: 'recentlyUsed', + suggestFontSize: 0, + suggestLineHeight: 0, + tabCompletion: 'off', + suggest: { + filterGraceful: true, + snippets: 'inline', + snippetsPreventQuickSuggestions: true, + localityBonus: false, + shareSuggestSelections: false, + showIcons: true, + maxVisibleSuggestions: 12, + filteredTypes: Object.create(null) + }, + gotoLocation: { + multiple: 'peek' + }, + selectionHighlight: true, + occurrencesHighlight: true, + codeLens: true, + folding: true, + foldingStrategy: 'auto', + showFoldingControls: 'mouseover', + matchBrackets: true, + find: { + seedSearchStringFromSelection: true, + autoFindInSelection: false, + globalFindClipboard: false, + addExtraSpaceOnTop: true + }, + colorDecorators: true, + lightbulbEnabled: true, + codeActionsOnSave: {}, + codeActionsOnSaveTimeout: 750 + }, +}; +// tslint:eanble:no-null-keyword + +// tslint:disable:max-line-length +// should be in sync with https://github.com/TypeFox/vscode/blob/70b8db24a37fafc77247de7f7cb5bb0195120ed0/src/vs/editor/common/config/commonEditorConfig.ts#L232 +// 1. Copy +// 2. Find -> Use Regular Expressions -> nls\.localize\(.*, "(.*)"\) -> "$1" +// 3. Find -> Use Regular Expressions -> nls\.localize\(.*, '(.*)'\) -> '$1' +// 4. Apply `quotemark` quick fixes +// 5. Fix the rest manually +const codeEditorPreferenceProperties = { + 'editor.fontFamily': { + 'type': 'string', + 'default': EDITOR_FONT_DEFAULTS.fontFamily, + 'description': 'Controls the font family.' + }, + 'editor.fontWeight': { + 'type': 'string', + 'enum': ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'], + 'default': EDITOR_FONT_DEFAULTS.fontWeight, + 'description': 'Controls the font weight.' + }, + 'editor.fontSize': { + 'type': 'number', + 'default': EDITOR_FONT_DEFAULTS.fontSize, + 'description': 'Controls the font size in pixels.' + }, + 'editor.lineHeight': { + 'type': 'number', + 'default': EDITOR_FONT_DEFAULTS.lineHeight, + 'description': 'Controls the line height. Use 0 to compute the line height from the font size.' + }, + 'editor.letterSpacing': { + 'type': 'number', + 'default': EDITOR_FONT_DEFAULTS.letterSpacing, + 'description': 'Controls the letter spacing in pixels.' + }, + 'editor.lineNumbers': { + 'type': 'string', + 'enum': ['off', 'on', 'relative', 'interval'], + 'enumDescriptions': [ + 'Line numbers are not rendered.', + 'Line numbers are rendered as absolute number.', + 'Line numbers are rendered as distance in lines to cursor position.', + 'Line numbers are rendered every 10 lines.' + ], + 'default': 'on', + 'description': 'Controls the display of line numbers.' + }, + 'editor.cursorSurroundingLines': { + 'type': 'number', + 'default': EDITOR_DEFAULTS.viewInfo.cursorSurroundingLines, + 'description': "Controls the minimal number of visible leading and trailing lines surrounding the cursor. Known as 'scrollOff' or `scrollOffset` in some other editors." + }, + 'editor.renderFinalNewline': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.viewInfo.renderFinalNewline, + 'description': 'Render last line number when the file ends with a newline.' + }, + 'editor.rulers': { + 'type': 'array', + 'items': { + 'type': 'number' + }, + 'default': EDITOR_DEFAULTS.viewInfo.rulers, + 'description': 'Render vertical rulers after a certain number of monospace characters. Use multiple values for multiple rulers. No rulers are drawn if array is empty.' + }, + 'editor.wordSeparators': { + 'type': 'string', + 'default': EDITOR_DEFAULTS.wordSeparators, + 'description': 'Characters that will be used as word separators when doing word related navigations or operations.' + }, + 'editor.tabSize': { + 'type': 'number', + 'default': EDITOR_MODEL_DEFAULTS.tabSize, + 'minimum': 1, + 'markdownDescription': 'The number of spaces a tab is equal to. This setting is overridden based on the file contents when `#editor.detectIndentation#` is on.' + }, + 'editor.insertSpaces': { + 'type': 'boolean', + 'default': EDITOR_MODEL_DEFAULTS.insertSpaces, + 'markdownDescription': 'Insert spaces when pressing `Tab`. This setting is overridden based on the file contents when `#editor.detectIndentation#` is on.' + }, + 'editor.detectIndentation': { + 'type': 'boolean', + 'default': EDITOR_MODEL_DEFAULTS.detectIndentation, + 'markdownDescription': 'Controls whether `#editor.tabSize#` and `#editor.insertSpaces#` will be automatically detected when a file is opened based on the file contents.' + }, + 'editor.roundedSelection': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.viewInfo.roundedSelection, + 'description': 'Controls whether selections should have rounded corners.' + }, + 'editor.scrollBeyondLastLine': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.viewInfo.scrollBeyondLastLine, + 'description': 'Controls whether the editor will scroll beyond the last line.' + }, + 'editor.scrollBeyondLastColumn': { + 'type': 'number', + 'default': EDITOR_DEFAULTS.viewInfo.scrollBeyondLastColumn, + 'description': 'Controls the number of extra characters beyond which the editor will scroll horizontally.' + }, + 'editor.smoothScrolling': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.viewInfo.smoothScrolling, + 'description': 'Controls whether the editor will scroll using an animation.' + }, + 'editor.minimap.enabled': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.viewInfo.minimap.enabled, + 'description': 'Controls whether the minimap is shown.' + }, + 'editor.minimap.side': { + 'type': 'string', + 'enum': ['left', 'right'], + 'default': EDITOR_DEFAULTS.viewInfo.minimap.side, + 'description': 'Controls the side where to render the minimap.' + }, + 'editor.minimap.showSlider': { + 'type': 'string', + 'enum': ['always', 'mouseover'], + 'default': EDITOR_DEFAULTS.viewInfo.minimap.showSlider, + 'description': 'Controls whether the minimap slider is automatically hidden.' + }, + 'editor.minimap.renderCharacters': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.viewInfo.minimap.renderCharacters, + 'description': 'Render the actual characters on a line as opposed to color blocks.' + }, + 'editor.minimap.maxColumn': { + 'type': 'number', + 'default': EDITOR_DEFAULTS.viewInfo.minimap.maxColumn, + 'description': 'Limit the width of the minimap to render at most a certain number of columns.' + }, + 'editor.hover.enabled': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.contribInfo.hover.enabled, + 'description': 'Controls whether the hover is shown.' + }, + 'editor.hover.delay': { + 'type': 'number', + 'default': EDITOR_DEFAULTS.contribInfo.hover.delay, + 'description': 'Controls the delay in milliseconds after which the hover is shown.' + }, + 'editor.hover.sticky': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.contribInfo.hover.sticky, + 'description': 'Controls whether the hover should remain visible when mouse is moved over it.' + }, + 'editor.find.seedSearchStringFromSelection': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.contribInfo.find.seedSearchStringFromSelection, + 'description': 'Controls whether the search string in the Find Widget is seeded from the editor selection.' + }, + 'editor.find.autoFindInSelection': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.contribInfo.find.autoFindInSelection, + 'description': 'Controls whether the find operation is carried out on selected text or the entire file in the editor.' + }, + 'editor.find.globalFindClipboard': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.contribInfo.find.globalFindClipboard, + 'description': 'Controls whether the Find Widget should read or modify the shared find clipboard on macOS.', + 'included': platform.isMacintosh + }, + 'editor.find.addExtraSpaceOnTop': { + 'type': 'boolean', + 'default': true, + 'description': 'Controls whether the Find Widget should add extra lines on top of the editor. When true, you can scroll beyond the first line when the Find Widget is visible.' + }, + 'editor.wordWrap': { + 'type': 'string', + 'enum': ['off', 'on', 'wordWrapColumn', 'bounded'], + 'markdownEnumDescriptions': [ + 'Lines will never wrap.', + 'Lines will wrap at the viewport width.', + 'Lines will wrap at `#editor.wordWrapColumn#`.', + 'Lines will wrap at the minimum of viewport and `#editor.wordWrapColumn#`.', + ], + 'default': EDITOR_DEFAULTS.wordWrap, + 'description': 'Controls how lines should wrap.' + }, + 'editor.wordWrapColumn': { + 'type': 'integer', + 'default': EDITOR_DEFAULTS.wordWrapColumn, + 'minimum': 1, + 'markdownDescription': 'Controls the wrapping column of the editor when `#editor.wordWrap#` is `wordWrapColumn` or `bounded`.' + }, + 'editor.wrappingIndent': { + 'type': 'string', + 'enum': ['none', 'same', 'indent', 'deepIndent'], + enumDescriptions: [ + 'No indentation. Wrapped lines begin at column 1.', + 'Wrapped lines get the same indentation as the parent.', + 'Wrapped lines get +1 indentation toward the parent.', + 'Wrapped lines get +2 indentation toward the parent.', + ], + 'default': 'same', + 'description': 'Controls the indentation of wrapped lines.', + }, + 'editor.mouseWheelScrollSensitivity': { + 'type': 'number', + 'default': EDITOR_DEFAULTS.viewInfo.scrollbar.mouseWheelScrollSensitivity, + 'markdownDescription': 'A multiplier to be used on the `deltaX` and `deltaY` of mouse wheel scroll events.' + }, + 'editor.fastScrollSensitivity': { + 'type': 'number', + 'default': EDITOR_DEFAULTS.viewInfo.scrollbar.fastScrollSensitivity, + 'markdownDescription': 'Scrolling speed multiplier when pressing `Alt`.' + }, + 'editor.multiCursorModifier': { + 'type': 'string', + 'enum': ['ctrlCmd', 'alt'], + 'markdownEnumDescriptions': [ + 'Maps to `Control` on Windows and Linux and to `Command` on macOS.', + 'Maps to `Alt` on Windows and Linux and to `Option` on macOS.' + ], + 'default': 'alt', + 'markdownDescription': 'The modifier to be used to add multiple cursors with the mouse. The Go To Definition and Open Link mouse gestures will adapt such that they do not conflict with the multicursor modifier. [Read more](https://code.visualstudio.com/docs/editor/codebasics#_multicursor-modifier).' + }, + 'editor.multiCursorMergeOverlapping': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.multiCursorMergeOverlapping, + 'description': 'Merge multiple cursors when they are overlapping.' + }, + 'editor.quickSuggestions': { + 'anyOf': [ + { + type: 'boolean', + }, + { + type: 'object', + properties: { + strings: { + type: 'boolean', + default: false, + description: 'Enable quick suggestions inside strings.' + }, + comments: { + type: 'boolean', + default: false, + description: 'Enable quick suggestions inside comments.' + }, + other: { + type: 'boolean', + default: true, + description: 'Enable quick suggestions outside of strings and comments.' + }, + } + } + ], + 'default': EDITOR_DEFAULTS.contribInfo.quickSuggestions, + 'description': 'Controls whether suggestions should automatically show up while typing.' + }, + 'editor.quickSuggestionsDelay': { + 'type': 'integer', + 'default': EDITOR_DEFAULTS.contribInfo.quickSuggestionsDelay, + 'minimum': 0, + 'description': 'Controls the delay in milliseconds after which quick suggestions will show up.' + }, + 'editor.parameterHints.enabled': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.contribInfo.parameterHints.enabled, + 'description': 'Enables a pop-up that shows parameter documentation and type information as you type.' + }, + 'editor.parameterHints.cycle': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.contribInfo.parameterHints.cycle, + 'description': 'Controls whether the parameter hints menu cycles or closes when reaching the end of the list.' + }, + 'editor.autoClosingBrackets': { + type: 'string', + enum: ['always', 'languageDefined', 'beforeWhitespace', 'never'], + enumDescriptions: [ + '', + 'Use language configurations to determine when to autoclose brackets.', + 'Autoclose brackets only when the cursor is to the left of whitespace.', + '', + + ], + 'default': EDITOR_DEFAULTS.autoClosingBrackets, + 'description': 'Controls whether the editor should automatically close brackets after the user adds an opening bracket.' + }, + 'editor.autoClosingQuotes': { + type: 'string', + enum: ['always', 'languageDefined', 'beforeWhitespace', 'never'], + enumDescriptions: [ + '', + 'Use language configurations to determine when to autoclose quotes.', + 'Autoclose quotes only when the cursor is to the left of whitespace.', + '', + ], + 'default': EDITOR_DEFAULTS.autoClosingQuotes, + 'description': 'Controls whether the editor should automatically close quotes after the user adds an opening quote.' + }, + 'editor.autoSurround': { + type: 'string', + enum: ['languageDefined', 'brackets', 'quotes', 'never'], + enumDescriptions: [ + 'Use language configurations to determine when to automatically surround selections.', + 'Surround with brackets but not quotes.', + 'Surround with quotes but not brackets.', + '' + ], + 'default': EDITOR_DEFAULTS.autoSurround, + 'description': 'Controls whether the editor should automatically surround selections.' + }, + 'editor.formatOnType': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.contribInfo.formatOnType, + 'description': 'Controls whether the editor should automatically format the line after typing.' + }, + 'editor.formatOnPaste': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.contribInfo.formatOnPaste, + 'description': 'Controls whether the editor should automatically format the pasted content. A formatter must be available and the formatter should be able to format a range in a document.' + }, + 'editor.autoIndent': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.autoIndent, + 'description': 'Controls whether the editor should automatically adjust the indentation when users type, paste or move lines. Extensions with indentation rules of the language must be available.' + }, + 'editor.suggestOnTriggerCharacters': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.contribInfo.suggestOnTriggerCharacters, + 'description': 'Controls whether suggestions should automatically show up when typing trigger characters.' + }, + 'editor.acceptSuggestionOnEnter': { + 'type': 'string', + 'enum': ['on', 'smart', 'off'], + 'default': EDITOR_DEFAULTS.contribInfo.acceptSuggestionOnEnter, + 'markdownEnumDescriptions': [ + '', + 'Only accept a suggestion with `Enter` when it makes a textual change.', + '' + ], + 'markdownDescription': 'Controls whether suggestions should be accepted on `Enter`, in addition to `Tab`. Helps to avoid ambiguity between inserting new lines or accepting suggestions.' + }, + 'editor.acceptSuggestionOnCommitCharacter': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.contribInfo.acceptSuggestionOnCommitCharacter, + 'markdownDescription': 'Controls whether suggestions should be accepted on commit characters. For example, in JavaScript, the semi-colon (`;`) can be a commit character that accepts a suggestion and types that character.' + }, + 'editor.snippetSuggestions': { + 'type': 'string', + 'enum': ['top', 'bottom', 'inline', 'none'], + 'enumDescriptions': [ + 'Show snippet suggestions on top of other suggestions.', + 'Show snippet suggestions below other suggestions.', + 'Show snippets suggestions with other suggestions.', + 'Do not show snippet suggestions.', + ], + 'default': EDITOR_DEFAULTS.contribInfo.suggest.snippets, + 'description': 'Controls whether snippets are shown with other suggestions and how they are sorted.' + }, + 'editor.emptySelectionClipboard': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.emptySelectionClipboard, + 'description': 'Controls whether copying without a selection copies the current line.' + }, + 'editor.copyWithSyntaxHighlighting': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.copyWithSyntaxHighlighting, + 'description': 'Controls whether syntax highlighting should be copied into the clipboard.' + }, + 'editor.wordBasedSuggestions': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.contribInfo.wordBasedSuggestions, + 'description': 'Controls whether completions should be computed based on words in the document.' + }, + 'editor.suggestSelection': { + 'type': 'string', + 'enum': ['first', 'recentlyUsed', 'recentlyUsedByPrefix'], + 'markdownEnumDescriptions': [ + 'Always select the first suggestion.', + 'Select recent suggestions unless further typing selects one, e.g. `console.| -> console.log` because `log` has been completed recently.', + 'Select suggestions based on previous prefixes that have completed those suggestions, e.g. `co -> console` and `con -> const`.', + ], + 'default': 'recentlyUsed', + 'description': 'Controls how suggestions are pre-selected when showing the suggest list.' + }, + 'editor.suggestFontSize': { + 'type': 'integer', + 'default': 0, + 'minimum': 0, + 'markdownDescription': 'Font size for the suggest widget. When set to `0`, the value of `#editor.fontSize#` is used.' + }, + 'editor.suggestLineHeight': { + 'type': 'integer', + 'default': 0, + 'minimum': 0, + 'markdownDescription': 'Line height for the suggest widget. When set to `0`, the value of `#editor.lineHeight#` is used.' + }, + 'editor.tabCompletion': { + type: 'string', + default: 'off', + enum: ['on', 'off', 'onlySnippets'], + enumDescriptions: [ + 'Tab complete will insert the best matching suggestion when pressing tab.', + 'Disable tab completions.', + "Tab complete snippets when their prefix match. Works best when 'quickSuggestions' aren't enabled.", + ], + description: 'Enables tab completions.' + }, + 'editor.suggest.filterGraceful': { + type: 'boolean', + default: true, + description: 'Controls whether filtering and sorting suggestions accounts for small typos.' + }, + 'editor.suggest.localityBonus': { + type: 'boolean', + default: false, + description: 'Controls whether sorting favours words that appear close to the cursor.' + }, + 'editor.suggest.shareSuggestSelections': { + type: 'boolean', + default: false, + markdownDescription: 'Controls whether remembered suggestion selections are shared between multiple workspaces and windows (needs `#editor.suggestSelection#`).' + }, + 'editor.suggest.snippetsPreventQuickSuggestions': { + type: 'boolean', + default: true, + description: 'Control whether an active snippet prevents quick suggestions.' + }, + 'editor.suggest.showIcons': { + type: 'boolean', + default: EDITOR_DEFAULTS.contribInfo.suggest.showIcons, + description: 'Controls whether to show or hide icons in suggestions.' + }, + 'editor.suggest.maxVisibleSuggestions': { + type: 'number', + default: EDITOR_DEFAULTS.contribInfo.suggest.maxVisibleSuggestions, + minimum: 1, + maximum: 15, + description: 'Controls how many suggestions IntelliSense will show before showing a scrollbar (maximum 15).' + }, + 'editor.suggest.filteredTypes': { + type: 'object', + default: { keyword: true, snippet: true }, + markdownDescription: 'Controls whether some suggestion types should be filtered from IntelliSense. A list of suggestion types can be found here: https://code.visualstudio.com/docs/editor/intellisense#_types-of-completions.', + properties: { + method: { + type: 'boolean', + default: true, + markdownDescription: 'When set to `false` IntelliSense never shows `method` suggestions.' + }, + function: { + type: 'boolean', + default: true, + markdownDescription: 'When set to `false` IntelliSense never shows `function` suggestions.' + }, + constructor: { + type: 'boolean', + default: true, + markdownDescription: 'When set to `false` IntelliSense never shows `constructor` suggestions.' + }, + field: { + type: 'boolean', + default: true, + markdownDescription: 'When set to `false` IntelliSense never shows `field` suggestions.' + }, + variable: { + type: 'boolean', + default: true, + markdownDescription: 'When set to `false` IntelliSense never shows `variable` suggestions.' + }, + class: { + type: 'boolean', + default: true, + markdownDescription: 'When set to `false` IntelliSense never shows `class` suggestions.' + }, + struct: { + type: 'boolean', + default: true, + markdownDescription: 'When set to `false` IntelliSense never shows `struct` suggestions.' + }, + interface: { + type: 'boolean', + default: true, + markdownDescription: 'When set to `false` IntelliSense never shows `interface` suggestions.' + }, + module: { + type: 'boolean', + default: true, + markdownDescription: 'When set to `false` IntelliSense never shows `module` suggestions.' + }, + property: { + type: 'boolean', + default: true, + markdownDescription: 'When set to `false` IntelliSense never shows `property` suggestions.' + }, + event: { + type: 'boolean', + default: true, + markdownDescription: 'When set to `false` IntelliSense never shows `event` suggestions.' + }, + operator: { + type: 'boolean', + default: true, + markdownDescription: 'When set to `false` IntelliSense never shows `operator` suggestions.' + }, + unit: { + type: 'boolean', + default: true, + markdownDescription: 'When set to `false` IntelliSense never shows `unit` suggestions.' + }, + value: { + type: 'boolean', + default: true, + markdownDescription: 'When set to `false` IntelliSense never shows `value` suggestions.' + }, + constant: { + type: 'boolean', + default: true, + markdownDescription: 'When set to `false` IntelliSense never shows `constant` suggestions.' + }, + enum: { + type: 'boolean', + default: true, + markdownDescription: 'When set to `false` IntelliSense never shows `enum` suggestions.' + }, + enumMember: { + type: 'boolean', + default: true, + markdownDescription: 'When set to `false` IntelliSense never shows `enumMember` suggestions.' + }, + keyword: { + type: 'boolean', + default: true, + markdownDescription: 'When set to `false` IntelliSense never shows `keyword` suggestions.' + }, + text: { + type: 'boolean', + default: true, + markdownDescription: 'When set to `false` IntelliSense never shows `text` suggestions.' + }, + color: { + type: 'boolean', + default: true, + markdownDescription: 'When set to `false` IntelliSense never shows `color` suggestions.' + }, + file: { + type: 'boolean', + default: true, + markdownDescription: 'When set to `false` IntelliSense never shows `file` suggestions.' + }, + reference: { + type: 'boolean', + default: true, + markdownDescription: 'When set to `false` IntelliSense never shows `reference` suggestions.' + }, + customcolor: { + type: 'boolean', + default: true, + markdownDescription: 'When set to `false` IntelliSense never shows `customcolor` suggestions.' + }, + folder: { + type: 'boolean', + default: true, + markdownDescription: 'When set to `false` IntelliSense never shows `folder` suggestions.' + }, + typeParameter: { + type: 'boolean', + default: true, + markdownDescription: 'When set to `false` IntelliSense never shows `typeParameter` suggestions.' + }, + snippet: { + type: 'boolean', + default: true, + markdownDescription: 'When set to `false` IntelliSense never shows `snippet` suggestions.' + }, + } + }, + 'editor.gotoLocation.multiple': { + description: "Controls the behavior of 'Go To' commands, like Go To Definition, when multiple target locations exist.", + type: 'string', + enum: ['peek', 'gotoAndPeek', 'goto'], + default: EDITOR_DEFAULTS.contribInfo.gotoLocation.multiple, + enumDescriptions: [ + 'Show peek view of the results (default)', + 'Go to the primary result and show a peek view', + 'Go to the primary result and enable peek-less navigation to others' + ] + }, + 'editor.selectionHighlight': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.contribInfo.selectionHighlight, + 'description': 'Controls whether the editor should highlight matches similar to the selection.' + }, + 'editor.occurrencesHighlight': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.contribInfo.occurrencesHighlight, + 'description': 'Controls whether the editor should highlight semantic symbol occurrences.' + }, + 'editor.overviewRulerLanes': { + 'type': 'integer', + 'default': 3, + 'description': 'Controls the number of decorations that can show up at the same position in the overview ruler.' + }, + 'editor.overviewRulerBorder': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.viewInfo.overviewRulerBorder, + 'description': 'Controls whether a border should be drawn around the overview ruler.' + }, + 'editor.cursorBlinking': { + 'type': 'string', + 'enum': ['blink', 'smooth', 'phase', 'expand', 'solid'], + 'default': 'blink', + 'description': 'Control the cursor animation style.' + }, + 'editor.mouseWheelZoom': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.viewInfo.mouseWheelZoom, + 'markdownDescription': 'Zoom the font of the editor when using mouse wheel and holding `Ctrl`.' + }, + 'editor.cursorSmoothCaretAnimation': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.viewInfo.cursorSmoothCaretAnimation, + 'description': 'Controls whether the smooth caret animation should be enabled.' + }, + 'editor.cursorStyle': { + 'type': 'string', + 'enum': ['block', 'block-outline', 'line', 'line-thin', 'underline', 'underline-thin'], + 'default': 'line', + 'description': 'Controls the cursor style.' + }, + 'editor.cursorWidth': { + 'type': 'integer', + 'default': EDITOR_DEFAULTS.viewInfo.cursorWidth, + 'markdownDescription': 'Controls the width of the cursor when `#editor.cursorStyle#` is set to `line`.' + }, + 'editor.fontLigatures': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.viewInfo.fontLigatures, + 'description': 'Enables/Disables font ligatures.' + }, + 'editor.hideCursorInOverviewRuler': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.viewInfo.hideCursorInOverviewRuler, + 'description': 'Controls whether the cursor should be hidden in the overview ruler.' + }, + 'editor.renderWhitespace': { + 'type': 'string', + 'enum': ['none', 'boundary', 'selection', 'all'], + 'enumDescriptions': [ + '', + 'Render whitespace characters except for single spaces between words.', + 'Render whitespace characters only on selected text.', + '' + ], + default: EDITOR_DEFAULTS.viewInfo.renderWhitespace, + description: 'Controls how the editor should render whitespace characters.' + }, + 'editor.renderControlCharacters': { + 'type': 'boolean', + default: EDITOR_DEFAULTS.viewInfo.renderControlCharacters, + description: 'Controls whether the editor should render control characters.' + }, + 'editor.renderIndentGuides': { + 'type': 'boolean', + default: EDITOR_DEFAULTS.viewInfo.renderIndentGuides, + description: 'Controls whether the editor should render indent guides.' + }, + 'editor.highlightActiveIndentGuide': { + 'type': 'boolean', + default: EDITOR_DEFAULTS.viewInfo.highlightActiveIndentGuide, + description: 'Controls whether the editor should highlight the active indent guide.' + }, + 'editor.renderLineHighlight': { + 'type': 'string', + 'enum': ['none', 'gutter', 'line', 'all'], + 'enumDescriptions': [ + '', + '', + '', + 'Highlights both the gutter and the current line.', + ], + default: EDITOR_DEFAULTS.viewInfo.renderLineHighlight, + description: 'Controls how the editor should render the current line highlight.' + }, + 'editor.codeLens': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.contribInfo.codeLens, + 'description': 'Controls whether the editor shows CodeLens.' + }, + 'editor.folding': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.contribInfo.folding, + 'description': 'Controls whether the editor has code folding enabled.' + }, + 'editor.foldingStrategy': { + 'type': 'string', + 'enum': ['auto', 'indentation'], + 'default': EDITOR_DEFAULTS.contribInfo.foldingStrategy, + 'markdownDescription': 'Controls the strategy for computing folding ranges. `auto` uses a language specific folding strategy, if available. `indentation` uses the indentation based folding strategy.' + }, + 'editor.showFoldingControls': { + 'type': 'string', + 'enum': ['always', 'mouseover'], + 'default': EDITOR_DEFAULTS.contribInfo.showFoldingControls, + 'description': 'Controls whether the fold controls on the gutter are automatically hidden.' + }, + 'editor.matchBrackets': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.contribInfo.matchBrackets, + 'description': 'Highlight matching brackets when one of them is selected.' + }, + 'editor.glyphMargin': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.viewInfo.glyphMargin, + 'description': 'Controls whether the editor should render the vertical glyph margin. Glyph margin is mostly used for debugging.' + }, + 'editor.useTabStops': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.useTabStops, + 'description': 'Inserting and deleting whitespace follows tab stops.' + }, + 'editor.trimAutoWhitespace': { + 'type': 'boolean', + 'default': EDITOR_MODEL_DEFAULTS.trimAutoWhitespace, + 'description': 'Remove trailing auto inserted whitespace.' + }, + 'editor.stablePeek': { + 'type': 'boolean', + 'default': false, + 'markdownDescription': 'Keep peek editors open even when double clicking their content or when hitting `Escape`.' + }, + 'editor.dragAndDrop': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.dragAndDrop, + 'description': 'Controls whether the editor should allow moving selections via drag and drop.' + }, + 'editor.accessibilitySupport': { + 'type': 'string', + 'enum': ['auto', 'on', 'off'], + 'enumDescriptions': [ + 'The editor will use platform APIs to detect when a Screen Reader is attached.', + 'The editor will be permanently optimized for usage with a Screen Reader.', + 'The editor will never be optimized for usage with a Screen Reader.', + ], + 'default': EDITOR_DEFAULTS.accessibilitySupport, + 'description': 'Controls whether the editor should run in a mode where it is optimized for screen readers.' + }, + 'editor.showUnused': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.showUnused, + 'description': 'Controls fading out of unused code.' + }, + 'editor.links': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.contribInfo.links, + 'description': 'Controls whether the editor should detect links and make them clickable.' + }, + 'editor.colorDecorators': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.contribInfo.colorDecorators, + 'description': 'Controls whether the editor should render the inline color decorators and color picker.' + }, + 'editor.lightbulb.enabled': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.contribInfo.lightbulbEnabled, + 'description': 'Enables the code action lightbulb in the editor.' + }, + 'editor.maxTokenizationLineLength': { + 'type': 'integer', + 'default': 20_000, + 'description': 'Lines above this length will not be tokenized for performance reasons' + }, + 'editor.codeActionsOnSave': { + 'type': 'object', + 'properties': { + 'source.organizeImports': { + 'type': 'boolean', + 'description': 'Controls whether organize imports action should be run on file save.' + }, + 'source.fixAll': { + 'type': 'boolean', + 'description': 'Controls whether auto fix action should be run on file save.' + } + }, + 'additionalProperties': { + 'type': 'boolean' + }, + 'default': EDITOR_DEFAULTS.contribInfo.codeActionsOnSave, + 'description': 'Code action kinds to be run on save.' + }, + 'editor.codeActionsOnSaveTimeout': { + 'type': 'number', + 'default': EDITOR_DEFAULTS.contribInfo.codeActionsOnSaveTimeout, + 'description': 'Timeout in milliseconds after which the code actions that are run on save are cancelled.' + }, + 'editor.selectionClipboard': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.contribInfo.selectionClipboard, + 'description': 'Controls whether the Linux primary clipboard should be supported.', + 'included': platform.isLinux + }, + 'diffEditor.renderSideBySide': { + 'type': 'boolean', + 'default': true, + 'description': 'Controls whether the diff editor shows the diff side by side or inline.' + }, + 'diffEditor.ignoreTrimWhitespace': { + 'type': 'boolean', + 'default': true, + 'description': 'Controls whether the diff editor shows changes in leading or trailing whitespace as diffs.' + }, + 'editor.largeFileOptimizations': { + 'type': 'boolean', + 'default': EDITOR_MODEL_DEFAULTS.largeFileOptimizations, + 'description': 'Special handling for large files to disable certain memory intensive features.' + }, + 'diffEditor.renderIndicators': { + 'type': 'boolean', + 'default': true, + 'description': 'Controls whether the diff editor shows +/- indicators for added/removed changes.' + } +}; +// tslint:enable:max-line-length + export const editorPreferenceSchema: PreferenceSchema = { 'type': 'object', 'scope': 'resource', 'overridable': true, 'properties': { - 'editor.tabSize': { - 'type': 'number', - 'minimum': 1, - 'default': 4, - 'description': 'Configure the tab size in the editor.' - }, - 'editor.fontSize': { - 'type': 'number', - 'default': EDITOR_FONT_DEFAULTS.fontSize, - 'description': 'Configure the editor font size.' - }, - 'editor.fontFamily': { - 'type': 'string', - 'default': EDITOR_FONT_DEFAULTS.fontFamily, - 'description': 'Controls the font family.' - }, - 'editor.lineHeight': { - 'type': 'number', - 'default': EDITOR_FONT_DEFAULTS.lineHeight, - 'description': 'Controls the line height. Use 0 to compute the line height from the font size.' - }, - 'editor.letterSpacing': { - 'type': 'number', - 'default': EDITOR_FONT_DEFAULTS.letterSpacing, - 'description': 'Controls the letter spacing in pixels.' - }, - 'editor.lineNumbers': { - 'enum': [ - 'on', - 'off', - 'relative', - 'interval' - ], - 'default': 'on', - 'description': 'Control the rendering of line numbers.' - }, - 'editor.renderWhitespace': { - 'enum': [ - 'none', - 'boundary', - 'all' - ], - 'default': 'none', - 'description': 'Control the rendering of whitespaces in the editor.' - }, + ...(codeEditorPreferenceProperties), 'editor.autoSave': { 'enum': [ 'on', @@ -107,183 +1046,6 @@ export const editorPreferenceSchema: PreferenceSchema = { 'description': 'Configure the auto save delay in milliseconds.', overridable: false }, - 'editor.rulers': { - 'type': 'array', - 'default': [], - 'description': 'Render vertical lines at the specified columns.' - }, - 'editor.wordSeparators': { - 'type': 'string', - 'default': "`~!@#$%^&*()-=+[{]}\\|;:'\",.<>/", - 'description': 'A string containing the word separators used when doing word navigation.' - }, - 'editor.glyphMargin': { - 'type': 'boolean', - 'default': true, - 'description': 'Enable the rendering of the glyph margin.' - }, - 'editor.roundedSelection': { - 'type': 'boolean', - 'default': true, - 'description': 'Render the editor selection with rounded borders.' - }, - 'editor.minimap.enabled': { - 'type': 'boolean', - 'default': false, - 'description': 'Enable or disable the minimap.' - }, - 'editor.minimap.showSlider': { - 'enum': [ - 'mouseover', - 'always' - ], - 'default': 'mouseover', - 'description': 'Controls whether the minimap slider is automatically hidden.' - }, - 'editor.minimap.renderCharacters': { - 'type': 'boolean', - 'default': true, - 'description': 'Render the actual characters on a line (as opposed to color blocks).' - }, - 'editor.minimap.maxColumn': { - 'type': 'number', - 'default': 120, - 'description': 'Limit the width of the minimap to render at most a certain number of columns.' - }, - 'editor.minimap.side': { - 'enum': [ - 'right', - 'left' - ], - 'default': 'right', - 'description': 'Control the side of the minimap in editor.' - }, - 'editor.overviewRulerLanes': { - 'type': 'number', - 'default': 2, - 'description': 'The number of vertical lanes the overview ruler should render.' - }, - 'editor.overviewRulerBorder': { - 'type': 'boolean', - 'default': true, - 'description': 'Controls if a border should be drawn around the overview ruler.' - }, - 'editor.cursorBlinking': { - 'enum': [ - 'blink', - 'smooth', - 'phase', - 'expand', - 'solid' - ], - 'default': 'blink', - 'description': "Control the cursor animation style, possible values are 'blink', 'smooth', 'phase', 'expand' and 'solid'." - }, - 'editor.mouseWheelZoom': { - 'type': 'boolean', - 'default': false, - 'description': 'Zoom the font in the editor when using the mouse wheel in combination with holding Ctrl.' - }, - 'editor.cursorStyle': { - 'enum': [ - 'line', - 'block' - ], - 'default': 'line', - 'description': "Control the cursor style, either 'block' or 'line'." - }, - 'editor.fontLigatures': { - 'type': 'boolean', - 'default': false, - 'description': 'Enable font ligatures.' - }, - 'editor.hideCursorInOverviewRuler': { - 'type': 'boolean', - 'default': false, - 'description': 'Should the cursor be hidden in the overview ruler.' - }, - 'editor.scrollBeyondLastLine': { - 'type': 'boolean', - 'default': true, - 'description': 'Enable that scrolling can go one screen size after the last line.' - }, - 'editor.wordWrap': { - 'enum': [ - 'off', - 'on', - 'wordWrapColumn', - 'bounded' - ], - 'default': 'off', - 'description': 'Control the wrapping of the editor.' - }, - 'editor.wordWrapColumn': { - 'type': 'number', - 'default': 80, - 'description': 'Control the wrapping of the editor.' - }, - 'editor.wrappingIndent': { - 'enum': [ - 'same', - 'indent', - 'deepIndent', - 'none' - ], - 'default': 'same', - 'description': 'Control indentation of wrapped lines.' - }, - 'editor.links': { - 'type': 'boolean', - 'default': true, - 'description': 'Enable detecting links and making them clickable.' - }, - 'editor.mouseWheelScrollSensitivity': { - 'type': 'number', - 'default': 1, - 'description': 'A multiplier to be used on the `deltaX` and `deltaY` of mouse wheel scroll events.' - }, - 'editor.multiCursorModifier': { - 'enum': [ - 'alt', - 'ctrlCmd' - ], - 'default': 'alt', - 'description': 'The modifier to be used to add multiple cursors with the mouse.' - }, - 'editor.accessibilitySupport': { - 'enum': [ - 'auto', - 'on', - 'off' - ], - 'default': 'auto', - 'description': "Configure the editor's accessibility support." - }, - 'editor.quickSuggestions': { - 'type': 'boolean', - 'default': true, - 'description': 'Enable quick suggestions (shadow suggestions).' - }, - 'editor.quickSuggestionsDelay': { - 'type': 'number', - 'default': 500, - 'description': 'Quick suggestions show delay (in ms).' - }, - 'editor.parameterHints': { - 'type': 'boolean', - 'default': true, - 'description': 'Enables parameter hints.' - }, - 'editor.autoClosingBrackets': { - 'type': 'boolean', - 'default': true, - 'description': 'Enable auto closing brackets.' - }, - 'editor.autoIndent': { - 'type': 'boolean', - 'default': false, - 'description': 'Enable auto indentation adjustment.' - }, 'editor.formatOnSave': { 'type': 'boolean', 'default': false, @@ -294,217 +1056,6 @@ export const editorPreferenceSchema: PreferenceSchema = { 'default': 750, 'description': 'Timeout in milliseconds after which the formatting that is run on file save is cancelled.' }, - 'editor.formatOnType': { - 'type': 'boolean', - 'default': false, - 'description': 'Enable format on type.' - }, - 'editor.formatOnPaste': { - 'type': 'boolean', - 'default': false, - 'description': 'Enable format on paste.' - }, - 'editor.dragAndDrop': { - 'type': 'boolean', - 'default': false, - 'description': 'Controls if the editor should allow to move selections via drag and drop.' - }, - 'editor.suggestOnTriggerCharacters': { - 'type': 'boolean', - 'default': true, - 'description': 'Enable the suggestion box to pop-up on trigger characters.' - }, - 'editor.acceptSuggestionOnEnter': { - 'enum': [ - 'on', - 'smart', - 'off' - ], - 'default': 'on', - 'description': 'Accept suggestions on ENTER.' - }, - 'editor.acceptSuggestionOnCommitCharacter': { - 'type': 'boolean', - 'default': true, - 'description': 'Accept suggestions on provider defined characters.' - }, - 'editor.snippetSuggestions': { - 'enum': [ - 'inline', - 'top', - 'bottom', - 'none' - ], - 'default': 'inline', - 'description': 'Enable snippet suggestions.' - }, - 'editor.emptySelectionClipboard': { - 'type': 'boolean', - 'default': true, - 'description': 'Copying without a selection copies the current line.' - }, - 'editor.wordBasedSuggestions': { - 'type': 'boolean', - 'default': true, - 'description': "Enable word based suggestions. Defaults to 'true'." - }, - 'editor.selectionHighlight': { - 'type': 'boolean', - 'default': true, - 'description': 'Enable selection highlight.' - }, - 'editor.occurrencesHighlight': { - 'type': 'boolean', - 'default': true, - 'description': 'Enable semantic occurrences highlight.' - }, - 'editor.codeLens': { - 'type': 'boolean', - 'default': true, - 'description': 'Show code lens.' - }, - 'editor.folding': { - 'type': 'boolean', - 'default': true, - 'description': 'Enable code folding.' - }, - 'editor.foldingStrategy': { - 'enum': [ - 'auto', - 'indentation' - ], - 'default': 'auto', - 'description': 'Selects the folding strategy.' - + '\'auto\' uses the strategies contributed for the current document, \'indentation\' uses the indentation based folding strategy. ' - }, - 'editor.showFoldingControls': { - 'enum': [ - 'mouseover', - 'always' - ], - 'default': 'mouseover', - 'description': 'Controls whether the fold actions in the gutter stay always visible or hide unless the mouse is over the gutter.' - }, - 'editor.matchBrackets': { - 'type': 'boolean', - 'default': true, - 'description': 'Enable highlighting of matching brackets.' - }, - 'editor.renderControlCharacters': { - 'type': 'boolean', - 'default': false, - 'description': 'Enable rendering of control characters.' - }, - 'editor.renderIndentGuides': { - 'type': 'boolean', - 'default': false, - 'description': 'Enable rendering of indent guides.' - }, - 'editor.renderLineHighlight': { - 'enum': [ - 'all', - 'gutter', - 'line', - 'none' - ], - 'default': 'all', - 'description': 'Enable rendering of current line highlight.' - }, - 'editor.useTabStops': { - 'type': 'boolean', - 'default': true, - 'description': 'Inserting and deleting whitespace follows tab stops.' - }, - 'editor.insertSpaces': { - 'type': 'boolean', - 'default': true, - 'description': 'Using whitespaces to replace tabs when tabbing.' - }, - 'editor.colorDecorators': { - 'type': 'boolean', - 'default': true, - 'description': 'Enable inline color decorators and color picker rendering.' - }, - 'editor.highlightActiveIndentGuide': { - 'type': 'boolean', - 'default': true, - 'description': 'Enable highlighting of the active indent guide.' - }, - 'editor.iconsInSuggestions': { - 'type': 'boolean', - 'default': true, - 'description': 'Render icons in suggestions box.' - }, - 'editor.showUnused': { - 'type': 'boolean', - 'default': true, - 'description': 'Controls fading out of unused variables.', - }, - 'editor.scrollBeyondLastColumn': { - 'type': 'number', - 'default': 5, - 'description': 'Enable that scrolling can go beyond the last column by a number of columns.' - }, - 'editor.suggestSelection': { - 'enum': [ - 'first', - 'recentlyUsed', - 'recentlyUsedByPrefix' - ], - 'default': 'first', - 'description': 'The history mode for suggestions' - }, - 'editor.fontWeight': { - 'enum': [ - 'normal', - 'bold', - 'bolder', - 'lighter', - 'initial', - 'inherit', - '100', - '200', - '300', - '400', - '500', - '600', - '700', - '800', - '900' - ], - 'default': EDITOR_FONT_DEFAULTS.fontWeight, - 'description': 'Controls the editor\'s font weight.' - }, - 'diffEditor.renderSideBySide': { - 'type': 'boolean', - 'description': 'Render the differences in two side-by-side editors.', - 'default': true - }, - 'diffEditor.ignoreTrimWhitespace': { - 'type': 'boolean', - 'description': 'Compute the diff by ignoring leading/trailing whitespace.', - 'default': true - }, - 'diffEditor.renderIndicators': { - 'type': 'boolean', - 'description': 'Render +/- indicators for added/deleted changes.', - 'default': true - }, - 'diffEditor.followsCaret': { - 'type': 'boolean', - 'description': 'Resets the navigator state when the user selects something in the editor.', - 'default': true - }, - 'diffEditor.ignoreCharChanges': { - 'type': 'boolean', - 'description': 'Jump from line to line.', - 'default': true - }, - 'diffEditor.alwaysRevealFirst': { - 'type': 'boolean', - 'description': 'Reveal first change.', - 'default': true - }, 'files.eol': { 'type': 'string', 'enum': [ @@ -528,80 +1079,19 @@ export const editorPreferenceSchema: PreferenceSchema = { } }; -export interface EditorConfiguration { - 'editor.tabSize': number - 'editor.fontFamily': string - 'editor.fontSize': number - 'editor.fontWeight'?: 'normal' | 'bold' | 'bolder' | 'lighter' | 'initial' - | 'inherit' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900' +type CodeEditorPreferenceProperties = typeof codeEditorPreferenceProperties; +export type CodeEditorConfiguration = { + [P in keyof CodeEditorPreferenceProperties]: + CodeEditorPreferenceProperties[P] extends { enum: string[] } ? + CodeEditorPreferenceProperties[P]['enum'][number] : + CodeEditorPreferenceProperties[P]['default']; +}; + +export interface EditorConfiguration extends CodeEditorConfiguration { 'editor.autoSave': 'on' | 'off' 'editor.autoSaveDelay': number - 'editor.lineNumbers'?: 'on' | 'off' - 'editor.renderWhitespace'?: 'none' | 'boundary' | 'all' - 'editor.rulers'?: number[] - 'editor.wordSeparators'?: string - 'editor.glyphMargin'?: boolean - 'editor.roundedSelection'?: boolean - 'editor.minimap.enabled'?: boolean - 'editor.minimap.showSlider'?: 'always' | 'mouseover' - 'editor.minimap.renderCharacters'?: boolean - 'editor.minimap.maxColumn'?: number - 'editor.minimap.side'?: 'right' | 'left' - 'editor.overviewRulerLanes'?: number - 'editor.overviewRulerBorder'?: boolean - 'editor.cursorBlinking'?: 'blink' | 'smooth' | 'phase' | 'expand' | 'solid' - 'editor.mouseWheelZoom'?: boolean - 'editor.cursorStyle'?: 'line' | 'block' - 'editor.fontLigatures'?: boolean - 'editor.hideCursorInOverviewRuler'?: boolean - 'editor.scrollBeyondLastLine'?: boolean - 'editor.scrollBeyondLastColumn'?: number - 'editor.wordWrap'?: 'off' | 'on' | 'wordWrapColumn' | 'bounded' - 'editor.wordWrapColumn'?: number - 'editor.wrappingIndent'?: 'none' | 'same' | 'indent' | 'deepIndent' - 'editor.links'?: boolean - 'editor.mouseWheelScrollSensitivity'?: number - 'editor.multiCursorModifier'?: 'ctrlCmd' | 'alt' - 'editor.accessibilitySupport'?: 'auto' | 'off' | 'on' - 'editor.quickSuggestions'?: boolean - 'editor.quickSuggestionsDelay'?: number - 'editor.suggestSelection'?: 'first' | 'recentlyUsed' | 'recentlyUsedByPrefix' - 'editor.iconsInSuggestions'?: boolean - 'editor.parameterHints'?: boolean - 'editor.autoClosingBrackets'?: boolean - 'editor.autoIndent'?: boolean - 'editor.formatOnType'?: boolean 'editor.formatOnSave': boolean 'editor.formatOnSaveTimeout': number - 'editor.formatOnPaste'?: boolean - 'editor.dragAndDrop'?: boolean - 'editor.suggestOnTriggerCharacters'?: boolean - 'editor.acceptSuggestionOnEnter'?: 'on' | 'smart' | 'off' - 'editor.acceptSuggestionOnCommitCharacter'?: boolean - 'editor.snippetSuggestions'?: 'top' | 'bottom' | 'inline' | 'none' - 'editor.emptySelectionClipboard'?: boolean - 'editor.wordBasedSuggestions'?: boolean - 'editor.selectionHighlight'?: boolean - 'editor.occurrencesHighlight'?: boolean - 'editor.codeLens'?: boolean - 'editor.folding'?: boolean - 'editor.foldingStrategy'?: 'auto' | 'indentation' - 'editor.showFoldingControls'?: 'always' | 'mouseover' - 'editor.matchBrackets'?: boolean - 'editor.renderControlCharacters'?: boolean - 'editor.renderIndentGuides'?: boolean - 'editor.highlightActiveIndentGuide'?: boolean - 'editor.renderLineHighlight'?: 'none' | 'gutter' | 'line' | 'all' - 'editor.useTabStops'?: boolean - 'editor.insertSpaces': boolean - 'editor.colorDecorators'?: boolean - 'editor.showUnused'?: boolean - 'diffEditor.renderSideBySide'?: boolean - 'diffEditor.ignoreTrimWhitespace'?: boolean - 'diffEditor.renderIndicators'?: boolean - 'diffEditor.followsCaret'?: boolean - 'diffEditor.ignoreCharChanges'?: boolean - 'diffEditor.alwaysRevealFirst'?: boolean 'files.eol': EndOfLinePreference 'files.encoding': string } diff --git a/packages/editor/src/browser/editor.ts b/packages/editor/src/browser/editor.ts index fd2df1b74e974..5df498682c17f 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/java-debug/src/browser/java-debug-frontend-contribution.ts b/packages/java-debug/src/browser/java-debug-frontend-contribution.ts index 1fec3f790a2e9..64cb7cfc41258 100644 --- a/packages/java-debug/src/browser/java-debug-frontend-contribution.ts +++ b/packages/java-debug/src/browser/java-debug-frontend-contribution.ts @@ -148,45 +148,46 @@ export class JavaDebugFrontendContribution implements FrontendApplicationContrib protected readonly toDisposeRunDebugCodeLens = new DisposableCollection(); protected updateRunDebugCodeLens(): void { - if (this.preferences['java.debug.settings.enableRunDebugCodeLens'] && this.toDisposeRunDebugCodeLens.disposed) { - if (this.languages.registerCodeLensProvider) { - this.toDisposeRunDebugCodeLens.push(this.languages.registerCodeLensProvider([{ language: 'java' }], { - provideCodeLenses: async params => { - if (!this.commands.isEnabled(JavaDebugCommands.RESOLVE_MAIN_METHOD)) { - return []; - } - try { - const uri = params.textDocument.uri; - const mainMethods = await this.commands.executeCommand(JavaDebugCommands.RESOLVE_MAIN_METHOD, uri) || []; - return _.flatten(mainMethods.map(method => [ - { - range: method.range, - command: { - title: '▶ Run', - command: JavaDebugCommands.RUN.id, - arguments: [method.mainClass, method.projectName, uri] - } - }, - { - range: method.range, - command: { - title: '🐞 Debug', - command: JavaDebugCommands.DEBUG.id, - arguments: [method.mainClass, method.projectName, uri] - } - } - ])); - } catch (e) { - console.error(e); - return []; + if (!this.preferences['java.debug.settings.enableRunDebugCodeLens']) { + this.toDisposeRunDebugCodeLens.dispose(); + return; + } + if (!this.languages.registerCodeLensProvider || !this.toDisposeRunDebugCodeLens.disposed) { + return; + } + this.toDisposeRunDebugCodeLens.push(this.languages.registerCodeLensProvider([{ language: 'java' }], { + provideCodeLenses: async params => { + if (!this.commands.isEnabled(JavaDebugCommands.RESOLVE_MAIN_METHOD)) { + return []; + } + try { + const uri = params.textDocument.uri; + const mainMethods = await this.commands.executeCommand(JavaDebugCommands.RESOLVE_MAIN_METHOD, uri) || []; + return _.flatten(mainMethods.map(method => [ + { + range: method.range, + command: { + title: '▶ Run', + command: JavaDebugCommands.RUN.id, + arguments: [method.mainClass, method.projectName, uri] + } + }, + { + range: method.range, + command: { + title: '🐞 Debug', + command: JavaDebugCommands.DEBUG.id, + arguments: [method.mainClass, method.projectName, uri] + } } + ])); + } catch (e) { + console.error(e); + return []; + } - } - })); } - } else { - this.toDisposeRunDebugCodeLens.dispose(); - } + })); } protected async runProgram(mainClass: string, projectName: string, uri: string, noDebug: boolean = true): Promise { diff --git a/packages/languages/package.json b/packages/languages/package.json index 68289e8e06dd0..be31781904706 100644 --- a/packages/languages/package.json +++ b/packages/languages/package.json @@ -7,9 +7,9 @@ "@theia/output": "^0.10.0", "@theia/process": "^0.10.0", "@theia/workspace": "^0.10.0", - "@typefox/monaco-editor-core": "^0.14.6", + "@typefox/monaco-editor-core": "^0.18.0-next", "@types/uuid": "^3.4.3", - "monaco-languageclient": "next", + "monaco-languageclient": "^0.10.2", "uuid": "^3.2.1" }, "publishConfig": { diff --git a/packages/languages/src/browser/workspace-symbols.ts b/packages/languages/src/browser/workspace-symbols.ts index 8f64b1708654e..aa0adcd5d3433 100644 --- a/packages/languages/src/browser/workspace-symbols.ts +++ b/packages/languages/src/browser/workspace-symbols.ts @@ -94,7 +94,8 @@ export class WorkspaceSymbolCommand implements QuickOpenModel, CommandContributi const workspaceProviderPromises = []; for (const provider of this.languages.workspaceSymbolProviders) { - workspaceProviderPromises.push(provider.provideWorkspaceSymbols(param, newCancellationSource.token).then(symbols => { + workspaceProviderPromises.push((async () => { + const symbols = await provider.provideWorkspaceSymbols(param, newCancellationSource.token); if (symbols && !newCancellationSource.token.isCancellationRequested) { for (const symbol of symbols) { items.push(this.createItem(symbol, provider, newCancellationSource.token)); @@ -102,10 +103,10 @@ export class WorkspaceSymbolCommand implements QuickOpenModel, CommandContributi acceptor(items); } return symbols; - })); + })()); } Promise.all(workspaceProviderPromises.map(p => p.then(sym => sym, _ => undefined))).then(symbols => { - const filteredSymbols = symbols.filter(el => el !== undefined && el.length !== 0); + const filteredSymbols = symbols.filter(el => el && el.length !== 0); if (filteredSymbols.length === 0) { items.push(new QuickOpenItem({ label: lookFor.length === 0 ? 'Type to search for symbols' : 'No symbols matching', diff --git a/packages/monaco/package.json b/packages/monaco/package.json index 5218a4aa49b08..8d9d5f5eb3bd7 100644 --- a/packages/monaco/package.json +++ b/packages/monaco/package.json @@ -12,8 +12,8 @@ "@theia/workspace": "^0.10.0", "deepmerge": "2.0.1", "jsonc-parser": "^2.0.2", - "monaco-css": "^2.0.1", - "monaco-html": "^2.0.2", + "monaco-css": "^2.5.0", + "monaco-html": "^2.5.2", "onigasm": "2.2.1", "vscode-textmate": "^4.0.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..6ba4815402801 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 { @@ -57,22 +56,17 @@ export namespace MonacoCommands { export const SELECTION_ADD_PREVIOUS_OCCURRENCE = 'editor.action.addSelectionToPreviousFindMatch'; export const SELECTION_SELECT_ALL_OCCURRENCES = 'editor.action.selectHighlights'; - export const ACTIONS: MonacoCommand[] = [ - { id: SELECTION_SELECT_ALL, label: 'Select All', delegate: 'editor.action.selectAll' } - ]; + export const ACTIONS = new Map(); + ACTIONS.set(SELECTION_SELECT_ALL, { id: SELECTION_SELECT_ALL, label: 'Select All', delegate: 'editor.action.selectAll' }); export const EXCLUDE_ACTIONS = new Set([ ...Object.keys(COMMON_ACTIONS), 'editor.action.quickCommand', 'editor.action.clipboardCutAction', 'editor.action.clipboardCopyAction', - 'editor.action.clipboardPasteAction', - 'editor.action.goToImplementation', - 'editor.action.toggleTabFocusMode', - 'find.history.showNext', - 'find.history.showPrevious', + 'editor.action.clipboardPasteAction' ]); 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); } @@ -82,7 +76,13 @@ export namespace MonacoCommands { if (!EXCLUDE_ACTIONS.has(id)) { const label = command.label; const iconClass = iconClasses.get(id); - ACTIONS.push({ id, label, iconClass }); + ACTIONS.set(id, { id, label, iconClass }); + } + } + for (const keybinding of monaco.keybindings.KeybindingsRegistry.getDefaultKeybindings()) { + const id = keybinding.command; + if (!ACTIONS.has(id) && !EXCLUDE_ACTIONS.has(id)) { + ACTIONS.set(id, { id, delegate: id }); } } } @@ -239,7 +239,7 @@ export class MonacoEditorCommandHandlers implements CommandContribution { } protected registerMonacoActionCommands(): void { - for (const action of MonacoCommands.ACTIONS) { + for (const action of MonacoCommands.ACTIONS.values()) { const handler = this.newMonacoActionHandler(action); this.registry.registerCommand(action, handler); } @@ -251,7 +251,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-configurations.ts b/packages/monaco/src/browser/monaco-configurations.ts index 15cf514c29985..f2fd1e0db68d3 100644 --- a/packages/monaco/src/browser/monaco-configurations.ts +++ b/packages/monaco/src/browser/monaco-configurations.ts @@ -22,11 +22,15 @@ import { Configurations, ConfigurationChangeEvent, WorkspaceConfiguration } from import { Event, Emitter } from '@theia/core/lib/common'; import { PreferenceServiceImpl, PreferenceChanges } from '@theia/core/lib/browser'; +export interface MonacoConfigurationChangeEvent extends ConfigurationChangeEvent { + affectedSections?: string[] +} + @injectable() export class MonacoConfigurations implements Configurations { - protected readonly onDidChangeConfigurationEmitter = new Emitter(); - readonly onDidChangeConfiguration: Event = this.onDidChangeConfigurationEmitter.event; + protected readonly onDidChangeConfigurationEmitter = new Emitter(); + readonly onDidChangeConfiguration: Event = this.onDidChangeConfigurationEmitter.event; @inject(PreferenceServiceImpl) protected readonly preferences: PreferenceServiceImpl; @@ -42,6 +46,7 @@ export class MonacoConfigurations implements Configurations { protected reconcileData(changes?: PreferenceChanges): void { this.tree = MonacoConfigurations.parse(this.preferences.getPreferences()); this.onDidChangeConfigurationEmitter.fire({ + affectedSections: MonacoConfigurations.parseSections(changes), affectsConfiguration: section => this.affectsConfiguration(section, changes) }); } @@ -65,6 +70,20 @@ export class MonacoConfigurations implements Configurations { } export namespace MonacoConfigurations { + export function parseSections(changes?: PreferenceChanges): string[] | undefined { + if (!changes) { + return undefined; + } + const sections = []; + for (let key of Object.keys(changes)) { + while (key) { + sections.push(key); + const index = key.lastIndexOf('.'); + key = key.substring(0, index); + } + } + return sections; + } export function parse(raw: { [section: string]: Object | undefined }): JSONObject { const tree = {}; for (const section of Object.keys(raw)) { 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 cc87ce729f39a..e6d6b409bcbe4 100644 --- a/packages/monaco/src/browser/monaco-editor-model.ts +++ b/packages/monaco/src/browser/monaco-editor-model.ts @@ -206,8 +206,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..5188c4bca0338 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 @@ -327,12 +334,14 @@ export class MonacoEditorProvider { return; } - referencesController._widget.show(range); - referencesController._widget.focus(); + if (referencesController._widget) { + referencesController._widget.show(range); + referencesController._widget.focus(); + } }, (e: any) => { referencesController._ignoreModelChangeEvent = false; - throw e; + monaco.error.onUnexpectedError(e); }); }; } @@ -360,8 +369,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 2d30c84631cd3..91a5934286d5c 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, @@ -43,11 +45,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(); @@ -73,11 +89,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, @@ -150,13 +166,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 }); @@ -187,7 +204,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); } @@ -201,7 +218,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) { @@ -256,8 +273,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 { @@ -282,22 +301,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 { @@ -362,7 +380,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; @@ -386,12 +404,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 { @@ -420,7 +437,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)); } @@ -460,7 +477,7 @@ export class MonacoEditor implements TextEditor { } storeViewState(): object { - return this.editor.saveViewState(); + return this.editor.saveViewState()!; } restoreViewState(state: object): void { @@ -475,11 +492,11 @@ export class MonacoEditor implements TextEditor { } async detectLanguage(): Promise { - const filename = this.uri.path.toString(); const modeService = monaco.services.StaticServices.modeService.get(); const firstLine = this.document.textEditorModel.getLineContent(1); - const mode = await modeService.getOrCreateModeByFilenameOrFirstLine(filename, firstLine); - this.setLanguage(mode.getId()); + const model = this.getControl().getModel(); + const language = modeService.createByFilepathOrFirstLine(model && model.uri, firstLine); + this.setLanguage(language.languageIdentifier.language); this._languageAutoDetected = true; } diff --git a/packages/monaco/src/browser/monaco-frontend-module.ts b/packages/monaco/src/browser/monaco-frontend-module.ts index 668e6e85cbf1e..eb331df910a17 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 => { @@ -160,10 +162,12 @@ export function createMonacoConfigurationService(container: interfaces.Container initFromConfiguration(); } }); - preferences.onPreferencesChanged(changes => { - const event = new monaco.services.ConfigurationChangeEvent(); - event.change(Object.keys(changes)); - service._onDidChangeConfiguration.fire(event); + configurations.onDidChangeConfiguration(e => { + if (e.affectedSections) { + const event = new monaco.services.ConfigurationChangeEvent(); + event.change(e.affectedSections); + service._onDidChangeConfiguration.fire(event); + } }); return service; diff --git a/packages/monaco/src/browser/monaco-keybinding.ts b/packages/monaco/src/browser/monaco-keybinding.ts index 816bfe908e435..9d43498e02b11 100644 --- a/packages/monaco/src/browser/monaco-keybinding.ts +++ b/packages/monaco/src/browser/monaco-keybinding.ts @@ -43,14 +43,11 @@ 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 when = item.when && item.when.serialize(); + 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 - ? EditorKeybindingContexts.diffEditorTextFocus - : EditorKeybindingContexts.strictEditorTextFocus; - registry.registerKeybinding({ command, keybinding, context }); + registry.registerKeybinding({ command, keybinding, when }); } } @@ -91,9 +88,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..c82fa96aa7d52 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,77 @@ 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 */); + if (!help) { + return undefined; + } + 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); + if (!actions) { + return undefined!; + } + 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); + if (!lenses) { + return undefined; + } + return { + lenses: this.p2m.asCodeLenses(lenses), + dispose: () => { } + }; + }, + resolveCodeLens: provider.resolveCodeLens ? async (model, codeLens, token) => { + if (!this.matchModel(selector, MonacoModelIdentifier.fromModel(model))) { + return codeLens; + } + const protocolCodeLens = this.m2p.asCodeLens(codeLens); + const result = await provider.resolveCodeLens!(protocolCodeLens, token); + if (result) { + const resolvedCodeLens = this.p2m.asCodeLens(result); + Object.assign(codeLens, resolvedCodeLens); + } + return codeLens; + } : ((_, codeLens, __) => codeLens) + }; + } + } diff --git a/packages/monaco/src/browser/monaco-loader.ts b/packages/monaco/src/browser/monaco-loader.ts index b6e7fd0814474..33304501f6aef 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,42 +64,39 @@ 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', 'vs/editor/browser/services/codeEditorService', 'vs/editor/browser/services/codeEditorServiceImpl', 'vs/platform/contextkey/common/contextkey', - 'vs/platform/contextkey/browser/contextKeyService' + 'vs/platform/contextkey/browser/contextKeyService', + 'vs/base/common/errors' ], (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) => { + contextKey: any, contextKeyService: any, + error: any) => { const global: any = self; global.monaco.commands = commands; 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; global.monaco.mime = mime; + global.monaco.error = error; resolve(); }); }); 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 8b62e9312b273..c43fc58483cf7 100644 --- a/packages/monaco/src/browser/monaco-snippet-suggest-provider.ts +++ b/packages/monaco/src/browser/monaco-snippet-suggest-provider.ts @@ -25,7 +25,7 @@ import { FileSystem, FileSystemError } from '@theia/filesystem/lib/common'; import { CompletionTriggerKind } from '@theia/languages/lib/browser'; @injectable() -export class MonacoSnippetSuggestProvider implements monaco.modes.ISuggestSupport { +export class MonacoSnippetSuggestProvider implements monaco.languages.CompletionItemProvider { private static readonly _maxPrefix = 10000; @@ -36,7 +36,7 @@ export class MonacoSnippetSuggestProvider implements monaco.modes.ISuggestSuppor protected readonly pendingSnippets = new Map[]>(); async provideCompletionItems(model: monaco.editor.ITextModel, position: monaco.Position, - context: monaco.modes.SuggestContext): Promise { + context: monaco.languages.CompletionContext): Promise { // copied and modified from https://github.com/microsoft/vscode/blob/master/src/vs/workbench/contrib/snippets/browser/snippetCompletionProvider.ts if (position.column >= MonacoSnippetSuggestProvider._maxPrefix) { @@ -83,8 +83,7 @@ export class MonacoSnippetSuggestProvider implements monaco.modes.ISuggestSuppor for (const start of lineOffsets) { availableSnippets.forEach(snippet => { if (this.isPatternInWord(linePrefixLow, start, linePrefixLow.length, snippet.prefix.toLowerCase(), 0, snippet.prefix.length)) { - suggestions.push(new MonacoSnippetSuggestion(snippet, - monaco.Range.fromPositions(this.newPositionWithDelta(position, 0, -(linePrefixLow.length - start)), position))); + suggestions.push(new MonacoSnippetSuggestion(snippet, monaco.Range.fromPositions(position.delta(0, -(linePrefixLow.length - start)), position))); availableSnippets.delete(snippet); } }); @@ -102,7 +101,7 @@ export class MonacoSnippetSuggestProvider implements monaco.modes.ISuggestSuppor return { suggestions }; } - resolveCompletionItem(textModel: monaco.editor.ITextModel, position: monaco.Position, item: monaco.modes.ISuggestion): monaco.modes.ISuggestion { + resolveCompletionItem(textModel: monaco.editor.ITextModel, position: monaco.Position, item: monaco.languages.CompletionItem): monaco.languages.CompletionItem { return item instanceof MonacoSnippetSuggestion ? item.resolve() : item; } @@ -199,7 +198,7 @@ export class MonacoSnippetSuggestProvider implements monaco.modes.ISuggestSuppor } } - isPatternInWord(patternLow: string, patternPos: number, patternLen: number, wordLow: string, wordPos: number, wordLen: number): boolean { + protected isPatternInWord(patternLow: string, patternPos: number, patternLen: number, wordLow: string, wordPos: number, wordLen: number): boolean { while (patternPos < patternLen && wordPos < wordLen) { if (patternLow[patternPos] === wordLow[wordPos]) { patternPos += 1; @@ -209,16 +208,6 @@ export class MonacoSnippetSuggestProvider implements monaco.modes.ISuggestSuppor return patternPos === patternLen; // pattern must be exhausted } - newPositionWithDelta(position: monaco.Position, deltaLineNumber: number = 0, deltaColumn: number = 0): monaco.Position { - const newLineNumber = position.lineNumber + deltaLineNumber; - const newColumn = position.column + deltaColumn; - if (newLineNumber === position.lineNumber && newColumn === position.column) { - return position; - } else { - return new monaco.Position(newLineNumber, newColumn); - } - } - } export interface SnippetLoadOptions { @@ -250,27 +239,27 @@ 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 range: monaco.IRange; readonly noAutoAccept = true; - readonly type: 'snippet' = 'snippet'; - readonly snippetType: 'textmate' = 'textmate'; - readonly overwriteBefore?: number; + readonly kind = monaco.languages.CompletionItemKind.Snippet; + readonly insertTextRules = monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet; insertText: string; documentation?: monaco.IMarkdownString; - constructor(protected readonly snippet: Snippet, range: monaco.IRange) { + 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; this.sortText = `z-${snippet.prefix}`; this.range = range; - this.overwriteBefore = this.range.endColumn - this.range.startColumn; } protected resolved = false; 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 181d75ad246c0..2ede3881a36f7 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 d8b14b011d7fa..27fe43eb2bb01 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. @@ -204,9 +208,10 @@ declare module monaco.editor { dark?: IThemeDecorationInstanceRenderOptions; } + // https://github.com/TypeFox/vscode/blob/70b8db24a37fafc77247de7f7cb5bb0195120ed0/src/vs/editor/common/editorCommon.ts#L552 export interface IContentDecorationRenderOptions { contentText?: string; - contentIconPath?: string | UriComponents; + contentIconPath?: UriComponents; border?: string; borderColor?: string | ThemeColor; @@ -231,6 +236,7 @@ declare module monaco.editor { dark?: IThemeDecorationRenderOptions; } + // https://github.com/TypeFox/vscode/blob/70b8db24a37fafc77247de7f7cb5bb0195120ed0/src/vs/editor/common/editorCommon.ts#L517 export interface IThemeDecorationRenderOptions { backgroundColor?: string | ThemeColor; @@ -251,10 +257,10 @@ declare module monaco.editor { textDecoration?: string; cursor?: string; color?: string | ThemeColor; - opacity?: number; + opacity?: string; letterSpacing?: string; - gutterIconPath?: string | UriComponents; + gutterIconPath?: UriComponents; gutterIconSize?: string; overviewRulerColor?: string | ThemeColor; @@ -273,21 +279,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 +303,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 +326,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 +342,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 +356,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 +366,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 +406,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 +431,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 +446,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 +455,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 { @@ -557,36 +487,20 @@ declare module monaco.services { a: number; } - export enum LanguageId { - Null = 0, - PlainText = 1 - } - export class LanguageIdentifier { - /** - * A string identifier. Unique across languages. e.g. 'javascript'. - */ readonly language: string; - - /** - * A numeric identifier. Unique across languages. e.g. 5 - * Will vary at runtime based on registration order, etc. - */ - readonly id: LanguageId; } + // https://github.com/TypeFox/vscode/blob/70b8db24a37fafc77247de7f7cb5bb0195120ed0/src/vs/editor/common/services/modeService.ts#L30 export interface IModeService { - getOrCreateModeByFilenameOrFirstLine(filename: string, firstLine?: string): monaco.Promise; + createByFilepathOrFirstLine(rsource: monaco.Uri | null, firstLine?: string): ILanguageSelection } - export interface IMode { - - getId(): string; - - getLanguageIdentifier(): LanguageIdentifier; - + export interface ILanguageSelection { + readonly languageIdentifier: LanguageIdentifier; } + export interface ServiceCollection { set(id: any, instanceOrDescriptor: T): T; } @@ -734,11 +648,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 +738,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 +810,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 +837,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; } @@ -1135,15 +925,19 @@ 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; } +declare module monaco.error { + export function onUnexpectedError(e: any): undefined; +} + /** * overloading languages register functions to accept LanguageSelector, * check that all register functions passing a selector to registries: - * https://github.com/TypeFox/vscode/blob/c1dd5c86b7e1e55223831b1beb73f017bb19af94/src/vs/editor/standalone/browser/standaloneLanguages.ts#L325 + * https://github.com/TypeFox/vscode/blob/70b8db24a37fafc77247de7f7cb5bb0195120ed0/src/vs/editor/standalone/browser/standaloneLanguages.ts#L335-L497 */ declare module monaco.languages { export function registerReferenceProvider(selector: monaco.modes.LanguageSelector, provider: ReferenceProvider): IDisposable; @@ -1164,4 +958,6 @@ declare module monaco.languages { export function registerCompletionItemProvider(selector: monaco.modes.LanguageSelector, provider: CompletionItemProvider): IDisposable; export function registerColorProvider(selector: monaco.modes.LanguageSelector, provider: DocumentColorProvider): IDisposable; export function registerFoldingRangeProvider(selector: monaco.modes.LanguageSelector, provider: FoldingRangeProvider): IDisposable; + export function registerDeclarationProvider(selector: monaco.modes.LanguageSelector, provider: FoldingRangeProvider): IDisposable; + export function registerSelectionRangeProvider(selector: monaco.modes.LanguageSelector, provider: SelectionRangeProvider): IDisposable; } 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 fa219971e5d4f..efe985f045303 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; @@ -191,6 +198,7 @@ export interface CompletionDto extends Completion { } export interface CompletionResultDto extends IdObject { + id: number; completions: CompletionDto[]; incomplete?: boolean; } @@ -229,7 +237,7 @@ export enum MarkerTag { } export interface ParameterInformation { - label: string; + label: string | [number, number]; documentation?: string | MarkdownString; } @@ -239,12 +247,19 @@ export interface SignatureInformation { parameters: ParameterInformation[]; } -export interface SignatureHelp { +export interface SignatureHelp extends IdObject { signatures: SignatureInformation[]; activeSignature: number; activeParameter: number; } +export interface SignatureHelpContext { + triggerKind: theia.SignatureHelpTriggerKind; + triggerCharacter?: string; + isRetrigger: boolean; + activeSignatureHelp?: SignatureHelp; +} + export interface Hover { contents: MarkdownString[]; range?: Range; @@ -312,7 +327,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 4ca92b9c3f04f..c1a2cace8f09e 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, @@ -61,7 +60,8 @@ import { ColorPresentation, RenameLocation, FileMoveEvent, - FileWillMoveEvent + FileWillMoveEvent, + SignatureHelpContext } from './plugin-api-rpc-model'; import { ExtPluginApi } from './plugin-ext-api-contribution'; import { KeysToAnyValues, KeysToKeysToAnyValue } from './types'; @@ -793,7 +793,7 @@ export enum TrackedRangeStickiness { } export interface ContentDecorationRenderOptions { contentText?: string; - contentIconPath?: string | UriComponents; + contentIconPath?: UriComponents; border?: string; borderColor?: string | ThemeColor; @@ -828,10 +828,10 @@ export interface ThemeDecorationRenderOptions { textDecoration?: string; cursor?: string; color?: string | ThemeColor; - opacity?: number; + opacity?: string; letterSpacing?: string; - gutterIconPath?: string | UriComponents; + gutterIconPath?: UriComponents; gutterIconSize?: string; overviewRulerColor?: string | ThemeColor; @@ -1095,13 +1095,16 @@ export interface LanguagesExt { $provideTypeDefinition(handle: number, resource: UriComponents, position: Position, token: CancellationToken): Promise; $provideDefinition(handle: number, resource: UriComponents, position: Position, token: CancellationToken): Promise; $provideReferences(handle: number, resource: UriComponents, position: Position, context: ReferenceContext, token: CancellationToken): Promise; - $provideSignatureHelp(handle: number, resource: UriComponents, position: Position, token: CancellationToken): Promise; + $provideSignatureHelp( + handle: number, resource: UriComponents, position: Position, context: SignatureHelpContext, token: CancellationToken + ): Promise; + $releaseSignatureHelp(handle: number, id: number): void; $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, @@ -1109,7 +1112,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; @@ -1120,7 +1123,7 @@ export interface LanguagesExt { rangeOrSelection: Range | Selection, context: monaco.languages.CodeActionContext, token: CancellationToken - ): Promise; + ): Promise; $provideDocumentSymbols(handle: number, resource: UriComponents, token: CancellationToken): Promise; $provideWorkspaceSymbols(handle: number, query: string, token: CancellationToken): PromiseLike; $resolveWorkspaceSymbol(handle: number, symbol: SymbolInformation, token: CancellationToken): PromiseLike; @@ -1146,7 +1149,7 @@ export interface LanguagesMain { $registerTypeDefinitionProvider(handle: number, selector: SerializedDocumentFilter[]): void; $registerDefinitionProvider(handle: number, selector: SerializedDocumentFilter[]): void; $registerReferenceProvider(handle: number, selector: SerializedDocumentFilter[]): void; - $registerSignatureHelpProvider(handle: number, selector: SerializedDocumentFilter[], triggerCharacters: string[]): void; + $registerSignatureHelpProvider(handle: number, selector: SerializedDocumentFilter[], metadata: theia.SignatureHelpProviderMetadata): void; $registerHoverProvider(handle: number, selector: SerializedDocumentFilter[]): void; $registerDocumentHighlightProvider(handle: number, selector: SerializedDocumentFilter[]): void; $registerQuickFixProvider(handle: number, selector: SerializedDocumentFilter[], codeActionKinds?: string[]): void; 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 453089c356f2d..09faf25368b6d 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 { DisposableCollection, Emitter } from '@theia/core'; @@ -44,6 +44,7 @@ import URI from 'vscode-uri/lib/umd'; import CoreURI from '@theia/core/lib/common/uri'; import { ProblemManager } from '@theia/markers/lib/browser'; import * as vst from 'vscode-languageserver-types'; +import * as theia from '@theia/plugin'; export class LanguagesMainImpl implements LanguagesMain { @@ -97,21 +98,17 @@ 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 => - Promise.resolve(this.proxy.$provideCompletionItems(handle, model.uri, position, context, token)).then(result => { + provideCompletionItems: (model, position, context, token) => + this.proxy.$provideCompletionItems(handle, model.uri, position, context, token).then(result => { if (!result) { - return undefined!; + return undefined; } return { suggestions: result.completions, incomplete: result.incomplete, - // tslint:disable-next-line:no-any - dispose: () => this.proxy.$releaseCompletionItems(handle, result.id!) + dispose: () => this.proxy.$releaseCompletionItems(handle, result.id) }; }), resolveCompletionItem: supportsResolveDetails @@ -141,7 +138,7 @@ export class LanguagesMainImpl implements LanguagesMain { provideReferences: (model, position, context, token) => this.proxy.$provideReferences(handle, model.uri, position, context, token).then(result => { if (!result) { - return undefined!; + return undefined; } if (Array.isArray(result)) { @@ -152,14 +149,14 @@ export class LanguagesMainImpl implements LanguagesMain { return references; } - return undefined!; + return undefined; }) }; } - $registerSignatureHelpProvider(handle: number, selector: SerializedDocumentFilter[], triggerCharacters: string[]): void { + $registerSignatureHelpProvider(handle: number, selector: SerializedDocumentFilter[], metadata: theia.SignatureHelpProviderMetadata): void { const languageSelector = fromLanguageSelector(selector); - const signatureHelpProvider = this.createSignatureHelpProvider(handle, triggerCharacters); + const signatureHelpProvider = this.createSignatureHelpProvider(handle, metadata); const disposable = new DisposableCollection(); disposable.push(monaco.languages.registerSignatureHelpProvider(languageSelector, signatureHelpProvider)); this.disposables.set(handle, disposable); @@ -191,12 +188,12 @@ export class LanguagesMainImpl implements LanguagesMain { provideImplementation: (model, position, token) => this.proxy.$provideImplementation(handle, model.uri, position, token).then(result => { if (!result) { - return undefined!; + return undefined; } 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) }); } @@ -225,12 +222,12 @@ export class LanguagesMainImpl implements LanguagesMain { provideTypeDefinition: (model, position, token) => this.proxy.$provideTypeDefinition(handle, model.uri, position, token).then(result => { if (!result) { - return undefined!; + return undefined; } 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) }); } @@ -257,7 +254,7 @@ export class LanguagesMainImpl implements LanguagesMain { protected createHoverProvider(handle: number): monaco.languages.HoverProvider { return { provideHover: (model, position, token) => - this.proxy.$provideHover(handle, model.uri, position, token).then(v => v!) + this.proxy.$provideHover(handle, model.uri, position, token) }; } @@ -274,7 +271,7 @@ export class LanguagesMainImpl implements LanguagesMain { provideDocumentHighlights: (model, position, token) => this.proxy.$provideDocumentHighlights(handle, model.uri, position, token).then(result => { if (!result) { - return undefined!; + return undefined; } if (Array.isArray(result)) { @@ -289,7 +286,7 @@ export class LanguagesMainImpl implements LanguagesMain { return highlights; } - return undefined!; + return undefined; }) }; } @@ -318,10 +315,29 @@ export class LanguagesMainImpl implements LanguagesMain { protected createLinkProvider(handle: number): monaco.languages.LinkProvider { return { - provideLinks: (model, token) => - this.proxy.$provideDocumentLinks(handle, model.uri, token).then(v => v!), - resolveLink: (link: monaco.languages.ILink, token) => - this.proxy.$resolveDocumentLink(handle, link, token).then(v => v!) + provideLinks: async (model, token) => { + 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: 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 }; } @@ -342,11 +358,20 @@ export class LanguagesMainImpl implements LanguagesMain { protected createCodeLensProvider(handle: number): monaco.languages.CodeLensProvider { return { - provideCodeLenses: (model, token) => - this.proxy.$provideCodeLenses(handle, model.uri, token).then(v => v!) - , + provideCodeLenses: async (model, token) => { + const lenses = await this.proxy.$provideCodeLenses(handle, model.uri, token); + if (!lenses) { + return undefined; + } + return { + lenses, + dispose: () => { + // TODO this.proxy.$releaseCodeLenses + } + }; + }, resolveCodeLens: (model, codeLens, token) => - this.proxy.$resolveCodeLens(handle, model.uri, codeLens, token).then(v => v!) + this.proxy.$resolveCodeLens(handle, model.uri, codeLens, token) }; } @@ -370,7 +395,7 @@ export class LanguagesMainImpl implements LanguagesMain { protected createDocumentSymbolProvider(handle: number): monaco.languages.DocumentSymbolProvider { return { provideDocumentSymbols: (model, token) => - this.proxy.$provideDocumentSymbols(handle, model.uri, token).then(v => v!) + this.proxy.$provideDocumentSymbols(handle, model.uri, token) }; } @@ -379,12 +404,12 @@ export class LanguagesMainImpl implements LanguagesMain { provideDefinition: (model, position, token) => this.proxy.$provideDefinition(handle, model.uri, position, token).then(result => { if (!result) { - return undefined!; + return undefined; } 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) }); } @@ -400,11 +425,24 @@ export class LanguagesMainImpl implements LanguagesMain { }; } - protected createSignatureHelpProvider(handle: number, triggerCharacters: string[]): monaco.languages.SignatureHelpProvider { + protected createSignatureHelpProvider(handle: number, metadata: theia.SignatureHelpProviderMetadata): monaco.languages.SignatureHelpProvider { return { - signatureHelpTriggerCharacters: triggerCharacters, - provideSignatureHelp: (model, position, token) => - this.proxy.$provideSignatureHelp(handle, model.uri, position, token).then(v => v!) + signatureHelpTriggerCharacters: metadata.triggerCharacters, + signatureHelpRetriggerCharacters: metadata.retriggerCharacters, + provideSignatureHelp: async (model, position, token, context) => { + const value = await this.proxy.$provideSignatureHelp(handle, model.uri, position, context, token); + if (!value) { + return undefined; + } + return { + value, + dispose: () => { + if (typeof value.id === 'number') { + this.proxy.$releaseSignatureHelp(handle, value.id); + } + } + }; + } }; } @@ -419,7 +457,7 @@ export class LanguagesMainImpl implements LanguagesMain { createDocumentFormattingSupport(handle: number): monaco.languages.DocumentFormattingEditProvider { return { provideDocumentFormattingEdits: (model, options, token) => - this.proxy.$provideDocumentFormattingEdits(handle, model.uri, options, token).then(v => v!) + this.proxy.$provideDocumentFormattingEdits(handle, model.uri, options, token) }; } @@ -434,7 +472,7 @@ export class LanguagesMainImpl implements LanguagesMain { createRangeFormattingProvider(handle: number): monaco.languages.DocumentRangeFormattingEditProvider { return { provideDocumentRangeFormattingEdits: (model, range: Range, options, token) => - this.proxy.$provideDocumentRangeFormattingEdits(handle, model.uri, range, options, token).then(v => v!) + this.proxy.$provideDocumentRangeFormattingEdits(handle, model.uri, range, options, token) }; } @@ -453,7 +491,7 @@ export class LanguagesMainImpl implements LanguagesMain { return { autoFormatTriggerCharacters, provideOnTypeFormattingEdits: (model, position, ch, options, token) => - this.proxy.$provideOnTypeFormattingEdits(handle, model.uri, position, ch, options, token).then(v => v!) + this.proxy.$provideOnTypeFormattingEdits(handle, model.uri, position, ch, options, token) }; } @@ -468,7 +506,7 @@ export class LanguagesMainImpl implements LanguagesMain { createFoldingRangeProvider(handle: number): monaco.languages.FoldingRangeProvider { return { provideFoldingRanges: (model, context, token) => - this.proxy.$provideFoldingRange(handle, model.uri, context, token).then(v => v!) + this.proxy.$provideFoldingRange(handle, model.uri, context, token) }; } @@ -522,8 +560,18 @@ export class LanguagesMainImpl implements LanguagesMain { protected createQuickFixProvider(handle: number, providedCodeActionKinds?: string[]): monaco.languages.CodeActionProvider { return { - provideCodeActions: (model, rangeOrSelection, monacoContext, token) => - this.proxy.$provideCodeActions(handle, model.uri, rangeOrSelection, monacoContext, token) + provideCodeActions: async (model, rangeOrSelection, monacoContext, token) => { + const actions = await this.proxy.$provideCodeActions(handle, model.uri, rangeOrSelection, monacoContext, token); + if (!actions) { + return undefined!; + } + return { + actions, + dispose: () => { + // TODO this.proxy.$releaseCodeActions(handle, cacheId); + } + }; + } }; } @@ -539,10 +587,10 @@ export class LanguagesMainImpl implements LanguagesMain { return { provideRenameEdits: (model, position, newName, token) => this.proxy.$provideRenameEdits(handle, model.uri, position, newName, token) - .then(v => reviveWorkspaceEditDto(v!)), + .then(reviveWorkspaceEditDto), resolveRenameLocation: supportsResolveLocation ? (model, position, token) => - this.proxy.$resolveRenameLocation(handle, model.uri, position, token).then(v => v!) + this.proxy.$resolveRenameLocation(handle, model.uri, position, token) : undefined }; } 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 a60325e72acea..6bce75bfeb037 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, @@ -55,6 +55,7 @@ import { Location, ColorPresentation, RenameLocation, + SignatureHelpContext, } from '../common/plugin-api-rpc-model'; import { CompletionAdapter } from './languages/completion'; import { Diagnostics } from './languages/diagnostics'; @@ -225,7 +226,7 @@ export class LanguagesExtImpl implements LanguagesExt { } $releaseCompletionItems(handle: number, id: number): void { - this.withAdapter(handle, CompletionAdapter, adapter => adapter.releaseCompletionItems(id)); + this.withAdapter(handle, CompletionAdapter, async adapter => adapter.releaseCompletionItems(id)); } registerCompletionItemProvider(selector: theia.DocumentSelector, provider: theia.CompletionItemProvider, triggerCharacters: string[]): theia.Disposable { @@ -248,13 +249,19 @@ export class LanguagesExtImpl implements LanguagesExt { // ### Definition provider end // ### Signature help begin - $provideSignatureHelp(handle: number, resource: UriComponents, position: Position, token: theia.CancellationToken): Promise { - return this.withAdapter(handle, SignatureHelpAdapter, adapter => adapter.provideSignatureHelp(URI.revive(resource), position, token)); + $provideSignatureHelp( + handle: number, resource: UriComponents, position: Position, context: SignatureHelpContext, token: theia.CancellationToken + ): Promise { + return this.withAdapter(handle, SignatureHelpAdapter, adapter => adapter.provideSignatureHelp(URI.revive(resource), position, token, context)); } - registerSignatureHelpProvider(selector: theia.DocumentSelector, provider: theia.SignatureHelpProvider, ...triggerCharacters: string[]): theia.Disposable { + $releaseSignatureHelp(handle: number, id: number): void { + this.withAdapter(handle, SignatureHelpAdapter, async adapter => adapter.releaseSignatureHelp(id)); + } + + registerSignatureHelpProvider(selector: theia.DocumentSelector, provider: theia.SignatureHelpProvider, metadata: theia.SignatureHelpProviderMetadata): theia.Disposable { const callId = this.addNewAdapter(new SignatureHelpAdapter(provider, this.documents)); - this.proxy.$registerSignatureHelpProvider(callId, this.transformDocumentSelector(selector), triggerCharacters); + this.proxy.$registerSignatureHelpProvider(callId, this.transformDocumentSelector(selector), metadata); return this.createDisposable(callId); } // ### Signature help end @@ -341,7 +348,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 @@ -354,7 +361,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 @@ -371,7 +378,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 @@ -413,7 +420,7 @@ export class LanguagesExtImpl implements LanguagesExt { rangeOrSelection: Range | Selection, context: monaco.languages.CodeActionContext, token: theia.CancellationToken - ): Promise { + ): Promise { return this.withAdapter(handle, CodeActionAdapter, adapter => adapter.provideCodeAction(URI.revive(resource), rangeOrSelection, context, token)); } // ### Code Actions Provider end diff --git a/packages/plugin-ext/src/plugin/languages/code-action.ts b/packages/plugin-ext/src/plugin/languages/code-action.ts index 04c2646ac976e..b5b25e21ccb96 100644 --- a/packages/plugin-ext/src/plugin/languages/code-action.ts +++ b/packages/plugin-ext/src/plugin/languages/code-action.ts @@ -36,7 +36,7 @@ export class CodeActionAdapter { ) { } provideCodeAction(resource: URI, rangeOrSelection: Range | Selection, - context: monaco.languages.CodeActionContext, token: theia.CancellationToken): Promise { + context: monaco.languages.CodeActionContext, token: theia.CancellationToken): Promise { const document = this.document.getDocumentData(resource); if (!document) { return Promise.reject(new Error(`There are no document for ${resource}`)); @@ -61,7 +61,7 @@ export class CodeActionAdapter { return Promise.resolve(this.provider.provideCodeActions(doc, ran, codeActionContext, token)).then(commandsOrActions => { if (!Array.isArray(commandsOrActions) || commandsOrActions.length === 0) { - return undefined!; + return undefined; } // TODO cache toDispose and dispose it const toDispose = new DisposableCollection(); diff --git a/packages/plugin-ext/src/plugin/languages/completion.ts b/packages/plugin-ext/src/plugin/languages/completion.ts index 4a3e893d2c0af..889c7ce0783d9 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'; import { CommandRegistryImpl } from '../command-registry'; import { DisposableCollection } from '@theia/core/lib/common/disposable'; @@ -130,50 +130,40 @@ export class CompletionAdapter { throw Error('DisposableCollection is missing...'); } - 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: this.commands.converter.toSafeCommand(item.command, toDispose), 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/languages/signature.ts b/packages/plugin-ext/src/plugin/languages/signature.ts index 8ff26ec0fc7ed..4ccb80d056eeb 100644 --- a/packages/plugin-ext/src/plugin/languages/signature.ts +++ b/packages/plugin-ext/src/plugin/languages/signature.ts @@ -13,23 +13,32 @@ * * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// copied and modified from https://github.com/TypeFox/vscode/blob/70b8db24a37fafc77247de7f7cb5bb0195120ed0/src/vs/workbench/api/common/extHostLanguageFeatures.ts#L771 import URI from 'vscode-uri/lib/umd'; import * as theia from '@theia/plugin'; import { DocumentsExtImpl } from '../documents'; import * as Converter from '../type-converters'; import { Position } from '../../common/plugin-api-rpc'; -import { SignatureHelp } from '../../common/plugin-api-rpc-model'; +import { SignatureHelp, SignatureHelpContext } from '../../common/plugin-api-rpc-model'; export class SignatureHelpAdapter { + private idSequence = 1; + private readonly cache = new Map(); + constructor( private readonly delegate: theia.SignatureHelpProvider, private readonly documents: DocumentsExtImpl) { } - provideSignatureHelp(resource: URI, position: Position, token: theia.CancellationToken): Promise { + async provideSignatureHelp(resource: URI, position: Position, token: theia.CancellationToken, context: SignatureHelpContext): Promise { const documentData = this.documents.getDocumentData(resource); if (!documentData) { return Promise.reject(new Error(`There are no document for ${resource}`)); @@ -37,8 +46,35 @@ export class SignatureHelpAdapter { const document = documentData.document; const zeroBasedPosition = Converter.toPosition(position); + const pluginHelpContext = this.reviveContext(context); + + const value = await this.delegate.provideSignatureHelp(document, zeroBasedPosition, token, pluginHelpContext); + if (!value) { + return undefined; + } + const id = this.idSequence++; + this.cache.set(id, value); + return Converter.SignatureHelp.from(id, value); + } + + private reviveContext(context: SignatureHelpContext): theia.SignatureHelpContext { + let activeSignatureHelp: theia.SignatureHelp | undefined = undefined; + if (context.activeSignatureHelp) { + const revivedSignatureHelp = Converter.SignatureHelp.to(context.activeSignatureHelp); + const saved = typeof context.activeSignatureHelp.id === 'number' && this.cache.get(context.activeSignatureHelp.id); + if (saved) { + activeSignatureHelp = saved; + activeSignatureHelp.activeSignature = revivedSignatureHelp.activeSignature; + activeSignatureHelp.activeParameter = revivedSignatureHelp.activeParameter; + } else { + activeSignatureHelp = revivedSignatureHelp; + } + } + return { ...context, activeSignatureHelp }; + } - return Promise.resolve(this.delegate.provideSignatureHelp(document, zeroBasedPosition, token)); + releaseSignatureHelp(id: number): void { + this.cache.delete(id); } } diff --git a/packages/plugin-ext/src/plugin/markdown-string.ts b/packages/plugin-ext/src/plugin/markdown-string.ts index cedf7a3090843..05b634263fa55 100644 --- a/packages/plugin-ext/src/plugin/markdown-string.ts +++ b/packages/plugin-ext/src/plugin/markdown-string.ts @@ -14,6 +14,8 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ +import { MarkdownString as IMarkdownString } from '../common/plugin-api-rpc-model'; + export class MarkdownString { value: string; @@ -45,7 +47,7 @@ export class MarkdownString { } // tslint:disable-next-line:no-any -export function isMarkdownString(thing: any): thing is MarkdownString { +export function isMarkdownString(thing: any): thing is IMarkdownString { if (thing instanceof MarkdownString) { return true; } else if (thing && typeof thing === 'object') { diff --git a/packages/plugin-ext/src/plugin/plugin-context.ts b/packages/plugin-ext/src/plugin/plugin-context.ts index f20832603b181..9f4981a3b8658 100644 --- a/packages/plugin-ext/src/plugin/plugin-context.ts +++ b/packages/plugin-ext/src/plugin/plugin-context.ts @@ -70,6 +70,7 @@ import { ParameterInformation, SignatureInformation, SignatureHelp, + SignatureHelpTriggerKind, Hover, DocumentHighlightKind, DocumentHighlight, @@ -547,8 +548,20 @@ export function createAPIFactory( registerDefinitionProvider(selector: theia.DocumentSelector, provider: theia.DefinitionProvider): theia.Disposable { return languagesExt.registerDefinitionProvider(selector, provider); }, - registerSignatureHelpProvider(selector: theia.DocumentSelector, provider: theia.SignatureHelpProvider, ...triggerCharacters: string[]): theia.Disposable { - return languagesExt.registerSignatureHelpProvider(selector, provider, ...triggerCharacters); + registerSignatureHelpProvider( + selector: theia.DocumentSelector, provider: theia.SignatureHelpProvider, first?: string | theia.SignatureHelpProviderMetadata, ...remaining: string[] + ): theia.Disposable { + let metadata: theia.SignatureHelpProviderMetadata; + if (typeof first === 'object') { + metadata = first; + } else { + const triggerCharacters: string[] = []; + metadata = { triggerCharacters, retriggerCharacters: [] }; + if (first) { + triggerCharacters.push(first, ...remaining); + } + } + return languagesExt.registerSignatureHelpProvider(selector, provider, metadata); }, registerTypeDefinitionProvider(selector: theia.DocumentSelector, provider: theia.TypeDefinitionProvider): theia.Disposable { return languagesExt.registerTypeDefinitionProvider(selector, provider); @@ -793,6 +806,7 @@ export function createAPIFactory( ParameterInformation, SignatureInformation, SignatureHelp, + SignatureHelpTriggerKind, Hover, DocumentHighlightKind, DocumentHighlight, diff --git a/packages/plugin-ext/src/plugin/text-editors.ts b/packages/plugin-ext/src/plugin/text-editors.ts index 78ab6dd8eb315..e120addcd2ed4 100644 --- a/packages/plugin-ext/src/plugin/text-editors.ts +++ b/packages/plugin-ext/src/plugin/text-editors.ts @@ -141,7 +141,7 @@ export class TextEditorDecorationType implements theia.TextEditorDecorationType this.key = TextEditorDecorationType.Keys.nextId(); this.proxy = proxy; // tslint:disable-next-line:no-any - this.proxy.$registerTextEditorDecorationType(this.key, options); + this.proxy.$registerTextEditorDecorationType(this.key, Converters.DecorationRenderOptions.from(options)); } dispose(): void { diff --git a/packages/plugin-ext/src/plugin/type-converters.ts b/packages/plugin-ext/src/plugin/type-converters.ts index 49c0606c66162..bb0227dc41413 100644 --- a/packages/plugin-ext/src/plugin/type-converters.ts +++ b/packages/plugin-ext/src/plugin/type-converters.ts @@ -26,11 +26,12 @@ import { ProcessTaskDto, PickOpenItem } from '../common/plugin-api-rpc'; +import * as rpc from '../common/plugin-api-rpc'; import * as model from '../common/plugin-api-rpc-model'; import * as theia from '@theia/plugin'; import * as types from './types-impl'; import { LanguageSelector, LanguageFilter, RelativePattern } from './languages'; -import { isMarkdownString } from './markdown-string'; +import { isMarkdownString, MarkdownString } from './markdown-string'; import URI from 'vscode-uri'; const SIDE_GROUP = -2; @@ -121,6 +122,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; @@ -210,6 +214,12 @@ export function fromMarkdown(markup: theia.MarkdownString | theia.MarkedString): } } +export function toMarkdown(value: model.MarkdownString): MarkdownString { + const ret = new MarkdownString(value.value); + ret.isTrusted = value.isTrusted; + return ret; +} + export function fromDocumentSelector(selector: theia.DocumentSelector | undefined): LanguageSelector | undefined { if (!selector) { return undefined; @@ -244,72 +254,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) }; @@ -444,6 +452,60 @@ export function fromDocumentHighlight(documentHighlight: theia.DocumentHighlight }; } +export namespace ParameterInformation { + export function from(info: types.ParameterInformation): model.ParameterInformation { + return { + label: info.label, + documentation: info.documentation ? fromMarkdown(info.documentation) : undefined + }; + } + export function to(info: model.ParameterInformation): types.ParameterInformation { + return { + label: info.label, + documentation: isMarkdownString(info.documentation) ? toMarkdown(info.documentation) : info.documentation + }; + } +} + +export namespace SignatureInformation { + + export function from(info: types.SignatureInformation): model.SignatureInformation { + return { + label: info.label, + documentation: info.documentation ? fromMarkdown(info.documentation) : undefined, + parameters: info.parameters && info.parameters.map(ParameterInformation.from) + }; + } + + export function to(info: model.SignatureInformation): types.SignatureInformation { + return { + label: info.label, + documentation: isMarkdownString(info.documentation) ? toMarkdown(info.documentation) : info.documentation, + parameters: info.parameters && info.parameters.map(ParameterInformation.to) + }; + } +} + +export namespace SignatureHelp { + + export function from(id: number, help: types.SignatureHelp): model.SignatureHelp { + return { + id, + activeSignature: help.activeSignature, + activeParameter: help.activeParameter, + signatures: help.signatures && help.signatures.map(SignatureInformation.from) + }; + } + + export function to(help: model.SignatureHelp): types.SignatureHelp { + return { + activeSignature: help.activeSignature, + activeParameter: help.activeParameter, + signatures: help.signatures && help.signatures.map(SignatureInformation.to) + }; + } +} + export namespace KnownCommands { // tslint:disable: no-any const mappings: { [id: string]: [string, (args: any[] | undefined) => any[] | undefined] } = {}; @@ -895,3 +957,123 @@ export function quickPickItemToPickOpenItem(items: Item[]): PickOpenItem[] { } return pickItems; } + +export namespace DecorationRenderOptions { + export function from(options: theia.DecorationRenderOptions): rpc.DecorationRenderOptions { + return { + isWholeLine: options.isWholeLine, + rangeBehavior: options.rangeBehavior ? DecorationRangeBehavior.from(options.rangeBehavior) : undefined, + overviewRulerLane: options.overviewRulerLane, + light: options.light ? ThemableDecorationRenderOptions.from(options.light) : undefined, + dark: options.dark ? ThemableDecorationRenderOptions.from(options.dark) : undefined, + + backgroundColor: options.backgroundColor, + outline: options.outline, + outlineColor: options.outlineColor, + outlineStyle: options.outlineStyle, + outlineWidth: options.outlineWidth, + border: options.border, + borderColor: options.borderColor, + borderRadius: options.borderRadius, + borderSpacing: options.borderSpacing, + borderStyle: options.borderStyle, + borderWidth: options.borderWidth, + fontStyle: options.fontStyle, + fontWeight: options.fontWeight, + textDecoration: options.textDecoration, + cursor: options.cursor, + color: options.color, + opacity: options.opacity, + letterSpacing: options.letterSpacing, + gutterIconPath: options.gutterIconPath ? pathOrURIToURI(options.gutterIconPath) : undefined, + gutterIconSize: options.gutterIconSize, + overviewRulerColor: options.overviewRulerColor, + before: options.before ? ThemableDecorationAttachmentRenderOptions.from(options.before) : undefined, + after: options.after ? ThemableDecorationAttachmentRenderOptions.from(options.after) : undefined, + }; + } +} + +export namespace DecorationRangeBehavior { + export function from(value: types.DecorationRangeBehavior): rpc.TrackedRangeStickiness { + if (typeof value === 'undefined') { + return value; + } + switch (value) { + case types.DecorationRangeBehavior.OpenOpen: + return rpc.TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges; + case types.DecorationRangeBehavior.ClosedClosed: + return rpc.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges; + case types.DecorationRangeBehavior.OpenClosed: + return rpc.TrackedRangeStickiness.GrowsOnlyWhenTypingBefore; + case types.DecorationRangeBehavior.ClosedOpen: + return rpc.TrackedRangeStickiness.GrowsOnlyWhenTypingAfter; + } + } +} + +export namespace ThemableDecorationRenderOptions { + export function from(options: theia.ThemableDecorationRenderOptions): rpc.ThemeDecorationRenderOptions { + if (typeof options === 'undefined') { + return options; + } + return { + backgroundColor: options.backgroundColor, + outline: options.outline, + outlineColor: options.outlineColor, + outlineStyle: options.outlineStyle, + outlineWidth: options.outlineWidth, + border: options.border, + borderColor: options.borderColor, + borderRadius: options.borderRadius, + borderSpacing: options.borderSpacing, + borderStyle: options.borderStyle, + borderWidth: options.borderWidth, + fontStyle: options.fontStyle, + fontWeight: options.fontWeight, + textDecoration: options.textDecoration, + cursor: options.cursor, + color: options.color, + opacity: options.opacity, + letterSpacing: options.letterSpacing, + gutterIconPath: options.gutterIconPath ? pathOrURIToURI(options.gutterIconPath) : undefined, + gutterIconSize: options.gutterIconSize, + overviewRulerColor: options.overviewRulerColor, + before: options.before ? ThemableDecorationAttachmentRenderOptions.from(options.before) : undefined, + after: options.after ? ThemableDecorationAttachmentRenderOptions.from(options.after) : undefined, + }; + } +} + +export namespace ThemableDecorationAttachmentRenderOptions { + export function from(options: theia.ThemableDecorationAttachmentRenderOptions): rpc.ContentDecorationRenderOptions { + if (typeof options === 'undefined') { + return options; + } + return { + contentText: options.contentText, + contentIconPath: options.contentIconPath ? pathOrURIToURI(options.contentIconPath) : undefined, + border: options.border, + borderColor: options.borderColor, + fontStyle: options.fontStyle, + fontWeight: options.fontWeight, + textDecoration: options.textDecoration, + color: options.color, + backgroundColor: options.backgroundColor, + margin: options.margin, + width: options.width, + height: options.height, + }; + } +} + +export function pathOrURIToURI(value: string | URI): URI { + if (typeof value === 'undefined') { + return value; + } + if (typeof value === 'string') { + return URI.file(value); + } else { + return value; + } +} diff --git a/packages/plugin-ext/src/plugin/types-impl.ts b/packages/plugin-ext/src/plugin/types-impl.ts index efca850f5c076..83f564b11b784 100644 --- a/packages/plugin-ext/src/plugin/types-impl.ts +++ b/packages/plugin-ext/src/plugin/types-impl.ts @@ -819,10 +819,10 @@ export enum MarkerTag { } export class ParameterInformation { - label: string; + label: string | [number, number]; documentation?: string | MarkdownString; - constructor(label: string, documentation?: string | MarkdownString) { + constructor(label: string | [number, number], documentation?: string | MarkdownString) { this.label = label; this.documentation = documentation; } @@ -836,13 +836,24 @@ export class SignatureInformation { constructor(label: string, documentation?: string | MarkdownString) { this.label = label; this.documentation = documentation; + this.parameters = []; } } +export enum SignatureHelpTriggerKind { + Invoke = 1, + TriggerCharacter = 2, + ContentChange = 3, +} + export class SignatureHelp { signatures: SignatureInformation[]; activeSignature: number; activeParameter: number; + + constructor() { + this.signatures = []; + } } export class Hover { diff --git a/packages/plugin/src/theia.d.ts b/packages/plugin/src/theia.d.ts index 7c31e19b794f6..814da96a4a165 100644 --- a/packages/plugin/src/theia.d.ts +++ b/packages/plugin/src/theia.d.ts @@ -4991,7 +4991,7 @@ declare module '@theia/plugin' { * The label of this signature. Will be shown in * the UI. */ - label: string; + label: string | [number, number]; /** * The human-readable doc-comment of this signature. Will be shown @@ -5065,6 +5065,61 @@ declare module '@theia/plugin' { activeParameter: number; } + /** + * How a [`SignatureHelpProvider`](#SignatureHelpProvider) was triggered. + */ + export enum SignatureHelpTriggerKind { + /** + * Signature help was invoked manually by the user or by a command. + */ + Invoke = 1, + + /** + * Signature help was triggered by a trigger character. + */ + TriggerCharacter = 2, + + /** + * Signature help was triggered by the cursor moving or by the document content changing. + */ + ContentChange = 3, + } + + /** + * Additional information about the context in which a + * [`SignatureHelpProvider`](#SignatureHelpProvider.provideSignatureHelp) was triggered. + */ + export interface SignatureHelpContext { + /** + * Action that caused signature help to be triggered. + */ + readonly triggerKind: SignatureHelpTriggerKind; + + /** + * Character that caused signature help to be triggered. + * + * This is `undefined` when signature help is not triggered by typing, such as when manually invoking + * signature help or when moving the cursor. + */ + readonly triggerCharacter?: string; + + /** + * `true` if signature help was already showing when it was triggered. + * + * Retriggers occur when the signature help is already active and can be caused by actions such as + * typing a trigger character, a cursor move, or document content changes. + */ + readonly isRetrigger: boolean; + + /** + * The currently active [`SignatureHelp`](#SignatureHelp). + * + * The `activeSignatureHelp` has its [`SignatureHelp.activeSignature`] field updated based on + * the user arrowing through available signatures. + */ + readonly activeSignatureHelp?: SignatureHelp; + } + /** * The signature help provider interface defines the contract between extensions and * the [parameter hints](https://code.visualstudio.com/docs/editor/intellisense)-feature. @@ -5077,10 +5132,30 @@ declare module '@theia/plugin' { * @param document The document in which the command was invoked. * @param position The position at which the command was invoked. * @param token A cancellation token. + * @param context Information about how signature help was triggered. + * * @return Signature help or a thenable that resolves to such. The lack of a result can be * signaled by returning `undefined` or `null`. */ - provideSignatureHelp(document: TextDocument, position: Position, token: CancellationToken | undefined): ProviderResult; + provideSignatureHelp(document: TextDocument, position: Position, token: CancellationToken, context: SignatureHelpContext): ProviderResult; + } + + /** + * Metadata about a registered [`SignatureHelpProvider`](#SignatureHelpProvider). + */ + export interface SignatureHelpProviderMetadata { + /** + * List of characters that trigger signature help. + */ + readonly triggerCharacters: ReadonlyArray; + + /** + * List of characters that re-trigger signature help. + * + * These trigger characters are only active when signature help is already showing. All trigger characters + * are also counted as re-trigger characters. + */ + readonly retriggerCharacters: ReadonlyArray; } /** @@ -5700,6 +5775,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) @@ -6753,9 +6835,11 @@ declare module '@theia/plugin' { * @param selector A selector that defines the documents this provider is applicable to. * @param provider A signature help provider. * @param triggerCharacters Trigger signature help when the user types one of the characters, like `,` or `(`. + * @param metadata Information about the provider. * @return A [disposable](#Disposable) that unregisters this provider when being disposed. */ export function registerSignatureHelpProvider(selector: DocumentSelector, provider: SignatureHelpProvider, ...triggerCharacters: string[]): Disposable; + export function registerSignatureHelpProvider(selector: DocumentSelector, provider: SignatureHelpProvider, metadata: SignatureHelpProviderMetadata): Disposable; /** * Register a type definition provider. diff --git a/packages/typescript/package.json b/packages/typescript/package.json index c2b4a18f7f432..2100d71c51b26 100644 --- a/packages/typescript/package.json +++ b/packages/typescript/package.json @@ -12,7 +12,7 @@ "@theia/monaco": "^0.10.0", "@theia/workspace": "^0.10.0", "command-exists": "^1.2.8", - "typescript-language-server": "^0.3.8" + "typescript-language-server": "^0.4.0" }, "publishConfig": { "access": "public" diff --git a/yarn.lock b/yarn.lock index fb2f53766bf00..0af00ab70f9aa 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@^0.18.0-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" @@ -7343,18 +7344,20 @@ moment@^2.10.6, moment@^2.21.0, moment@^2.6.0: version "2.22.2" resolved "https://registry.yarnpkg.com/moment/-/moment-2.22.2.tgz#3c257f9839fc0e93ff53149632239eb90783ff66" -monaco-css@^2.0.1: - version "2.2.0" - resolved "https://registry.yarnpkg.com/monaco-css/-/monaco-css-2.2.0.tgz#644e6e45d05f7704a4e1f563883bef4af9badb1f" +monaco-css@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/monaco-css/-/monaco-css-2.5.0.tgz#eb173658306d6ae6a8d38c08df7f67ecba685f80" + integrity sha512-V5YuMysU5MbNMPlZxMfB4os/mx+nIH3emrl2zgQe7Iu77dQhODoUysd5OoZB9hzpFoRDZ/KFuEaFaib8/ziYRQ== -monaco-html@^2.0.2: - version "2.2.0" - resolved "https://registry.yarnpkg.com/monaco-html/-/monaco-html-2.2.0.tgz#435f2f4ce6e5c7f707fb57e7c8a05b41e5cd1aa0" +monaco-html@^2.5.2: + version "2.5.2" + resolved "https://registry.yarnpkg.com/monaco-html/-/monaco-html-2.5.2.tgz#3e231ff50d024ed4f3c2d9fb075e77351f33508a" + integrity sha512-tugs+jHMtfInq/gMl5wXYoUs649rc5h6a/bbaK2+4MTx//iWUZ9mgTsgmbqqfbujEgHxxJHiGWTDIZjz8Ztx7g== -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== +monaco-languageclient@^0.10.2: + version "0.10.2" + resolved "https://registry.yarnpkg.com/monaco-languageclient/-/monaco-languageclient-0.10.2.tgz#d243964737b29fc20d542deca1835fa045cd19eb" + integrity sha512-ZfOm4jQyJzz7rHFIBfLiSM2tfgWoWXkumarLJPSqMFYyK1swIXwS8a8f8cwVZFkJ/aUO1GHcQXaaPZ1B5YtsLQ== dependencies: glob-to-regexp "^0.3.0" vscode-jsonrpc "^4.1.0-next" @@ -10587,17 +10590,17 @@ typedoc@^0.15.0-0: typedoc-default-themes "^0.6.0-0" typescript "3.3.x" -typescript-language-server@^0.3.8: - version "0.3.8" - resolved "https://registry.yarnpkg.com/typescript-language-server/-/typescript-language-server-0.3.8.tgz#de69db5a4d96b3cd1e994d37a8981010237f6c00" - integrity sha512-ohi+libVtaJ0F8asuXeqYlrPV84AkbcpWsp5kBeO6XnCrilwQS+elDrJ+jPu2tfVy3CIUpUbUYUmg4Bq3CA/XQ== +typescript-language-server@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/typescript-language-server/-/typescript-language-server-0.4.0.tgz#9b4aee8e001a69fcd152459a6cc1a08283db9193" + integrity sha512-K8jNOmDFn+QfrCh8ujby2pGDs5rpjYZQn+zvQnf42rxG4IHbfw5CHoMvbGkWPK/J5Gw8/l5K3i03kVZC2IBElg== dependencies: command-exists "1.2.6" commander "^2.11.0" fs-extra "^7.0.0" p-debounce "^1.0.0" tempy "^0.2.1" - vscode-languageserver "^4.4.0" + vscode-languageserver "^5.3.0-next" vscode-uri "^1.0.5" typescript@3.3.x: @@ -10980,14 +10983,10 @@ vscode-json-languageservice@^3.3.0: vscode-nls "^4.1.1" vscode-uri "^2.0.3" -vscode-jsonrpc@^3.6.2: - version "3.6.2" - resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-3.6.2.tgz#3b5eef691159a15556ecc500e9a8a0dd143470c8" - -vscode-jsonrpc@^4.1.0-next, vscode-jsonrpc@^4.1.0-next.2: - version "4.1.0-next.2" - resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-4.1.0-next.2.tgz#3bd318910a48e631742b290975386e3dae685be3" - integrity sha512-GsBLjP9DxQ42yl1mW9GEIlnSc0+R8mfzhaebwmmTPEJjezD5SPoAo3DFrIAFZha9yvQ1nzZfZlhtVpGQmgxtXg== +vscode-jsonrpc@^4.1.0-next, vscode-jsonrpc@^4.1.0-next.3: + version "4.1.0-next.3" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-4.1.0-next.3.tgz#05fe742959a2726020d4d0bfbc3d3c97873c7fde" + integrity sha512-Z6oxBiMks2+UADV1QHXVooSakjyhI+eHTnXzDyVvVMmegvSfkXk2w6mPEdSkaNHFBdtWW7n20H1yw2nA3A17mg== vscode-languageclient@^5.3.0-next: version "5.3.0-next.6" @@ -10997,36 +10996,26 @@ vscode-languageclient@^5.3.0-next: semver "^5.5.0" vscode-languageserver-protocol "^3.15.0-next.6" -vscode-languageserver-protocol@^3.10.3: - version "3.10.3" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.10.3.tgz#59841c9602a6a6baab68613c2a47760994657196" +vscode-languageserver-protocol@^3.15.0-next.6, vscode-languageserver-protocol@^3.15.0-next.8: + version "3.15.0-next.8" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.0-next.8.tgz#5e3cc0ae143fe3364820249e32f6019ce974fa9e" + integrity sha512-9FigDhuYTUqX73IGKgJeyLmfXQJv9p3t22RF3peT+HM33uFiTZE3MUgHj4I9m/dKCDvuJt0yvbI27ut4hDoGRQ== dependencies: - vscode-jsonrpc "^3.6.2" - vscode-languageserver-types "^3.10.1" + vscode-jsonrpc "^4.1.0-next.3" + vscode-languageserver-types "^3.15.0-next.4" -vscode-languageserver-protocol@^3.15.0-next.6: - version "3.15.0-next.6" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.0-next.6.tgz#a8aeb7e7dd65da8216b386db59494cdfd3215d92" - integrity sha512-/yDpYlWyNs26mM23mT73xmOFsh1iRfgZfBdHmfAxwDKwpQKLoOSqVidtYfxlK/pD3IEKGcAVnT4WXTsguxxAMQ== - dependencies: - vscode-jsonrpc "^4.1.0-next.2" - vscode-languageserver-types "^3.15.0-next.2" - -vscode-languageserver-types@^3.10.1: - version "3.10.1" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.10.1.tgz#d5d5f44f688a3b2aa9857dc53cb9cacca73fe35a" - -vscode-languageserver-types@^3.15.0-next, vscode-languageserver-types@^3.15.0-next.2: - version "3.15.0-next.2" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.15.0-next.2.tgz#a0601332cdaafac21931f497bb080cfb8d73f254" - integrity sha512-2JkrMWWUi2rlVLSo9OFR2PIGUzdiowEM8NgNYiwLKnXTjpwpjjIrJbNNxDik7Rv4oo9KtikcFQZKXbrKilL/MQ== +vscode-languageserver-types@^3.15.0-next, vscode-languageserver-types@^3.15.0-next.2, vscode-languageserver-types@^3.15.0-next.4: + version "3.15.0-next.4" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.15.0-next.4.tgz#9aae49844ef826ae656382facecc20664113c060" + integrity sha512-IKIWTdUPBnOtwznIrhxKnjVZ7hYxEzwZ3M2xmDi7OjjexuOM6LnGtoo1Dv4wYSik4epK4STEib6e8da2GxUsJA== -vscode-languageserver@^4.4.0: - version "4.4.2" - resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-4.4.2.tgz#600ae9cc7a6ff1e84d93c7807840c2cb5b22821b" +vscode-languageserver@^5.3.0-next: + version "5.3.0-next.10" + resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-5.3.0-next.10.tgz#995fe8b57fc4eb9fea0d11762d3a803de4278995" + integrity sha512-QL7Fe1FT6PdLtVzwJeZ78pTic4eZbzLRy7yAQgPb9xalqqgZESR0+yDZPwJrM3E7PzOmwHBceYcJR54eQZ7Kng== dependencies: - vscode-languageserver-protocol "^3.10.3" - vscode-uri "^1.0.5" + vscode-languageserver-protocol "^3.15.0-next.8" + vscode-textbuffer "^1.0.0" vscode-languageserver@^5.3.0-next.8: version "5.3.0-next.8"