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 committed Jan 22, 2020
1 parent a99b585 commit d86edea
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import TheiaURI from '@theia/core/lib/common/uri';
import { EditorManager } from '@theia/editor/lib/browser';
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 { fromViewColumn, 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 @@ -140,7 +140,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 @@ -94,7 +94,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 @@ -169,7 +170,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
87 changes: 72 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,96 @@
* 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;

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 Promise.resolve(this.content);
} else {
return '';
}
}

async saveContents(content: string, options?: { encoding?: string, overwriteEncoding?: string }): Promise<void> {
const that = this;
// The path of URI's with Untitled scheme has a prefix of: 'file:///'.
// When the user chosse to save the file we need to change the scheme from 'untitled' to 'file'.
// Need to find better way to remove the 'file:///' prefix from a URI with untitled scheme
const fileUri = new URI(this.uri.path.toString().replace('file:///', ''));
if (this.fileResourceResolver) {
this.fileResourceResolver.resolve(fileUri).then(resolvedFileResource => {
that.fileResource = resolvedFileResource;
that.fileResource.saveContents(content, options);
});
}
}

async saveContentChanges(changes: TextDocumentContentChangeEvent[], options?: { encoding?: string, overwriteEncoding?: string }): Promise<void> {
if (this.fileResource) {
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 +115,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
10 changes: 5 additions & 5 deletions packages/plugin-ext/src/plugin/documents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,11 +124,11 @@ export class DocumentsExtImpl implements DocumentsExt {
protected async fireTextDocumentWillSaveEvent({
document, reason, fireEvent, accept
}: {
document: theia.TextDocument,
reason: theia.TextDocumentSaveReason,
fireEvent: (e: theia.TextDocumentWillSaveEvent) => any,
accept: (operation: SingleEditOperation) => void
}): Promise<void> {
document: theia.TextDocument,
reason: theia.TextDocumentSaveReason,
fireEvent: (e: theia.TextDocumentWillSaveEvent) => any,
accept: (operation: SingleEditOperation) => void
}): Promise<void> {

const promises: PromiseLike<TextEdit[] | any>[] = [];
fireEvent(Object.freeze({
Expand Down

0 comments on commit d86edea

Please sign in to comment.