Skip to content

Commit

Permalink
Fix 'window.showTextDocument' to open resources with 'untitled' schema,
Browse files Browse the repository at this point in the history
Fixes #6565

Signed-off-by: Shimon Ben Yair <[email protected]>
  • Loading branch information
ShimonBenYair committed Feb 16, 2020
1 parent ba016ee commit ee339b6
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 23 deletions.
2 changes: 1 addition & 1 deletion packages/core/src/browser/widget-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export class WidgetManager {

protected _cachedFactories: Map<string, WidgetFactory>;
protected readonly widgets = new Map<string, Widget>();
protected readonly widgetPromises = new Map<string, MaybePromise<Widget>>();
public readonly widgetPromises = new Map<string, MaybePromise<Widget>>();
protected readonly pendingWidgetPromises = new Map<string, MaybePromise<Widget>>();

@inject(ContributionProvider) @named(WidgetFactory)
Expand Down
26 changes: 25 additions & 1 deletion packages/core/src/browser/widget-open-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,34 @@ export abstract class WidgetOpenHandler<W extends BaseWidget> implements OpenHan
* Reject if the given options is not an widget options or a widget cannot be opened.
*/
async open(uri: URI, options?: WidgetOpenerOptions): Promise<W> {

const widget = await this.getOrCreateWidget(uri, options);
await this.doOpen(widget, options);
const widgetId = this.getOpenedUntitledWidgetId(uri);
if (!widgetId) {
await this.doOpen(widget, options);
} else {
await this.shell.activateWidget(widgetId);
}
return widget;
}

getOpenedUntitledWidgetId(uri: URI): string | undefined {
const keyUri = uri.path.toString();
const untitledScheme = 'untitled';
if (uri.scheme !== untitledScheme && this.widgetManager.widgetPromises) {
const existingWidgetsKeysArray = Array.from(this.widgetManager.widgetPromises.keys());
for (let i = 0; i < existingWidgetsKeysArray.length; i++) {
const widgetKey = JSON.parse(existingWidgetsKeysArray[i]);
if (widgetKey.options && widgetKey.options.uri && widgetKey.options.uri.startsWith(untitledScheme)) {
if (widgetKey.options.uri.includes(keyUri)) {
return widgetKey.factoryId + ':' + widgetKey.options.uri;
}
}
}
}
return undefined;
}

protected async doOpen(widget: W, options?: WidgetOpenerOptions): Promise<void> {
const op: WidgetOpenerOptions = {
mode: 'activate',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import { EditorManager } from '@theia/editor/lib/browser';
import { CodeEditorWidget } from '@theia/plugin-ext/lib/main/browser/menus/menus-contribution-handler';
import { TextDocumentShowOptions } from '@theia/plugin-ext/lib/common/plugin-api-rpc-model';
import { DocumentsMainImpl } from '@theia/plugin-ext/lib/main/browser/documents-main';
import { createUntitledResource } from '@theia/plugin-ext/lib/main/browser/editor/untitled-resource';
import { createUntitledURI } from '@theia/plugin-ext/lib/main/browser/editor/untitled-resource';
import { toDocumentSymbol } from '@theia/plugin-ext/lib/plugin/type-converters';
import { ViewColumn } from '@theia/plugin-ext/lib/plugin/types-impl';
import { WorkspaceCommands } from '@theia/workspace/lib/browser';
Expand Down Expand Up @@ -172,7 +172,7 @@ export class PluginVscodeCommandsContribution implements CommandContribution {
* and apply actions only to them
*/
commands.registerCommand({ id: 'workbench.action.files.newUntitledFile' }, {
execute: () => open(this.openerService, createUntitledResource().uri)
execute: () => open(this.openerService, createUntitledURI())
});
commands.registerCommand({ id: 'workbench.action.files.openFile' }, {
execute: () => commands.executeCommand(WorkspaceCommands.OPEN_FILE.id)
Expand Down
7 changes: 4 additions & 3 deletions packages/plugin-ext/src/main/browser/documents-main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { DisposableCollection, Disposable } from '@theia/core';
import { MonacoEditorModel } from '@theia/monaco/lib/browser/monaco-editor-model';
import { RPCProtocol } from '../../common/rpc-protocol';
import { EditorModelService } from './text-editor-model-service';
import { createUntitledResource } from './editor/untitled-resource';
import { UntitledResourceResolver } from './editor/untitled-resource';
import { EditorManager, EditorOpenerOptions } from '@theia/editor/lib/browser';
import URI from '@theia/core/lib/common/uri';
import CodeURI from 'vscode-uri';
Expand Down Expand Up @@ -92,7 +92,8 @@ export class DocumentsMainImpl implements DocumentsMain, Disposable {
rpc: RPCProtocol,
private editorManager: EditorManager,
private openerService: OpenerService,
private shell: ApplicationShell
private shell: ApplicationShell,
private untitledResourceResolver: UntitledResourceResolver
) {
this.proxy = rpc.getProxy(MAIN_RPC_CONTEXT.DOCUMENTS_EXT);

Expand Down Expand Up @@ -167,7 +168,7 @@ export class DocumentsMainImpl implements DocumentsMain, Disposable {
async $tryCreateDocument(options?: { language?: string; content?: string; }): Promise<UriComponents> {
const language = options && options.language;
const content = options && options.content;
const resource = createUntitledResource(content, language);
const resource = await this.untitledResourceResolver.createUntitledResource(content, language);
return monaco.Uri.parse(resource.uri.toString());
}

Expand Down
83 changes: 68 additions & 15 deletions packages/plugin-ext/src/main/browser/editor/untitled-resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,39 +14,92 @@
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

import { ResourceResolver, Resource } from '@theia/core';
import { injectable, inject } from 'inversify';
import { TextDocumentContentChangeEvent } from 'vscode-languageserver-protocol';
import { Resource, ResourceResolver } from '@theia/core';
import URI from '@theia/core/lib/common/uri';
import { injectable } from 'inversify';
import { Schemes } from '../../../common/uri-components';
import { FileResource, FileResourceResolver } from '@theia/filesystem/lib/browser';

const resources = new Map<string, UntitledResource>();
let index = 0;

@injectable()
export class UntitledResourceResolver implements ResourceResolver {
resolve(uri: URI): Resource | Promise<Resource> {
if (uri.scheme === Schemes.UNTITLED) {
return resources.get(uri.toString())!;

@inject(FileResourceResolver)
protected readonly fileResourceResolver: FileResourceResolver;

protected readonly resources = new Map<string, UntitledResource>();

async resolve(uri: URI): Promise<UntitledResource> {
if (uri.scheme !== Schemes.UNTITLED) {
throw new Error('The given uri is not untitled file uri: ' + uri);
} else {
const untitledResource = this.resources.get(uri.toString());
if (!untitledResource) {
return this.createUntitledResource('', '', uri, this.fileResourceResolver);
} else {
return untitledResource;
}
}
}

async createUntitledResource(content?: string, language?: string, uri?: URI, fileResourceResolver?: FileResourceResolver): Promise<UntitledResource> {
let extension;
if (language) {
for (const lang of monaco.languages.getLanguages()) {
if (lang.id === language) {
if (lang.extensions) {
extension = lang.extensions[0];
break;
}
}
}
}
throw new Error(`scheme ${uri.scheme} is not '${Schemes.UNTITLED}'`);
return new UntitledResource(this.resources, uri ? uri : new URI().withScheme(Schemes.UNTITLED).withPath(`/Untitled-${index++}${extension ? extension : ''}`),
content, fileResourceResolver);
}
}

export class UntitledResource implements Resource {
private fileResource?: FileResource;

constructor(public uri: URI, private content?: string) {
resources.set(this.uri.toString(), this);
constructor(private resources: Map<string, UntitledResource>, public uri: URI, private content?: string, private fileResourceResolver?: FileResourceResolver) {
this.resources.set(this.uri.toString(), this);
}

readContents(options?: { encoding?: string | undefined; } | undefined): Promise<string> {
return Promise.resolve(this.content ? this.content : '');
dispose(): void {
this.resources.delete(this.uri.toString());
}

dispose(): void {
resources.delete(this.uri.toString());
async readContents(options?: { encoding?: string | undefined; } | undefined): Promise<string> {
if (this.fileResource) {
return this.fileResource.readContents(options);
} else if (this.content) {
return this.content;
} else {
return '';
}
}

async saveContents(content: string, options?: { encoding?: string, overwriteEncoding?: string }): Promise<void> {
if (this.fileResourceResolver && !this.fileResource) {
this.fileResource = await this.fileResourceResolver.resolve(new URI(this.uri.path.toString()));
}
if (this.fileResource) {
await this.fileResource.saveContents(content, options);
}
}

async saveContentChanges(changes: TextDocumentContentChangeEvent[], options?: { encoding?: string, overwriteEncoding?: string }): Promise<void> {
if (!this.fileResource) {
throw new Error('FileResource is not available for: ' + this.uri.path.toString());
}
await this.fileResource.saveContentChanges(changes, options);
}
}

export function createUntitledResource(content?: string, language?: string): UntitledResource {
export function createUntitledURI(language?: string): URI {
let extension;
if (language) {
for (const lang of monaco.languages.getLanguages()) {
Expand All @@ -58,5 +111,5 @@ export function createUntitledResource(content?: string, language?: string): Unt
}
}
}
return new UntitledResource(new URI().withScheme(Schemes.UNTITLED).withPath(`/Untitled-${index++}${extension ? extension : ''}`), content);
return new URI().withScheme(Schemes.UNTITLED).withPath(`/Untitled-${index++}${extension ? extension : ''}`);
}
4 changes: 3 additions & 1 deletion packages/plugin-ext/src/main/browser/main-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import { OpenerService } from '@theia/core/lib/browser/opener-service';
import { ApplicationShell } from '@theia/core/lib/browser/shell/application-shell';
import { MonacoBulkEditService } from '@theia/monaco/lib/browser/monaco-bulk-edit-service';
import { MonacoEditorService } from '@theia/monaco/lib/browser/monaco-editor-service';
import { UntitledResourceResolver } from './editor/untitled-resource';

export function setUpPluginApi(rpc: RPCProtocol, container: interfaces.Container): void {
const commandRegistryMain = new CommandRegistryMainImpl(rpc, container);
Expand All @@ -73,7 +74,8 @@ export function setUpPluginApi(rpc: RPCProtocol, container: interfaces.Container
const editorManager = container.get(EditorManager);
const openerService = container.get<OpenerService>(OpenerService);
const shell = container.get(ApplicationShell);
const documentsMain = new DocumentsMainImpl(editorsAndDocuments, modelService, rpc, editorManager, openerService, shell);
const untitledResourceResolver = container.get(UntitledResourceResolver);
const documentsMain = new DocumentsMainImpl(editorsAndDocuments, modelService, rpc, editorManager, openerService, shell, untitledResourceResolver);
rpc.set(PLUGIN_RPC_CONTEXT.DOCUMENTS_MAIN, documentsMain);

const bulkEditService = container.get(MonacoBulkEditService);
Expand Down

0 comments on commit ee339b6

Please sign in to comment.