diff --git a/.vscode/launch.json b/.vscode/launch.json index 8ce9ccf71d247..0768794112228 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,133 +1,156 @@ { - // Use IntelliSense to learn about possible Node.js debug attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "type": "node", - "request": "attach", - "name": "Attach by Process ID", - "processId": "${command:PickProcess}" - }, - { - "type": "node", - "request": "launch", - "name": "Launch with Node.js", - "program": "${file}" - }, - { - "type": "node", - "request": "launch", - "name": "Launch Electron Backend", - "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron", - "windows": { - "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron.cmd" - }, - "program": "${workspaceRoot}/examples/electron/src-gen/frontend/electron-main.js", - "protocol": "inspector", - "args": [ - "--log-level=debug", - "--hostname=localhost", - "--no-cluster", - "--app-project-path=${workspaceRoot}/examples/electron", - "--no-app-auto-install" - ], - "env": { - "NODE_ENV": "development" - }, - "sourceMaps": true, - "outFiles": [ - "${workspaceRoot}/examples/electron/src-gen/frontend/electron-main.js", - "${workspaceRoot}/examples/electron/src-gen/backend/main.js", - "${workspaceRoot}/examples/electron/lib/**/*.js", - "${workspaceRoot}/packages/*/lib/**/*.js", - "${workspaceRoot}/dev-packages/*/lib/**/*.js" - ], - "smartStep": true, - "internalConsoleOptions": "openOnSessionStart", - "outputCapture": "std" - }, - { - "type": "node", - "request": "launch", - "name": "Launch Backend", - "program": "${workspaceRoot}/examples/browser/src-gen/backend/main.js", - "args": [ - "--hostname=0.0.0.0", - "--port=3000", - "--no-cluster", - "--app-project-path=${workspaceRoot}/examples/browser", - "--no-app-auto-install", - "--plugins=local-dir:plugins" - ], - "env": { - "NODE_ENV": "development" - }, - "sourceMaps": true, - "outFiles": [ - "${workspaceRoot}/examples/browser/src-gen/backend/*.js", - "${workspaceRoot}/examples/browser/lib/**/*.js", - "${workspaceRoot}/packages/*/lib/**/*.js", - "${workspaceRoot}/dev-packages/*/lib/**/*.js" - ], - "smartStep": true, - "internalConsoleOptions": "openOnSessionStart", - "outputCapture": "std" - }, - { - "type": "node", - "request": "launch", - "name": "Launch Backend (eclipse.jdt.ls)", - "program": "${workspaceRoot}/examples/browser/src-gen/backend/main.js", - "args": [ - "--log-level=debug", - "--root-dir=${workspaceRoot}/../eclipse.jdt.ls/org.eclipse.jdt.ls.core", - "--port=3000", - "--no-cluster", - "--no-app-auto-install" - ], - "env": { - "NODE_ENV": "development" - }, - "sourceMaps": true, - "outFiles": [ - "${workspaceRoot}/examples/browser/src-gen/backend/*.js", - "${workspaceRoot}/examples/browser/lib/**/*.js", - "${workspaceRoot}/packages/*/lib/**/*.js", - "${workspaceRoot}/dev-packages/*/lib/**/*.js" - ], - "smartStep": true, - "internalConsoleOptions": "openOnSessionStart", - "outputCapture": "std" - }, - { - "type": "node", - "request": "launch", - "protocol": "inspector", - "name": "Run Mocha Test", - "program": "${workspaceRoot}/node_modules/mocha/bin/_mocha", - "args": [ - "--no-timeouts", - "--colors", - "--opts", - "${workspaceRoot}/configs/mocha.opts", - "**/${fileBasenameNoExtension}.js" - ], - "env": { - "TS_NODE_PROJECT": "${workspaceRoot}/tsconfig.json" - }, - "sourceMaps": true, - "smartStep": true, - "internalConsoleOptions": "openOnSessionStart", - "outputCapture": "std" - }, - { - "name": "Launch Frontend", - "type": "chrome", - "request": "launch", - "url": "http://localhost:3000/", - "webRoot": "${workspaceRoot}" - } - ] + // Use IntelliSense to learn about possible Node.js debug attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "attach", + "name": "Attach by Process ID", + "processId": "${command:PickProcess}" + }, + { + "type": "node", + "request": "launch", + "name": "Launch with Node.js", + "program": "${file}" + }, + { + "type": "node", + "request": "launch", + "name": "Launch Electron Backend", + "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron", + "windows": { + "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron.cmd" + }, + "program": "${workspaceRoot}/examples/electron/src-gen/frontend/electron-main.js", + "protocol": "inspector", + "args": [ + "--log-level=debug", + "--hostname=localhost", + "--no-cluster", + "--app-project-path=${workspaceRoot}/examples/electron", + "--no-app-auto-install" + ], + "env": { + "NODE_ENV": "development" + }, + "sourceMaps": true, + "outFiles": [ + "${workspaceRoot}/examples/electron/src-gen/frontend/electron-main.js", + "${workspaceRoot}/examples/electron/src-gen/backend/main.js", + "${workspaceRoot}/examples/electron/lib/**/*.js", + "${workspaceRoot}/packages/*/lib/**/*.js", + "${workspaceRoot}/dev-packages/*/lib/**/*.js" + ], + "smartStep": true, + "internalConsoleOptions": "openOnSessionStart", + "outputCapture": "std" + }, + { + "type": "node", + "request": "launch", + "name": "Launch Backend", + "program": "${workspaceRoot}/examples/browser/src-gen/backend/main.js", + "args": [ + "--hostname=0.0.0.0", + "--port=3000", + "--no-cluster", + "--app-project-path=${workspaceRoot}/examples/browser", + "--no-app-auto-install", + "--plugins=local-dir:plugins" + ], + "env": { + "NODE_ENV": "development" + }, + "sourceMaps": true, + "outFiles": [ + "${workspaceRoot}/examples/browser/src-gen/backend/*.js", + "${workspaceRoot}/examples/browser/lib/**/*.js", + "${workspaceRoot}/packages/*/lib/**/*.js", + "${workspaceRoot}/dev-packages/*/lib/**/*.js" + ], + "smartStep": true, + "internalConsoleOptions": "openOnSessionStart", + "outputCapture": "std" + }, + { + "type": "node", + "request": "launch", + "name": "Launch Backend (eclipse.jdt.ls)", + "program": "${workspaceRoot}/examples/browser/src-gen/backend/main.js", + "args": [ + "--log-level=debug", + "--root-dir=${workspaceRoot}/../eclipse.jdt.ls/org.eclipse.jdt.ls.core", + "--port=3000", + "--no-cluster", + "--no-app-auto-install" + ], + "env": { + "NODE_ENV": "development" + }, + "sourceMaps": true, + "outFiles": [ + "${workspaceRoot}/examples/browser/src-gen/backend/*.js", + "${workspaceRoot}/examples/browser/lib/**/*.js", + "${workspaceRoot}/packages/*/lib/**/*.js", + "${workspaceRoot}/dev-packages/*/lib/**/*.js" + ], + "smartStep": true, + "internalConsoleOptions": "openOnSessionStart", + "outputCapture": "std" + }, + { + "type": "node", + "request": "launch", + "protocol": "inspector", + "name": "Run Mocha Test", + "program": "${workspaceRoot}/node_modules/mocha/bin/_mocha", + "args": [ + "--no-timeouts", + "--colors", + "--opts", + "${workspaceRoot}/configs/mocha.opts", + "**/${fileBasenameNoExtension}.js" + ], + "env": { + "TS_NODE_PROJECT": "${workspaceRoot}/tsconfig.json" + }, + "sourceMaps": true, + "smartStep": true, + "internalConsoleOptions": "openOnSessionStart", + "outputCapture": "std" + }, + { + "name": "Launch Frontend", + "type": "chrome", + "request": "launch", + "url": "http://localhost:3000/", + "webRoot": "${workspaceRoot}" + }, + { + "name": "Launch VS Code Tests", + "type": "node", + "request": "launch", + "args": [ + "${workspaceFolder}/examples/browser/src-gen/backend/main.js", + "${workspaceFolder}/plugins/vscode-api-tests/testWorkspace", + "--port", + "3030", + "--hostname", + "0.0.0.0", + "--extensionTestsPath=${workspaceFolder}/plugins/vscode-api-tests/out/singlefolder-tests", + "--hosted-plugin-inspect=9339" + ], + "env": { + "THEIA_DEFAULT_PLUGINS": "local-dir:${workspaceFolder}/plugins" + }, + "stopOnEntry": false, + "sourceMaps": true, + "outFiles": [ + "${workspaceFolder}/../.js" + ] + } + ] } diff --git a/packages/plugin-ext-vscode/src/browser/plugin-vscode-commands-contribution.ts b/packages/plugin-ext-vscode/src/browser/plugin-vscode-commands-contribution.ts index fef6bd3cc352c..5b827ce5c6a83 100644 --- a/packages/plugin-ext-vscode/src/browser/plugin-vscode-commands-contribution.ts +++ b/packages/plugin-ext-vscode/src/browser/plugin-vscode-commands-contribution.ts @@ -23,11 +23,13 @@ import { ContextKeyService } from '@theia/core/lib/browser/context-key-service'; import { DiffService } from '@theia/workspace/lib/browser/diff-service'; import { EditorManager } from '@theia/editor/lib/browser'; import { WebviewWidget } from '@theia/plugin-ext/lib/main/browser/webview/webview'; -import { ApplicationShell } from '@theia/core/lib/browser'; +import { ApplicationShell, NavigatableWidget, OpenerService, open, Saveable } from '@theia/core/lib/browser'; import { ResourceProvider } from '@theia/core'; import { ViewColumn } from '@theia/plugin-ext/lib/plugin/types-impl'; import { TextDocumentShowOptions } from '@theia/plugin-ext/lib/api/model'; import { fromViewColumn } from '@theia/plugin-ext/lib/plugin/type-converters'; +import { WorkspaceCommands } from '@theia/workspace/lib/browser'; +import { createUntitledResource } from '@theia/plugin-ext/lib/main/browser/editor/untitled-resource'; export namespace VscodeCommands { export const OPEN: Command = { @@ -61,6 +63,8 @@ export class PluginVscodeCommandsContribution implements CommandContribution { protected readonly resources: ResourceProvider; @inject(DiffService) protected readonly diffService: DiffService; + @inject(OpenerService) + protected readonly openerService: OpenerService; registerCommands(commands: CommandRegistry): void { commands.registerCommand(VscodeCommands.OPEN, { @@ -133,8 +137,171 @@ export class PluginVscodeCommandsContribution implements CommandContribution { }); } - } - ); + }); + + // https://code.visualstudio.com/docs/getstarted/keybindings#_navigation + /* + * internally, in VS Code, any widget opened in the main area is represented as an editor + * operations below apply to them, but not to side-bar widgets, like the explorer + * + * in Theia, there are not such difference and any widget can be put in any area + * because of it we filter out editors from views based on `NavigatableWidget.is` + * and apply actions only to them + */ + commands.registerCommand({ id: 'workbench.action.files.newUntitledFile' }, { + execute: () => open(this.openerService, createUntitledResource().uri) + }); + commands.registerCommand({ id: 'workbench.action.files.openFile' }, { + execute: () => commands.executeCommand(WorkspaceCommands.OPEN_FILE.id) + }); + commands.registerCommand({ id: 'workbench.action.files.openFolder' }, { + execute: () => commands.executeCommand(WorkspaceCommands.OPEN_FOLDER.id) + }); + commands.registerCommand({ id: 'workbench.action.files.save', }, { + execute: (uri?: monaco.Uri) => { + if (uri) { + const uriString = uri.toString(); + const widget = this.shell.widgets.find(w => { + const resourceUri = Saveable.is(w) && NavigatableWidget.is(w) && w.getResourceUri(); + return (resourceUri && resourceUri.toString()) === uriString; + }); + if (Saveable.is(widget)) { + Saveable.save(widget); + } + } else { + this.shell.save(); + } + } + }); + commands.registerCommand({ id: 'workbench.action.files.saveAll', }, { + execute: () => this.shell.saveAll() + }); + commands.registerCommand({ id: 'workbench.action.closeActiveEditor' }, { + execute: (uri?: monaco.Uri) => { + let widget = this.editorManager.currentEditor || this.shell.currentWidget; + if (uri) { + const uriString = uri.toString(); + widget = this.shell.widgets.find(w => { + const resourceUri = NavigatableWidget.is(w) && w.getResourceUri(); + return (resourceUri && resourceUri.toString()) === uriString; + }); + } + if (NavigatableWidget.is(widget)) { + widget.close(); + } + } + }); + commands.registerCommand({ id: 'workbench.action.closeOtherEditors' }, { + execute: (uri?: monaco.Uri) => { + let editor = this.editorManager.currentEditor || this.shell.currentWidget; + if (uri) { + const uriString = uri.toString(); + editor = this.editorManager.all.find(e => { + const resourceUri = e.getResourceUri(); + return (resourceUri && resourceUri.toString()) === uriString; + }); + } + for (const widget of this.shell.widgets) { + if (NavigatableWidget.is(widget) && widget !== editor) { + widget.close(); + } + } + } + }); + commands.registerCommand({ id: 'workbench.action.closeEditorsInGroup' }, { + execute: (uri?: monaco.Uri) => { + let editor = this.editorManager.currentEditor || this.shell.currentWidget; + if (uri) { + const uriString = uri.toString(); + editor = this.editorManager.all.find(e => { + const resourceUri = e.getResourceUri(); + return (resourceUri && resourceUri.toString()) === uriString; + }); + } + if (editor) { + const tabBar = this.shell.getTabBarFor(editor); + if (tabBar) { + this.shell.closeTabs(tabBar, + ({ owner }) => NavigatableWidget.is(owner) && owner !== editor + ); + } + } + } + }); + commands.registerCommand({ id: 'workbench.action.closeEditorsInOtherGroups' }, { + execute: () => { + const editor = this.editorManager.currentEditor || this.shell.currentWidget; + if (editor) { + const editorTabBar = this.shell.getTabBarFor(editor); + for (const tabBar of this.shell.allTabBars) { + if (tabBar !== editorTabBar) { + this.shell.closeTabs(tabBar, + ({ owner }) => NavigatableWidget.is(owner) + ); + } + } + } + } + }); + commands.registerCommand({ id: 'workbench.action.closeEditorsToTheLeft' }, { + execute: () => { + const editor = this.editorManager.currentEditor || this.shell.currentWidget; + if (editor) { + const tabBar = this.shell.getTabBarFor(editor); + if (tabBar) { + let left = true; + this.shell.closeTabs(tabBar, + ({ owner }) => { + if (owner === editor) { + left = false; + return false; + } + return left && NavigatableWidget.is(owner); + } + ); + } + } + } + }); + commands.registerCommand({ id: 'workbench.action.closeEditorsToTheRight' }, { + execute: () => { + const editor = this.editorManager.currentEditor || this.shell.currentWidget; + if (editor) { + const tabBar = this.shell.getTabBarFor(editor); + if (tabBar) { + let left = true; + this.shell.closeTabs(tabBar, + ({ owner }) => { + if (owner === editor) { + left = false; + return false; + } + return !left && NavigatableWidget.is(owner); + } + ); + } + } + } + }); + commands.registerCommand({ id: 'workbench.action.closeAllEditors' }, { + execute: () => { + for (const widget of this.shell.widgets) { + if (NavigatableWidget.is(widget)) { + widget.close(); + } + } + } + }); + /** + * TODO: + * Keep Open workbench.action.keepEditor + * Open Next workbench.action.openNextRecentlyUsedEditorInGroup + * Open Previous workbench.action.openPreviousRecentlyUsedEditorInGroup + * Copy Path of Active File workbench.action.files.copyPathOfActiveFile + * Reveal Active File in Windows workbench.action.files.revealActiveFileInWindows + * Show Opened File in New Window workbench.action.files.showOpenedFileInNewWindow + * Compare Opened File With workbench.files.action.compareFileWith + */ } private getHtml(body: String) { diff --git a/packages/plugin-ext/src/main/browser/documents-main.ts b/packages/plugin-ext/src/main/browser/documents-main.ts index 53adbbc2841d4..9d5947054a2ce 100644 --- a/packages/plugin-ext/src/main/browser/documents-main.ts +++ b/packages/plugin-ext/src/main/browser/documents-main.ts @@ -121,7 +121,8 @@ export class DocumentsMainImpl implements DocumentsMain { async $tryCreateDocument(options?: { language?: string; content?: string; }): Promise { const language = options && options.language; const content = options && options.content; - return createUntitledResource(content, language); + const resource = createUntitledResource(content, language); + return monaco.Uri.parse(resource.uri.toString()); } async $tryOpenDocument(uri: UriComponents, options?: TextDocumentShowOptions): Promise { diff --git a/packages/plugin-ext/src/main/browser/editor/untitled-resource.ts b/packages/plugin-ext/src/main/browser/editor/untitled-resource.ts index 55e98e7bd75da..d68b1067925c9 100644 --- a/packages/plugin-ext/src/main/browser/editor/untitled-resource.ts +++ b/packages/plugin-ext/src/main/browser/editor/untitled-resource.ts @@ -17,7 +17,7 @@ import { ResourceResolver, Resource } from '@theia/core'; import URI from '@theia/core/lib/common/uri'; import { injectable } from 'inversify'; -import { UriComponents, Schemes } from '../../../common/uri-components'; +import { Schemes } from '../../../common/uri-components'; const resources = new Map(); let index = 0; @@ -46,7 +46,7 @@ export class UntitledResource implements Resource { } } -export function createUntitledResource(content?: string, language?: string): UriComponents { +export function createUntitledResource(content?: string, language?: string): UntitledResource { let extension; if (language) { for (const lang of monaco.languages.getLanguages()) { @@ -58,6 +58,5 @@ export function createUntitledResource(content?: string, language?: string): Uri } } } - const resource = new UntitledResource(new URI().withScheme(Schemes.UNTITLED).withPath(`/Untitled-${index++}${extension ? extension : ''}`), content); - return monaco.Uri.parse(resource.uri.toString()); + return new UntitledResource(new URI().withScheme(Schemes.UNTITLED).withPath(`/Untitled-${index++}${extension ? extension : ''}`), content); } diff --git a/packages/plugin-ext/src/main/browser/plugin-frontend-contribution.ts b/packages/plugin-ext/src/main/browser/plugin-frontend-contribution.ts index 9f2ec88b07ae1..c1b0171d18754 100644 --- a/packages/plugin-ext/src/main/browser/plugin-frontend-contribution.ts +++ b/packages/plugin-ext/src/main/browser/plugin-frontend-contribution.ts @@ -54,11 +54,6 @@ export class PluginApiFrontendContribution implements CommandContribution { execute: () => this.pluginExtDeployCommandService.deploy() }); - // this command only for compatibility reason - commands.registerCommand({ id: 'workbench.action.closeActiveEditor' }, { - execute: () => commands.executeCommand('core.close.tab') - }); - commands.registerCommand(OpenUriCommandHandler.COMMAND_METADATA, { execute: (arg: URI) => this.openUriCommandHandler.execute(arg), isVisible: () => false