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 <shimon.ben.yair@sap.com>
  • Loading branch information
ShimonBenYair authored and akosyakov committed Feb 24, 2020
1 parent 8ad04be commit 0efc669
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 21 deletions.
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
9 changes: 6 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 All @@ -30,6 +30,7 @@ import { Range } from 'vscode-languageserver-types';
import { OpenerService } from '@theia/core/lib/browser/opener-service';
import { Reference } from '@theia/core/lib/common/reference';
import { dispose } from '../../common/disposable-util';
import { FileResourceResolver } from '@theia/filesystem/lib/browser';

/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
Expand Down Expand Up @@ -92,7 +93,9 @@ export class DocumentsMainImpl implements DocumentsMain, Disposable {
rpc: RPCProtocol,
private editorManager: EditorManager,
private openerService: OpenerService,
private shell: ApplicationShell
private shell: ApplicationShell,
private untitledResourceResolver: UntitledResourceResolver,
private fileResourceResolver: FileResourceResolver
) {
this.proxy = rpc.getProxy(MAIN_RPC_CONTEXT.DOCUMENTS_EXT);

Expand Down Expand Up @@ -178,7 +181,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(this.fileResourceResolver, content, language);
return monaco.Uri.parse(resource.uri.toString());
}

Expand Down
110 changes: 95 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,119 @@
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

import { ResourceResolver, Resource } from '@theia/core';
import { Emitter, Event } from '@theia/core/lib/common/event';
import { injectable, inject } from 'inversify';
import { TextDocumentContentChangeEvent } from 'vscode-languageserver-protocol';
import { Resource, ResourceResolver, ResourceVersion } 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(this.fileResourceResolver, '', '', uri);
} else {
return untitledResource;
}
}
}

async createUntitledResource(fileResourceResolver: FileResourceResolver, content?: string, language?: string, uri?: URI): 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 : ''}`),
fileResourceResolver, content);
}
}

export class UntitledResource implements Resource {

constructor(public uri: URI, private content?: string) {
resources.set(this.uri.toString(), this);
}
private fileResource?: FileResource;

protected readonly onDidChangeContentsEmitter = new Emitter<void>();
readonly onDidChangeContents: Event<void> = this.onDidChangeContentsEmitter.event;

readContents(options?: { encoding?: string | undefined; } | undefined): Promise<string> {
return Promise.resolve(this.content ? this.content : '');
constructor(private resources: Map<string, UntitledResource>, public uri: URI, private fileResourceResolver: FileResourceResolver, private content?: string) {
this.resources.set(this.uri.toString(), this);
}

dispose(): void {
resources.delete(this.uri.toString());
this.resources.delete(this.uri.toString());
this.onDidChangeContentsEmitter.dispose();
if (this.fileResource) {
this.fileResource.dispose();
}
}

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.fileResource) {
this.fileResource = await this.fileResourceResolver.resolve(new URI(this.uri.path.toString()));
if (this.fileResource.onDidChangeContents) {
this.fileResource.onDidChangeContents(() => this.fireDidChangeContents());
}
}
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);
}

async guessEncoding(): Promise<string | undefined> {
if (this.fileResource) {
return this.fileResource.guessEncoding();
}
}

protected fireDidChangeContents(): void {
this.onDidChangeContentsEmitter.fire(undefined);
}

get version(): ResourceVersion | undefined {
if (this.fileResource) {
return this.fileResource.version;
}
return undefined;
}
}

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 +138,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 : ''}`);
}
6 changes: 5 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,8 @@ 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';
import { FileResourceResolver } from '@theia/filesystem/lib/browser';

export function setUpPluginApi(rpc: RPCProtocol, container: interfaces.Container): void {
const commandRegistryMain = new CommandRegistryMainImpl(rpc, container);
Expand All @@ -73,7 +75,9 @@ 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 fileResourceResolver = container.get(FileResourceResolver);
const documentsMain = new DocumentsMainImpl(editorsAndDocuments, modelService, rpc, editorManager, openerService, shell, untitledResourceResolver, fileResourceResolver);
rpc.set(PLUGIN_RPC_CONTEXT.DOCUMENTS_MAIN, documentsMain);

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

0 comments on commit 0efc669

Please sign in to comment.