From 67d587677605bf3b832f8dea97ab87421ee4b41e Mon Sep 17 00:00:00 2001 From: Igor Vinokur Date: Tue, 20 Nov 2018 11:20:55 +0200 Subject: [PATCH] Plugin-2801: API endpoint for window.showTextDocument Signed-off-by: Igor Vinokur --- packages/plugin-ext/src/api/model.ts | 4 ++ packages/plugin-ext/src/api/plugin-api.ts | 5 +- .../src/main/browser/documents-main.ts | 17 ++++- packages/plugin-ext/src/plugin/documents.ts | 19 ++++-- .../plugin-ext/src/plugin/plugin-context.ts | 21 ++++++ packages/plugin/src/theia.d.ts | 64 +++++++++++++++++++ 6 files changed, 121 insertions(+), 9 deletions(-) diff --git a/packages/plugin-ext/src/api/model.ts b/packages/plugin-ext/src/api/model.ts index 78db956bc9cf9..997aaa578fa9f 100644 --- a/packages/plugin-ext/src/api/model.ts +++ b/packages/plugin-ext/src/api/model.ts @@ -19,6 +19,10 @@ import { UriComponents } from '../common/uri-components'; // Should contains internal Plugin API types +export interface TextDocumentShowOptions { + selection?: Range; +} + export interface Range { /** * Line number on which the range starts (starts at 1). diff --git a/packages/plugin-ext/src/api/plugin-api.ts b/packages/plugin-ext/src/api/plugin-api.ts index 4d31fe4946225..503581638014c 100644 --- a/packages/plugin-ext/src/api/plugin-api.ts +++ b/packages/plugin-ext/src/api/plugin-api.ts @@ -44,7 +44,8 @@ import { Command, TextEdit, ReferenceContext, - Location + Location, + TextDocumentShowOptions } from './model'; export interface PluginInitData { @@ -627,7 +628,7 @@ export interface DocumentsExt { export interface DocumentsMain { $tryCreateDocument(options?: { language?: string; content?: string; }): Promise; - $tryOpenDocument(uri: UriComponents): Promise; + $tryOpenDocument(uri: UriComponents, options?: TextDocumentShowOptions): Promise; $trySaveDocument(uri: UriComponents): Promise; } diff --git a/packages/plugin-ext/src/main/browser/documents-main.ts b/packages/plugin-ext/src/main/browser/documents-main.ts index 60f4a4f5e835c..6072a5db24899 100644 --- a/packages/plugin-ext/src/main/browser/documents-main.ts +++ b/packages/plugin-ext/src/main/browser/documents-main.ts @@ -21,9 +21,10 @@ import { MonacoEditorModel } from '@theia/monaco/lib/browser/monaco-editor-model import { RPCProtocol } from '../../api/rpc-protocol'; import { EditorModelService } from './text-editor-model-service'; import { createUntitledResource } from './editor/untitled-resource'; -import { EditorManager } from '@theia/editor/lib/browser'; +import { EditorManager, EditorOpenerOptions } from '@theia/editor/lib/browser'; import URI from '@theia/core/lib/common/uri'; import { Saveable } from '@theia/core/lib/browser'; +import { TextDocumentShowOptions } from '../../api/model'; export class DocumentsMainImpl implements DocumentsMain { @@ -103,12 +104,22 @@ export class DocumentsMainImpl implements DocumentsMain { return createUntitledResource(content, language); } - async $tryOpenDocument(uri: UriComponents): Promise { + async $tryOpenDocument(uri: UriComponents, options?: TextDocumentShowOptions): Promise { // Removing try-catch block here makes it not possible to handle errors. // Following message is appeared in browser console // - Uncaught (in promise) Error: Cannot read property 'message' of undefined. try { - await this.editorManger.open(new URI(uri.external!)); + let editorOpenerOptions: EditorOpenerOptions | undefined; + if (options && options.selection) { + const selection = options.selection; + editorOpenerOptions = { + selection: { + start: { line: selection.startLineNumber - 1 , character: selection.startColumn - 1 }, + end: { line: selection.endLineNumber - 1 , character: selection.endColumn - 1 } + } + }; + } + await this.editorManger.open(new URI(uri.external!), editorOpenerOptions); } catch (err) { throw new Error(err); } diff --git a/packages/plugin-ext/src/plugin/documents.ts b/packages/plugin-ext/src/plugin/documents.ts index e8e1a8cc7b120..ac6bc36b990df 100644 --- a/packages/plugin-ext/src/plugin/documents.ts +++ b/packages/plugin-ext/src/plugin/documents.ts @@ -23,6 +23,7 @@ import { DocumentDataExt, setWordDefinitionFor } from './document-data'; import { EditorsAndDocumentsExtImpl } from './editors-and-documents'; import * as Converter from './type-converters'; import { DisposableCollection } from '@theia/core/lib/common/disposable'; +import { Range } from '../api/model'; export class DocumentsExtImpl implements DocumentsExt { private toDispose = new DisposableCollection(); @@ -120,7 +121,7 @@ export class DocumentsExtImpl implements DocumentsExt { return undefined; } - async openDocument(uri: URI): Promise { + async openDocument(uri: URI, options?: theia.TextDocumentShowOptions): Promise { const cached = this.editorsAndDocuments.getDocument(uri.toString()); if (cached) { return cached; @@ -135,7 +136,7 @@ export class DocumentsExtImpl implements DocumentsExt { try { // start opening document - const document = this.loadDocument(uri); + const document = this.loadDocument(uri, options); // add loader to the map this.loadingDocuments.set(uri.toString(), document); // wait the document being opened @@ -150,8 +151,18 @@ export class DocumentsExtImpl implements DocumentsExt { } } - private async loadDocument(uri: URI): Promise { - await this.proxy.$tryOpenDocument(uri); + private async loadDocument(uri: URI, options?: theia.TextDocumentShowOptions): Promise { + let range: Range | undefined; + if (options && options.selection) { + const { start, end } = options.selection; + range = { + startLineNumber: start.line, + startColumn: start.character, + endLineNumber: end.line, + endColumn: end.character + }; + } + await this.proxy.$tryOpenDocument(uri, { selection: range }); return this.editorsAndDocuments.getDocument(uri.toString()); } diff --git a/packages/plugin-ext/src/plugin/plugin-context.ts b/packages/plugin-ext/src/plugin/plugin-context.ts index a06d8851b2d4c..9057181e85e7e 100644 --- a/packages/plugin-ext/src/plugin/plugin-context.ts +++ b/packages/plugin-ext/src/plugin/plugin-context.ts @@ -155,6 +155,27 @@ export function createAPIFactory( onDidChangeTextEditorVisibleRanges(listener, thisArg?, disposables?) { return editors.onDidChangeTextEditorVisibleRanges(listener, thisArg, disposables); }, + async showTextDocument(documentArg: theia.TextDocument | Uri, + optionsArg?: theia.TextDocumentShowOptions | theia.ViewColumn, + preserveFocus?: boolean + ): Promise { + // Todo pass additional arguments to documents service, when the API will support them. + let documentOptions: theia.TextDocumentShowOptions | undefined; + const uri: Uri = documentArg instanceof Uri ? documentArg : documentArg.uri; + if (optionsArg) { + const optionsAny: any = optionsArg; + if (optionsAny.selection) { + documentOptions = optionsArg as theia.TextDocumentShowOptions; + } + } + await documents.openDocument(uri, documentOptions); + const textEditor = editors.getVisibleTextEditors().find(editor => editor.document.uri.toString() === uri.toString()); + if (textEditor) { + return Promise.resolve(textEditor); + } else { + throw new Error(`Failed to show text document ${documentArg.toString()}`); + } + }, // tslint:disable-next-line:no-any showQuickPick(items: any, options: theia.QuickPickOptions, token?: theia.CancellationToken): any { if (token) { diff --git a/packages/plugin/src/theia.d.ts b/packages/plugin/src/theia.d.ts index c29bfb74114cb..aa492c5208fce 100644 --- a/packages/plugin/src/theia.d.ts +++ b/packages/plugin/src/theia.d.ts @@ -2365,6 +2365,40 @@ declare module '@theia/plugin' { */ export const onDidChangeTextEditorViewColumn: Event; + /** + * Show the given document in a text editor. A [column](#ViewColumn) can be provided + * to control where the editor is being shown. Might change the [active editor](#window.activeTextEditor). + * + * @param document A text document to be shown. + * @param column A view column in which the [editor](#TextEditor) should be shown. The default is the [active](#ViewColumn.Active), other values + * are adjusted to be `Min(column, columnCount + 1)`, the [active](#ViewColumn.Active)-column is not adjusted. Use [`ViewColumn.Beside`](#ViewColumn.Beside) + * to open the editor to the side of the currently active one. + * @param preserveFocus When `true` the editor will not take focus. + * @return A promise that resolves to an [editor](#TextEditor). + */ + export function showTextDocument(document: TextDocument, column?: ViewColumn, preserveFocus?: boolean): Thenable; + + /** + * Show the given document in a text editor. [Options](#TextDocumentShowOptions) can be provided + * to control options of the editor is being shown. Might change the [active editor](#window.activeTextEditor). + * + * @param document A text document to be shown. + * @param options [Editor options](#TextDocumentShowOptions) to configure the behavior of showing the [editor](#TextEditor). + * @return A promise that resolves to an [editor](#TextEditor). + */ + export function showTextDocument(document: TextDocument, options?: TextDocumentShowOptions): Thenable; + + /** + * A short-hand for `openTextDocument(uri).then(document => showTextDocument(document, options))`. + * + * @see [openTextDocument](#openTextDocument) + * + * @param uri A resource identifier. + * @param options [Editor options](#TextDocumentShowOptions) to configure the behavior of showing the [editor](#TextEditor). + * @return A promise that resolves to an [editor](#TextEditor). + */ + export function showTextDocument(uri: Uri, options?: TextDocumentShowOptions): Thenable; + /** * Shows a selection list. * @param items @@ -5463,4 +5497,34 @@ declare module '@theia/plugin' { */ export function registerDebugConfigurationProvider(debugType: string, provider: DebugConfigurationProvider): Disposable; } + + /** + * Represents options to configure the behavior of showing a [document](#TextDocument) in an [editor](#TextEditor). + */ + export interface TextDocumentShowOptions { + /** + * An optional view column in which the [editor](#TextEditor) should be shown. + * The default is the [active](#ViewColumn.Active), other values are adjusted to + * be `Min(column, columnCount + 1)`, the [active](#ViewColumn.Active)-column is + * not adjusted. Use [`ViewColumn.Beside`](#ViewColumn.Beside) to open the + * editor to the side of the currently active one. + */ + viewColumn?: ViewColumn; + + /** + * An optional flag that when `true` will stop the [editor](#TextEditor) from taking focus. + */ + preserveFocus?: boolean; + + /** + * An optional flag that controls if an [editor](#TextEditor)-tab will be replaced + * with the next editor or if it will be kept. + */ + preview?: boolean; + + /** + * An optional selection to apply for the document in the [editor](#TextEditor). + */ + selection?: Range; + } }