From d82542caeff73c47c7fedfec83351c7c9cfff379 Mon Sep 17 00:00:00 2001 From: Anton Kosyakov Date: Fri, 29 Nov 2019 15:56:14 +0000 Subject: [PATCH] [fs][plugin] fix #6661: map editor icon paths to external URIs VS Code extensions are using absolute paths to load editor icons. It does not work in remote environments. Instead we rewrite files URIs to external URIs to make it work. Signed-off-by: Anton Kosyakov --- .../node/download/file-download-endpoint.ts | 11 +++++++ .../src/main/browser/text-editors-main.ts | 33 ++++++++++++++++++- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/packages/filesystem/src/node/download/file-download-endpoint.ts b/packages/filesystem/src/node/download/file-download-endpoint.ts index 6b66620847687..6066d28a0b47e 100644 --- a/packages/filesystem/src/node/download/file-download-endpoint.ts +++ b/packages/filesystem/src/node/download/file-download-endpoint.ts @@ -16,11 +16,13 @@ // tslint:disable:no-any +import * as url from 'url'; import { injectable, inject, named } from 'inversify'; import { json } from 'body-parser'; // tslint:disable-next-line:no-implicit-dependencies import { Application, Router } from 'express'; import { BackendApplicationContribution } from '@theia/core/lib/node/backend-application'; +import { FileUri } from '@theia/core/lib/node/file-uri'; import { FileDownloadHandler } from './file-download-handler'; @injectable() @@ -48,6 +50,15 @@ export class FileDownloadEndpoint implements BackendApplicationContribution { // Content-Type: application/json app.use(json()); app.use(FileDownloadEndpoint.PATH, router); + app.get('/file', (request, response) => { + const uri = url.parse(request.url).query; + if (!uri) { + response.status(400).send('invalid uri'); + return; + } + const fsPath = FileUri.fsPath(decodeURIComponent(uri)); + response.sendFile(fsPath); + }); } } diff --git a/packages/plugin-ext/src/main/browser/text-editors-main.ts b/packages/plugin-ext/src/main/browser/text-editors-main.ts index 11d49b15156cb..507332de9f264 100644 --- a/packages/plugin-ext/src/main/browser/text-editors-main.ts +++ b/packages/plugin-ext/src/main/browser/text-editors-main.ts @@ -14,6 +14,7 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ +import URI from 'vscode-uri'; import { TextEditorsMain, MAIN_RPC_CONTEXT, @@ -25,6 +26,7 @@ import { ApplyEditsOptions, UndoStopOptions, DecorationRenderOptions, + ThemeDecorationInstanceRenderOptions, DecorationOptions, WorkspaceEditDto } from '../../common/plugin-api-rpc'; @@ -37,18 +39,21 @@ import { disposed } from '../../common/errors'; import { toMonacoWorkspaceEdit } from './languages-main'; import { MonacoBulkEditService } from '@theia/monaco/lib/browser/monaco-bulk-edit-service'; import { MonacoEditorService } from '@theia/monaco/lib/browser/monaco-editor-service'; +import { theiaUritoUriComponents, UriComponents } from '../../common/uri-components'; +import { Endpoint } from '@theia/core/lib/browser/endpoint'; export class TextEditorsMainImpl implements TextEditorsMain, Disposable { private readonly proxy: TextEditorsExt; private readonly toDispose = new DisposableCollection(); private readonly editorsToDispose = new Map(); + private readonly fileEndpoint = new Endpoint({ path: 'file' }).getRestUrl(); constructor( private readonly editorsAndDocuments: EditorsAndDocumentsMain, rpc: RPCProtocol, private readonly bulkEditService: MonacoBulkEditService, - private readonly monacoEditorService: MonacoEditorService + private readonly monacoEditorService: MonacoEditorService, ) { this.proxy = rpc.getProxy(MAIN_RPC_CONTEXT.TEXT_EDITORS_EXT); this.toDispose.push(editorsAndDocuments); @@ -127,10 +132,36 @@ export class TextEditorsMainImpl implements TextEditorsMain, Disposable { } $registerTextEditorDecorationType(key: string, options: DecorationRenderOptions): void { + this.injectRemoteUris(options); this.monacoEditorService.registerDecorationType(key, options); this.toDispose.push(Disposable.create(() => this.$removeTextEditorDecorationType(key))); } + protected injectRemoteUris(options: DecorationRenderOptions | ThemeDecorationInstanceRenderOptions): void { + if (options.before) { + options.before.contentIconPath = this.toRemoteUri(options.before.contentIconPath); + } + if (options.after) { + options.after.contentIconPath = this.toRemoteUri(options.after.contentIconPath); + } + if ('gutterIconPath' in options) { + options.gutterIconPath = this.toRemoteUri(options.gutterIconPath); + } + if ('dark' in options && options.dark) { + this.injectRemoteUris(options.dark); + } + if ('light' in options && options.light) { + this.injectRemoteUris(options.light); + } + } + + protected toRemoteUri(uri?: UriComponents): UriComponents | undefined { + if (uri && uri.scheme === 'file') { + return theiaUritoUriComponents(this.fileEndpoint.withQuery(URI.revive(uri).toString())); + } + return uri; + } + $removeTextEditorDecorationType(key: string): void { this.monacoEditorService.removeDecorationType(key); }