diff --git a/CHANGELOG.md b/CHANGELOG.md
index 401b4d65cfcb7..bc446b0b9b947 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,12 @@
# Change Log
+## v1.1.0
+
+Breaking changes:
+
+- [plugin] removed `configStorage` argument from `PluginManager.registerPlugin`.
+Use `PluginManager.configStorage` property instead. [#7265](https://github.com/eclipse-theia/theia/pull/7265#discussion_r399956070)
+
## v1.0.0
- [core] added functionality to ensure that nodes are refreshed properly on tree expansion [#7400](https://github.com/eclipse-theia/theia/pull/7400)
diff --git a/dev-packages/application-manager/src/expose-loader.ts b/dev-packages/application-manager/src/expose-loader.ts
index 43ecf4dfe50ec..3a23ce118ef9d 100644
--- a/dev-packages/application-manager/src/expose-loader.ts
+++ b/dev-packages/application-manager/src/expose-loader.ts
@@ -53,7 +53,7 @@ export = function (this: webpack.loader.LoaderContext, source: string, sourceMap
this.cacheable();
}
- let modulePackage = modulePackages.find(({ dir }) => this.resourcePath.startsWith(dir));
+ let modulePackage = modulePackages.find(({ dir }) => this.resourcePath.startsWith(dir + '/'));
if (modulePackage) {
this.callback(undefined, exposeModule(modulePackage, this.resourcePath, source), sourceMap);
return;
diff --git a/examples/api-tests/src/api-tests.d.ts b/examples/api-tests/src/api-tests.d.ts
new file mode 100644
index 0000000000000..4e8bfa6aad80b
--- /dev/null
+++ b/examples/api-tests/src/api-tests.d.ts
@@ -0,0 +1,21 @@
+/********************************************************************************
+ * Copyright (C) 2020 TypeFox and others.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the Eclipse
+ * Public License v. 2.0 are satisfied: GNU General Public License, version 2
+ * with the GNU Classpath Exception which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ ********************************************************************************/
+
+interface Window {
+ theia: {
+ container: import('inversify').Container
+ }
+}
diff --git a/examples/api-tests/src/launch-preferences.spec.js b/examples/api-tests/src/launch-preferences.spec.js
index 265cfaed9ff00..c47cfc2309ae9 100644
--- a/examples/api-tests/src/launch-preferences.spec.js
+++ b/examples/api-tests/src/launch-preferences.spec.js
@@ -15,8 +15,6 @@
********************************************************************************/
// @ts-check
-///
-
/* eslint-disable no-unused-expressions, @typescript-eslint/no-explicit-any */
/**
@@ -28,6 +26,7 @@
* See https://github.com/akosyakov/vscode-launch/blob/master/src/test/extension.test.ts
*/
describe('Launch Preferences', function () {
+ this.timeout(5000);
const { assert } = chai;
@@ -38,8 +37,7 @@ describe('Launch Preferences', function () {
const { MonacoTextModelService } = require('@theia/monaco/lib/browser/monaco-text-model-service');
const { MonacoWorkspace } = require('@theia/monaco/lib/browser/monaco-workspace');
- /** @type {import('inversify').Container} */
- const container = window['theia'].container;
+ const container = window.theia.container;
/** @type {import('@theia/core/lib/browser/preferences/preference-service').PreferenceService} */
const preferences = container.get(PreferenceService);
const workspaceService = container.get(WorkspaceService);
@@ -425,7 +423,7 @@ describe('Launch Preferences', function () {
]);
}
- const client = fileSystem.getClient();
+ const client = /** @type {import('@theia/filesystem/lib/common/filesystem').FileSystemClient} */ (fileSystem.getClient());
const originalShouldOverwrite = client.shouldOverwrite;
before(async () => {
@@ -460,6 +458,7 @@ describe('Launch Preferences', function () {
/** @typedef {monaco.editor.IReference} ConfigModelReference */
/** @type {ConfigModelReference[]} */
beforeEach(async () => {
+ /** @type {Promise[]} */
const promises = [];
/**
* @param {string} name
@@ -507,11 +506,13 @@ describe('Launch Preferences', function () {
});
testIt('get from undefined', () => {
+ /** @type {any} */
const config = preferences.get('launch', undefined, undefined);
assert.deepStrictEqual(JSON.parse(JSON.stringify(config)), expectation);
});
testIt('get from rootUri', () => {
+ /** @type {any} */
const config = preferences.get('launch', undefined, rootUri.toString());
assert.deepStrictEqual(JSON.parse(JSON.stringify(config)), expectation);
});
diff --git a/examples/api-tests/src/menus.spec.js b/examples/api-tests/src/menus.spec.js
index bbaa8d6547385..5c8ef3da4e45f 100644
--- a/examples/api-tests/src/menus.spec.js
+++ b/examples/api-tests/src/menus.spec.js
@@ -29,11 +29,10 @@ describe('Menus', function () {
const { ProblemContribution } = require('@theia/markers/lib/browser/problem/problem-contribution');
const { SearchInWorkspaceFrontendContribution } = require('@theia/search-in-workspace/lib/browser/search-in-workspace-frontend-contribution');
- /** @type {import('inversify').Container} */
- const container = window['theia'].container;
+ const container = window.theia.container;
const shell = container.get(ApplicationShell);
const menuBarContribution = container.get(BrowserMenuBarContribution);
- const menuBar = menuBarContribution.menuBar;
+ const menuBar = /** @type {import('@theia/core/lib/browser/menu/browser-menu-plugin').MenuBarWidget} */ (menuBarContribution.menuBar);
for (const contribution of [
container.get(CallHierarchyContribution),
diff --git a/examples/api-tests/src/monaco-api.spec.js b/examples/api-tests/src/monaco-api.spec.js
index 958ec6ed960b7..6f9c71a2fc557 100644
--- a/examples/api-tests/src/monaco-api.spec.js
+++ b/examples/api-tests/src/monaco-api.spec.js
@@ -15,8 +15,8 @@
********************************************************************************/
// @ts-check
-///
describe('Monaco API', async function () {
+ this.timeout(5000);
const { assert } = chai;
@@ -27,8 +27,7 @@ describe('Monaco API', async function () {
const { MonacoResolvedKeybinding } = require('@theia/monaco/lib/browser/monaco-resolved-keybinding');
const { MonacoTextmateService } = require('@theia/monaco/lib/browser/textmate/monaco-textmate-service');
- /** @type {import('inversify').Container} */
- const container = window['theia'].container;
+ const container = window.theia.container;
const editorManager = container.get(EditorManager);
const workspaceService = container.get(WorkspaceService);
const textmateService = container.get(MonacoTextmateService);
@@ -40,7 +39,7 @@ describe('Monaco API', async function () {
const editor = await editorManager.open(new Uri.default(root.uri).resolve('package.json'), {
mode: 'reveal'
});
- monacoEditor = MonacoEditor.get(editor);
+ monacoEditor = /** @type {MonacoEditor} */ (MonacoEditor.get(editor));
});
it('KeybindingService.resolveKeybinding', () => {
@@ -65,24 +64,25 @@ describe('Monaco API', async function () {
assert.deepStrictEqual({
label, ariaLabel, electronAccelerator, userSettingsLabel, WYSIWYG, chord, parts, dispatchParts
}, {
- label: "Ctrl+Shift+Alt+K",
- ariaLabel: "Ctrl+Shift+Alt+K",
- electronAccelerator: null,
- userSettingsLabel: "ctrl+shift+alt+K",
- WYSIWYG: true,
- chord: false,
- parts: [{
- altKey: true,
- ctrlKey: true,
- keyAriaLabel: "K",
- keyLabel: "K",
- metaKey: false,
- shiftKey: true
- }],
- dispatchParts: [
- "ctrl+shift+alt+K"
- ]
- });
+ label: 'Ctrl+Shift+Alt+K',
+ ariaLabel: 'Ctrl+Shift+Alt+K',
+ // eslint-disable-next-line no-null/no-null
+ electronAccelerator: null,
+ userSettingsLabel: 'ctrl+shift+alt+K',
+ WYSIWYG: true,
+ chord: false,
+ parts: [{
+ altKey: true,
+ ctrlKey: true,
+ keyAriaLabel: 'K',
+ keyLabel: 'K',
+ metaKey: false,
+ shiftKey: true
+ }],
+ dispatchParts: [
+ 'ctrl+shift+alt+K'
+ ]
+ });
} else {
assert.fail(`resolvedKeybinding must be of ${MonacoResolvedKeybinding.name} type`);
}
@@ -94,16 +94,16 @@ describe('Monaco API', async function () {
const toDispose = monaco.modes.TokenizationRegistry.onDidChange(() => {
toDispose.dispose();
resolve();
- })
+ });
});
textmateService['themeService'].setCurrentTheme('light');
await didChangeColorMap;
}
const textMateColorMap = textmateService['grammarRegistry'].getColorMap();
- assert.notEqual(textMateColorMap.indexOf('#795E26'), -1, 'Expected custom toke colors for the ligth theme to be enabled.')
+ assert.notEqual(textMateColorMap.indexOf('#795E26'), -1, 'Expected custom toke colors for the ligth theme to be enabled.');
- const monacoColorMap = monaco.modes.TokenizationRegistry.getColorMap().
+ const monacoColorMap = (monaco.modes.TokenizationRegistry.getColorMap() || []).
splice(0, textMateColorMap.length).map(c => c.toString().toUpperCase());
assert.deepStrictEqual(monacoColorMap, textMateColorMap, 'Expected textmate colors to have the same index in the monaco color map.');
});
diff --git a/examples/api-tests/src/saveable.spec.js b/examples/api-tests/src/saveable.spec.js
index d67e294935f6c..bdfc2ab704c7a 100644
--- a/examples/api-tests/src/saveable.spec.js
+++ b/examples/api-tests/src/saveable.spec.js
@@ -16,22 +16,21 @@
// @ts-check
describe('Saveable', function () {
+ this.timeout(5000);
const { assert } = chai;
const { EditorManager } = require('@theia/editor/lib/browser/editor-manager');
const { EditorWidget } = require('@theia/editor/lib/browser/editor-widget');
- const { PreferenceService, PreferenceScope } = require('@theia/core/lib/browser/preferences/preference-service');
+ const { PreferenceService } = require('@theia/core/lib/browser/preferences/preference-service');
const Uri = require('@theia/core/lib/common/uri');
const { Saveable, SaveableWidget } = require('@theia/core/lib/browser/saveable');
const { WorkspaceService } = require('@theia/workspace/lib/browser/workspace-service');
const { FileSystem } = require('@theia/filesystem/lib/common/filesystem');
const { MonacoEditor } = require('@theia/monaco/lib/browser/monaco-editor');
- const { DisposableCollection } = require('@theia/core/lib/common/disposable');
const { Deferred } = require('@theia/core/lib/common/promise-util');
- /** @type {import('inversify').Container} */
- const container = window['theia'].container;
+ const container = window.theia.container;
const editorManager = container.get(EditorManager);
const workspaceService = container.get(WorkspaceService);
/** @type {import('@theia/filesystem/lib/common/filesystem').FileSystem} */
@@ -79,9 +78,9 @@ describe('Saveable', function () {
assert.isTrue(Saveable.isDirty(widget), `should be dirty before '${edit}' save`);
await Saveable.save(widget);
assert.isFalse(Saveable.isDirty(widget), `should NOT be dirty after '${edit}' save`);
- assert.equal(editor.getControl().getValue(), edit, `model should be updated with '${edit}'`);
+ assert.equal(editor.getControl().getValue().trimRight(), edit, `model should be updated with '${edit}'`);
const state = await fileSystem.resolveContent(fileUri.toString());
- assert.equal(state.content, edit, `fs should be updated with '${edit}'`);
+ assert.equal(state.content.trimRight(), edit, `fs should be updated with '${edit}'`);
}
});
@@ -129,7 +128,7 @@ describe('Saveable', function () {
assert.isTrue(outOfSync, 'file should be out of sync');
assert.equal(outOfSyncCount, 1, 'user should be prompted only once with out of sync dialog');
assert.isTrue(Saveable.isDirty(widget), 'should be dirty after rejected save');
- assert.equal(editor.getControl().getValue(), longContent.substring(3), 'model should be updated');
+ assert.equal(editor.getControl().getValue().trimRight(), longContent.substring(3), 'model should be updated');
const state = await fileSystem.resolveContent(fileUri.toString());
assert.equal(state.content, 'baz', 'fs should NOT be updated');
});
@@ -151,7 +150,7 @@ describe('Saveable', function () {
await Saveable.save(widget);
assert.isTrue(outOfSync, 'file should be out of sync');
assert.isTrue(Saveable.isDirty(widget), 'should be dirty after rejected save');
- assert.equal(editor.getControl().getValue(), 'bar', 'model should be updated');
+ assert.equal(editor.getControl().getValue().trimRight(), 'bar', 'model should be updated');
let state = await fileSystem.resolveContent(fileUri.toString());
assert.equal(state.content, 'baz', 'fs should NOT be updated');
@@ -164,9 +163,9 @@ describe('Saveable', function () {
await Saveable.save(widget);
assert.isTrue(outOfSync, 'file should be out of sync');
assert.isFalse(Saveable.isDirty(widget), 'should NOT be dirty after save');
- assert.equal(editor.getControl().getValue(), 'bar', 'model should be updated');
+ assert.equal(editor.getControl().getValue().trimRight(), 'bar', 'model should be updated');
state = await fileSystem.resolveContent(fileUri.toString());
- assert.equal(state.content, 'bar', 'fs should be updated');
+ assert.equal(state.content.trimRight(), 'bar', 'fs should be updated');
});
it('accept new save', async () => {
@@ -181,9 +180,9 @@ describe('Saveable', function () {
await Saveable.save(widget);
assert.isTrue(outOfSync, 'file should be out of sync');
assert.isFalse(Saveable.isDirty(widget), 'should NOT be dirty after save');
- assert.equal(editor.getControl().getValue(), 'bar', 'model should be updated');
+ assert.equal(editor.getControl().getValue().trimRight(), 'bar', 'model should be updated');
const state = await fileSystem.resolveContent(fileUri.toString());
- assert.equal(state.content, 'bar', 'fs should be updated');
+ assert.equal(state.content.trimRight(), 'bar', 'fs should be updated');
});
it('cancel save on close', async () => {
@@ -243,7 +242,7 @@ describe('Saveable', function () {
assert.isTrue(outOfSync, 'file should be out of sync');
assert.isTrue(widget.isDisposed, 'model should be disposed after close');
const state = await fileSystem.resolveContent(fileUri.toString());
- assert.equal(state.content, 'bar', 'fs should be updated');
+ assert.equal(state.content.trimRight(), 'bar', 'fs should be updated');
});
it('normal close', async () => {
@@ -254,7 +253,7 @@ describe('Saveable', function () {
});
assert.isTrue(widget.isDisposed, 'model should be disposed after close');
const state = await fileSystem.resolveContent(fileUri.toString());
- assert.equal(state.content, 'bar', 'fs should be updated');
+ assert.equal(state.content.trimRight(), 'bar', 'fs should be updated');
});
it('delete file for saved', async () => {
@@ -320,7 +319,7 @@ describe('Saveable', function () {
assert.isFalse(Saveable.isDirty(widget), 'should NOT be dirty after save');
assert.isTrue(editor.document.valid, 'should be valid after save');
const state = await fileSystem.resolveContent(fileUri.toString());
- assert.equal(state.content, 'bar', 'fs should be updated');
+ assert.equal(state.content.trimRight(), 'bar', 'fs should be updated');
});
it('move file for saved', async function () {
diff --git a/examples/api-tests/src/shell.spec.js b/examples/api-tests/src/shell.spec.js
index d1bc9358eac14..499b2db496c1c 100644
--- a/examples/api-tests/src/shell.spec.js
+++ b/examples/api-tests/src/shell.spec.js
@@ -22,8 +22,7 @@ describe('Shell', function () {
const { ApplicationShell } = require('@theia/core/lib/browser/shell/application-shell');
const { StatusBarImpl } = require('@theia/core/lib/browser/status-bar');
- /** @type {import('inversify').Container} */
- const container = window['theia'].container;
+ const container = window.theia.container;
const shell = container.get(ApplicationShell);
const statusBar = container.get(StatusBarImpl);
diff --git a/examples/api-tests/src/typescript.spec.js b/examples/api-tests/src/typescript.spec.js
new file mode 100644
index 0000000000000..2afe1ad68d970
--- /dev/null
+++ b/examples/api-tests/src/typescript.spec.js
@@ -0,0 +1,776 @@
+/********************************************************************************
+ * Copyright (C) 2020 TypeFox and others.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the Eclipse
+ * Public License v. 2.0 are satisfied: GNU General Public License, version 2
+ * with the GNU Classpath Exception which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ ********************************************************************************/
+
+// @ts-check
+describe('TypeScript', function () {
+ this.timeout(30000);
+
+ const { assert } = chai;
+
+ const Uri = require('@theia/core/lib/common/uri');
+ const { DisposableCollection } = require('@theia/core/lib/common/disposable');
+ const { BrowserMainMenuFactory } = require('@theia/core/lib/browser/menu/browser-menu-plugin');
+ const { EditorManager } = require('@theia/editor/lib/browser/editor-manager');
+ const { EditorWidget } = require('@theia/editor/lib/browser/editor-widget');
+ const { EDITOR_CONTEXT_MENU } = require('@theia/editor/lib/browser/editor-menu');
+ const { WorkspaceService } = require('@theia/workspace/lib/browser/workspace-service');
+ const { MonacoEditor } = require('@theia/monaco/lib/browser/monaco-editor');
+ const { HostedPluginSupport } = require('@theia/plugin-ext/lib/hosted/browser/hosted-plugin');
+ const { ContextKeyService } = require('@theia/core/lib/browser/context-key-service');
+ const { CommandRegistry } = require('@theia/core/lib/common/command');
+ const { KeybindingRegistry } = require('@theia/core/lib/browser/keybinding');
+ const { OpenerService, open } = require('@theia/core/lib/browser/opener-service');
+ const { EditorPreviewWidget } = require('@theia/editor-preview/lib/browser/editor-preview-widget');
+ const { animationFrame } = require('@theia/core/lib/browser/browser');
+ const { PreferenceService, PreferenceScope } = require('@theia/core/lib/browser/preferences/preference-service');
+ const { ProgressStatusBarItem } = require('@theia/core/lib/browser/progress-status-bar-item');
+ const { FileSystem } = require('@theia/filesystem/lib/common/filesystem');
+
+ const container = window.theia.container;
+ const editorManager = container.get(EditorManager);
+ const workspaceService = container.get(WorkspaceService);
+ const menuFactory = container.get(BrowserMainMenuFactory);
+ const pluginService = container.get(HostedPluginSupport);
+ const contextKeyService = container.get(ContextKeyService);
+ const commands = container.get(CommandRegistry);
+ const openerService = container.get(OpenerService);
+ const keybindings = container.get(KeybindingRegistry);
+ /** @type {import('@theia/core/lib/browser/preferences/preference-service').PreferenceService} */
+ const preferences = container.get(PreferenceService);
+ const progressStatusBarItem = container.get(ProgressStatusBarItem);
+ /** @type {import('@theia/filesystem/lib/common/filesystem').FileSystem} */
+ const fileSystem = container.get(FileSystem);
+
+ const pluginId = 'vscode.typescript-language-features';
+ const rootUri = new Uri.default(workspaceService.tryGetRoots()[0].uri);
+ const serverUri = rootUri.resolve('src-gen/backend/test-server.js');
+ const inversifyUri = rootUri.resolve('../../node_modules/inversify/dts/inversify.d.ts').normalizePath();
+ const containerUri = rootUri.resolve('../../node_modules/inversify/dts/container/container.d.ts').normalizePath();
+
+ before(async function () {
+ await fileSystem.createFile(serverUri.toString(), {
+ content: `// @ts-check
+require('reflect-metadata');
+const path = require('path');
+const express = require('express');
+const { Container } = require('inversify');
+const { BackendApplication, CliManager } = require('@theia/core/lib/node');
+const { backendApplicationModule } = require('@theia/core/lib/node/backend-application-module');
+const { messagingBackendModule } = require('@theia/core/lib/node/messaging/messaging-backend-module');
+const { loggerBackendModule } = require('@theia/core/lib/node/logger-backend-module');
+
+const container = new Container();
+container.load(backendApplicationModule);
+container.load(messagingBackendModule);
+container.load(loggerBackendModule);
+
+function load(raw) {
+ return Promise.resolve(raw.default).then(module =>
+ container.load(module)
+ )
+}
+
+function start(port, host, argv) {
+ if (argv === undefined) {
+ argv = process.argv;
+ }
+
+ const cliManager = container.get(CliManager);
+ return cliManager.initializeCli(argv).then(function () {
+ const application = container.get(BackendApplication);
+ application.use(express.static(path.join(__dirname, '../../lib')));
+ application.use(express.static(path.join(__dirname, '../../lib/index.html')));
+ return application.start(port, host);
+ });
+}
+
+module.exports = (port, host, argv) => Promise.resolve()
+ .then(function () { return Promise.resolve(require('@theia/process/lib/node/process-backend-module')).then(load) })
+ .then(function () { return Promise.resolve(require('@theia/filesystem/lib/node/filesystem-backend-module')).then(load) })
+ .then(function () { return Promise.resolve(require('@theia/filesystem/lib/node/download/file-download-backend-module')).then(load) })
+ .then(function () { return Promise.resolve(require('@theia/workspace/lib/node/workspace-backend-module')).then(load) })
+ .then(function () { return Promise.resolve(require('@theia/languages/lib/node/languages-backend-module')).then(load) })
+ .then(function () { return Promise.resolve(require('@theia/terminal/lib/node/terminal-backend-module')).then(load) })
+ .then(function () { return Promise.resolve(require('@theia/task/lib/node/task-backend-module')).then(load) })
+ .then(function () { return Promise.resolve(require('@theia/debug/lib/node/debug-backend-module')).then(load) })
+ .then(function () { return Promise.resolve(require('@theia/file-search/lib/node/file-search-backend-module')).then(load) })
+ .then(function () { return Promise.resolve(require('@theia/git/lib/node/git-backend-module')).then(load) })
+ .then(function () { return Promise.resolve(require('@theia/git/lib/node/env/git-env-module')).then(load) })
+ .then(function () { return Promise.resolve(require('@theia/json/lib/node/json-backend-module')).then(load) })
+ .then(function () { return Promise.resolve(require('@theia/metrics/lib/node/metrics-backend-module')).then(load) })
+ .then(function () { return Promise.resolve(require('@theia/mini-browser/lib/node/mini-browser-backend-module')).then(load) })
+ .then(function () { return Promise.resolve(require('@theia/search-in-workspace/lib/node/search-in-workspace-backend-module')).then(load) })
+ .then(function () { return Promise.resolve(require('@theia/plugin-ext/lib/plugin-ext-backend-module')).then(load) })
+ .then(function () { return Promise.resolve(require('@theia/plugin-dev/lib/node/plugin-dev-backend-module')).then(load) })
+ .then(function () { return Promise.resolve(require('@theia/plugin-ext-vscode/lib/node/plugin-vscode-backend-module')).then(load) })
+ .then(function () { return Promise.resolve(require('@theia/plugin-metrics/lib/node/plugin-metrics-backend-module')).then(load) })
+ .then(function () { return Promise.resolve(require('@theia/vsx-registry/lib/node/vsx-registry-backend-module')).then(load) })
+ .then(() => start(port, host, argv)).catch(reason => {
+ console.error('Failed to start the backend application.');
+ if (reason) {
+ console.error(reason);
+ }
+ throw reason;
+ });
+ `
+ });
+ await pluginService.didStart;
+ if (!pluginService.getPlugin(pluginId)) {
+ throw new Error(pluginId + ' should be started');
+ }
+ await pluginService.activatePlugin(pluginId);
+ });
+
+ after(async function () {
+ await fileSystem.delete(serverUri.toString());
+ });
+
+ beforeEach(async function () {
+ await editorManager.closeAll({ save: false });
+ });
+
+ const toTearDown = new DisposableCollection();
+ afterEach(async () => {
+ toTearDown.dispose();
+ await editorManager.closeAll({ save: false });
+ });
+
+ /**
+ * @param {Uri.default} uri
+ * @param {boolean} preview
+ */
+ async function openEditor(uri, preview = false) {
+ const widget = await open(openerService, uri, { mode: 'activate', preview });
+ const editorWidget = widget instanceof EditorPreviewWidget ? widget.editorWidget : widget instanceof EditorWidget ? widget : undefined;
+ const editor = MonacoEditor.get(editorWidget);
+ assert.isDefined(editor);
+
+ // wait till tsserver is running, see:
+ // https://github.com/microsoft/vscode/blob/93cbbc5cae50e9f5f5046343c751b6d010468200/extensions/typescript-language-features/src/extension.ts#L98-L103
+ await waitForAnimation(() => contextKeyService.match('typescript.isManagedFile'));
+
+ // wait till projects are loaded, see:
+ // https://github.com/microsoft/vscode/blob/4aac84268c6226d23828cc6a1fe45ee3982927f0/extensions/typescript-language-features/src/typescriptServiceClient.ts#L911
+ await waitForAnimation(() => !progressStatusBarItem.currentProgress);
+
+ return /** @type {MonacoEditor} */ (editor);
+ }
+
+ /**
+ * @template T
+ * @param {() => Promise | T} condition
+ * @returns {Promise}
+ */
+ function waitForAnimation(condition) {
+ return new Promise(async (resolve, dispose) => {
+ toTearDown.push({ dispose });
+ do {
+ await animationFrame();
+ } while (!condition());
+ resolve();
+ });
+ }
+
+ /**
+ * We ignore attributes on purprse since they are not stable.
+ * But structure is important for us to see whether the plain text is rendered or markdown.
+ *
+ * @param {Element} element
+ * @returns {string}
+ */
+ function nodeAsString(element, indentation = '') {
+ const header = element.tagName;
+ let body = '';
+ const childIndentation = indentation + ' ';
+ for (let i = 0; i < element.childNodes.length; i++) {
+ const childNode = element.childNodes.item(i);
+ if (childNode.nodeType === childNode.TEXT_NODE) {
+ body += childIndentation + `"${childNode.textContent}"` + '\n';
+ } else if (childNode instanceof HTMLElement) {
+ body += childIndentation + nodeAsString(childNode, childIndentation) + '\n';
+ }
+ }
+ const result = header + (body ? ' {\n' + body + indentation + '}' : '');
+ if (indentation) {
+ return result;
+ }
+ return `\n${result}\n`;
+ }
+
+ /**
+ * @param {MonacoEditor} editor
+ */
+ async function assertPeekOpened(editor) {
+ const referencesController = editor.getControl()._contributions['editor.contrib.referencesController'];
+ await waitForAnimation(() => referencesController._widget && referencesController._widget._tree.getFocus().length);
+
+ assert.isFalse(contextKeyService.match('editorTextFocus'));
+ assert.isTrue(contextKeyService.match('referenceSearchVisible'));
+ assert.isTrue(contextKeyService.match('listFocus'));
+ }
+
+ /**
+ * @param {MonacoEditor} editor
+ */
+ async function openPeek(editor) {
+ assert.isTrue(contextKeyService.match('editorTextFocus'));
+ assert.isFalse(contextKeyService.match('referenceSearchVisible'));
+ assert.isFalse(contextKeyService.match('listFocus'));
+
+ await commands.executeCommand('editor.action.peekDefinition');
+ await assertPeekOpened(editor);
+ }
+
+ async function openReference() {
+ keybindings.dispatchKeyDown('Enter');
+ await waitForAnimation(() => contextKeyService.match('listFocus'));
+ assert.isFalse(contextKeyService.match('editorTextFocus'));
+ assert.isTrue(contextKeyService.match('referenceSearchVisible'));
+ assert.isTrue(contextKeyService.match('listFocus'));
+ }
+
+ async function closePeek() {
+ keybindings.dispatchKeyDown('Escape');
+ await waitForAnimation(() => !contextKeyService.match('listFocus'));
+ assert.isTrue(contextKeyService.match('editorTextFocus'));
+ assert.isFalse(contextKeyService.match('referenceSearchVisible'));
+ assert.isFalse(contextKeyService.match('listFocus'));
+ }
+
+ it('document formating should be visible and enabled', async () => {
+ await openEditor(serverUri);
+ const menu = menuFactory.createContextMenu(EDITOR_CONTEXT_MENU);
+ const item = menu.items.find(i => i.command === 'editor.action.formatDocument');
+ if (item) {
+ assert.isTrue(item.isVisible);
+ assert.isTrue(item.isEnabled);
+ } else {
+ assert.isDefined(item);
+ }
+ });
+
+ describe('editor.action.revealDefinition', function () {
+ for (const preview of [false, true]) {
+ const from = 'an editor' + (preview ? ' preview' : '');
+ it('within ' + from, async function () {
+ const editor = await openEditor(serverUri, preview);
+ // con|tainer.load(backendApplicationModule);
+ editor.getControl().setPosition({ lineNumber: 12, column: 4 });
+ // @ts-ignore
+ assert.equal(editor.getControl().getModel().getWordAtPosition(editor.getControl().getPosition()).word, 'container');
+
+ await commands.executeCommand('editor.action.revealDefinition');
+
+ const activeEditor = /** @type {MonacoEditor} */ (MonacoEditor.get(editorManager.activeEditor));
+ // @ts-ignore
+ assert.equal(editorManager.activeEditor.parent instanceof EditorPreviewWidget, preview);
+ assert.equal(activeEditor.uri.toString(), serverUri.toString());
+ // const |container = new Container();
+ // @ts-ignore
+ const { lineNumber, column } = activeEditor.getControl().getPosition();
+ assert.deepEqual({ lineNumber, column }, { lineNumber: 11, column: 7 });
+ // @ts-ignore
+ assert.equal(activeEditor.getControl().getModel().getWordAtPosition({ lineNumber, column }).word, 'container');
+ });
+
+ it(`from ${from} to another editor`, async function () {
+ await editorManager.open(inversifyUri, { mode: 'open' });
+
+ const editor = await openEditor(serverUri, preview);
+ // const { Cont|ainer } = require('inversify');
+ editor.getControl().setPosition({ lineNumber: 5, column: 13 });
+ // @ts-ignore
+ assert.equal(editor.getControl().getModel().getWordAtPosition(editor.getControl().getPosition()).word, 'Container');
+
+ await commands.executeCommand('editor.action.revealDefinition');
+
+ const activeEditor = /** @type {MonacoEditor} */ (MonacoEditor.get(editorManager.activeEditor));
+ // @ts-ignore
+ assert.isFalse(editorManager.activeEditor.parent instanceof EditorPreviewWidget);
+ assert.equal(activeEditor.uri.toString(), inversifyUri.toString());
+ // export { |Container } from "./container/container";
+ // @ts-ignore
+ const { lineNumber, column } = activeEditor.getControl().getPosition();
+ assert.deepEqual({ lineNumber, column }, { lineNumber: 3, column: 10 });
+ // @ts-ignore
+ assert.equal(activeEditor.getControl().getModel().getWordAtPosition({ lineNumber, column }).word, 'Container');
+ });
+
+ it(`from ${from} to an editor preview`, async function () {
+ const editor = await openEditor(serverUri);
+ // const { Cont|ainer } = require('inversify');
+ editor.getControl().setPosition({ lineNumber: 5, column: 13 });
+ // @ts-ignore
+ assert.equal(editor.getControl().getModel().getWordAtPosition(editor.getControl().getPosition()).word, 'Container');
+
+ await commands.executeCommand('editor.action.revealDefinition');
+
+ const activeEditor = /** @type {MonacoEditor} */ (MonacoEditor.get(editorManager.activeEditor));
+ // @ts-ignore
+ assert.isTrue(editorManager.activeEditor.parent instanceof EditorPreviewWidget);
+ assert.equal(activeEditor.uri.toString(), inversifyUri.toString());
+ // export { |Container } from "./container/container";
+ // @ts-ignore
+ const { lineNumber, column } = activeEditor.getControl().getPosition();
+ assert.deepEqual({ lineNumber, column }, { lineNumber: 3, column: 10 });
+ // @ts-ignore
+ assert.equal(activeEditor.getControl().getModel().getWordAtPosition({ lineNumber, column }).word, 'Container');
+ });
+ }
+ });
+
+ describe('editor.action.peekDefinition', function () {
+
+ for (const preview of [false, true]) {
+ const from = 'an editor' + (preview ? ' preview' : '');
+ it('within ' + from, async function () {
+ const editor = await openEditor(serverUri, preview);
+ // con|tainer.load(backendApplicationModule);
+ editor.getControl().setPosition({ lineNumber: 12, column: 4 });
+ // @ts-ignore
+ assert.equal(editor.getControl().getModel().getWordAtPosition(editor.getControl().getPosition()).word, 'container');
+
+ await openPeek(editor);
+ await openReference();
+
+ const activeEditor = /** @type {MonacoEditor} */ (MonacoEditor.get(editorManager.activeEditor));
+ // @ts-ignore
+ assert.equal(editorManager.activeEditor.parent instanceof EditorPreviewWidget, preview);
+ assert.equal(activeEditor.uri.toString(), serverUri.toString());
+ // const |container = new Container();
+ // @ts-ignore
+ const { lineNumber, column } = activeEditor.getControl().getPosition();
+ assert.deepEqual({ lineNumber, column }, { lineNumber: 11, column: 7 });
+ // @ts-ignore
+ assert.equal(activeEditor.getControl().getModel().getWordAtPosition({ lineNumber, column }).word, 'container');
+
+ await closePeek();
+ });
+
+ it(`from ${from} to another editor`, async function () {
+ await editorManager.open(inversifyUri, { mode: 'open' });
+
+ const editor = await openEditor(serverUri, preview);
+ // const { Cont|ainer } = require('inversify');
+ editor.getControl().setPosition({ lineNumber: 5, column: 13 });
+ // @ts-ignore
+ assert.equal(editor.getControl().getModel().getWordAtPosition(editor.getControl().getPosition()).word, 'Container');
+
+ await openPeek(editor);
+ await openReference();
+
+ const activeEditor = /** @type {MonacoEditor} */ (MonacoEditor.get(editorManager.activeEditor));
+ // @ts-ignore
+ assert.isFalse(editorManager.activeEditor.parent instanceof EditorPreviewWidget);
+ assert.equal(activeEditor.uri.toString(), inversifyUri.toString());
+ // export { |Container } from "./container/container";
+ // @ts-ignore
+ const { lineNumber, column } = activeEditor.getControl().getPosition();
+ assert.deepEqual({ lineNumber, column }, { lineNumber: 3, column: 10 });
+ // @ts-ignore
+ assert.equal(activeEditor.getControl().getModel().getWordAtPosition({ lineNumber, column }).word, 'Container');
+
+ await closePeek();
+ });
+
+ it(`from ${from} to an editor preview`, async function () {
+ const editor = await openEditor(serverUri);
+ // const { Cont|ainer } = require('inversify');
+ editor.getControl().setPosition({ lineNumber: 5, column: 13 });
+ // @ts-ignore
+ assert.equal(editor.getControl().getModel().getWordAtPosition(editor.getControl().getPosition()).word, 'Container');
+
+ await openPeek(editor);
+ await openReference();
+
+ const activeEditor = /** @type {MonacoEditor} */ (MonacoEditor.get(editorManager.activeEditor));
+ // @ts-ignore
+ assert.isTrue(editorManager.activeEditor.parent instanceof EditorPreviewWidget);
+ assert.equal(activeEditor.uri.toString(), inversifyUri.toString());
+ // export { |Container } from "./container/container";
+ // @ts-ignore
+ const { lineNumber, column } = activeEditor.getControl().getPosition();
+ assert.deepEqual({ lineNumber, column }, { lineNumber: 3, column: 10 });
+ // @ts-ignore
+ assert.equal(activeEditor.getControl().getModel().getWordAtPosition({ lineNumber, column }).word, 'Container');
+
+ await closePeek();
+ });
+ }
+ });
+
+ it('editor.action.triggerSuggest', async function () {
+ const editor = await openEditor(serverUri);
+ // const { [|Container] } = require('inversify');
+ editor.getControl().setPosition({ lineNumber: 5, column: 9 });
+ editor.getControl().setSelection({ startLineNumber: 5, startColumn: 9, endLineNumber: 5, endColumn: 18 });
+ // @ts-ignore
+ assert.equal(editor.getControl().getModel().getWordAtPosition(editor.getControl().getPosition()).word, 'Container');
+
+ assert.isTrue(contextKeyService.match('editorTextFocus'));
+ assert.isFalse(contextKeyService.match('suggestWidgetVisible'));
+
+ await commands.executeCommand('editor.action.triggerSuggest');
+ await waitForAnimation(() => contextKeyService.match('suggestWidgetVisible'));
+
+ assert.isTrue(contextKeyService.match('editorTextFocus'));
+ assert.isTrue(contextKeyService.match('suggestWidgetVisible'));
+
+ keybindings.dispatchKeyDown('Enter');
+ await waitForAnimation(() => !contextKeyService.match('suggestWidgetVisible'));
+
+ assert.isTrue(contextKeyService.match('editorTextFocus'));
+ assert.isFalse(contextKeyService.match('suggestWidgetVisible'));
+
+ const activeEditor = /** @type {MonacoEditor} */ (MonacoEditor.get(editorManager.activeEditor));
+ assert.equal(activeEditor.uri.toString(), serverUri.toString());
+ // const { Container| } = require('inversify');
+ // @ts-ignore
+ const { lineNumber, column } = activeEditor.getControl().getPosition();
+ assert.deepEqual({ lineNumber, column }, { lineNumber: 5, column: 18 });
+ // @ts-ignore
+ assert.equal(activeEditor.getControl().getModel().getWordAtPosition({ lineNumber, column }).word, 'Container');
+ });
+
+ it('editor.action.rename', async function () {
+ const editor = await openEditor(serverUri);
+ // const |container = new Container();
+ editor.getControl().setPosition({ lineNumber: 11, column: 7 });
+ // @ts-ignore
+ assert.equal(editor.getControl().getModel().getWordAtPosition(editor.getControl().getPosition()).word, 'container');
+
+ assert.isTrue(contextKeyService.match('editorTextFocus'));
+ assert.isFalse(contextKeyService.match('renameInputVisible'));
+
+ const renaming = commands.executeCommand('editor.action.rename');
+ await waitForAnimation(() => contextKeyService.match('renameInputVisible')
+ && document.activeElement instanceof HTMLInputElement
+ && document.activeElement.selectionEnd === 'container'.length);
+ assert.isFalse(contextKeyService.match('editorTextFocus'));
+ assert.isTrue(contextKeyService.match('renameInputVisible'));
+
+ const input = document.activeElement;
+ if (!(input instanceof HTMLInputElement)) {
+ assert.fail('expected focused input, but: ' + input);
+ return;
+ }
+
+ input.value = 'foo';
+ keybindings.dispatchKeyDown('Enter', input);
+
+ await renaming;
+ assert.isTrue(contextKeyService.match('editorTextFocus'));
+ assert.isFalse(contextKeyService.match('renameInputVisible'));
+
+ const activeEditor = /** @type {MonacoEditor} */ (MonacoEditor.get(editorManager.activeEditor));
+ assert.equal(activeEditor.uri.toString(), serverUri.toString());
+ // const |foo = new Container();
+ // @ts-ignore
+ const { lineNumber, column } = activeEditor.getControl().getPosition();
+ assert.deepEqual({ lineNumber, column }, { lineNumber: 11, column: 7 });
+ // @ts-ignore
+ assert.equal(activeEditor.getControl().getModel().getWordAtPosition({ lineNumber, column }).word, 'foo');
+ });
+
+ it('editor.action.triggerParameterHints', async function () {
+ const editor = await openEditor(serverUri);
+ // container.load(|backendApplicationModule);
+ editor.getControl().setPosition({ lineNumber: 12, column: 16 });
+ // @ts-ignore
+ assert.equal(editor.getControl().getModel().getWordAtPosition(editor.getControl().getPosition()).word, 'backendApplicationModule');
+
+ assert.isTrue(contextKeyService.match('editorTextFocus'));
+ assert.isFalse(contextKeyService.match('parameterHintsVisible'));
+
+ await commands.executeCommand('editor.action.triggerParameterHints');
+ await waitForAnimation(() => contextKeyService.match('parameterHintsVisible'));
+
+ assert.isTrue(contextKeyService.match('editorTextFocus'));
+ assert.isTrue(contextKeyService.match('parameterHintsVisible'));
+
+ keybindings.dispatchKeyDown('Escape');
+ await waitForAnimation(() => !contextKeyService.match('parameterHintsVisible'));
+
+ assert.isTrue(contextKeyService.match('editorTextFocus'));
+ assert.isFalse(contextKeyService.match('parameterHintsVisible'));
+ });
+
+ it('editor.action.showHover', async function () {
+ const editor = await openEditor(serverUri);
+ // container.load(|backendApplicationModule);
+ editor.getControl().setPosition({ lineNumber: 12, column: 16 });
+ // @ts-ignore
+ assert.equal(editor.getControl().getModel().getWordAtPosition(editor.getControl().getPosition()).word, 'backendApplicationModule');
+
+ const hover = editor.getControl()._contributions['editor.contrib.hover'];
+
+ assert.isTrue(contextKeyService.match('editorTextFocus'));
+ assert.isFalse(hover.contentWidget.isVisible);
+
+ await commands.executeCommand('editor.action.showHover');
+ await waitForAnimation(() => hover.contentWidget.isVisible);
+
+ assert.isTrue(contextKeyService.match('editorTextFocus'));
+ assert.isTrue(hover.contentWidget.isVisible);
+
+ assert.deepEqual(nodeAsString(hover.contentWidget._domNode), `
+DIV {
+ DIV {
+ DIV {
+ DIV {
+ DIV {
+ SPAN {
+ DIV {
+ SPAN {
+ "const"
+ }
+ SPAN {
+ " "
+ }
+ SPAN {
+ "backendApplicationModule"
+ }
+ SPAN {
+ ": "
+ }
+ SPAN {
+ "ContainerModule"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+`);
+
+ keybindings.dispatchKeyDown('Escape');
+ await waitForAnimation(() => !hover.contentWidget.isVisible);
+
+ assert.isTrue(contextKeyService.match('editorTextFocus'));
+ assert.isFalse(hover.contentWidget.isVisible);
+ });
+
+ it('highligh semantic (write) occurences', async function () {
+ const editor = await openEditor(serverUri);
+ // const |container = new Container();
+ const lineNumber = 11;
+ const column = 7;
+ const endColumn = column + 'container'.length;
+
+ const hasWriteDecoration = () => {
+ // @ts-ignore
+ for (const decoration of editor.getControl().getModel().getLineDecorations(lineNumber)) {
+ if (decoration.range.startColumn === column && decoration.range.endColumn === endColumn && decoration.options.className === 'wordHighlightStrong') {
+ return true;
+ }
+ }
+ return false;
+ };
+ assert.isFalse(hasWriteDecoration());
+
+ editor.getControl().setPosition({ lineNumber, column });
+ // @ts-ignore
+ assert.equal(editor.getControl().getModel().getWordAtPosition(editor.getControl().getPosition()).word, 'container');
+ // highlight occurences is not trigged on the explicit position change, so move a cursor as a user
+ keybindings.dispatchKeyDown('ArrowRight');
+ await waitForAnimation(() => hasWriteDecoration());
+
+ assert.isTrue(hasWriteDecoration());
+ });
+
+ it('editor.action.goToImplementation', async function () {
+ const editor = await openEditor(serverUri);
+ // con|tainer.load(backendApplicationModule);
+ editor.getControl().setPosition({ lineNumber: 12, column: 4 });
+ // @ts-ignore
+ assert.equal(editor.getControl().getModel().getWordAtPosition(editor.getControl().getPosition()).word, 'container');
+
+ await commands.executeCommand('editor.action.goToImplementation');
+
+ const activeEditor = /** @type {MonacoEditor} */ (MonacoEditor.get(editorManager.activeEditor));
+ assert.equal(activeEditor.uri.toString(), serverUri.toString());
+ // const |container = new Container();
+ // @ts-ignore
+ const { lineNumber, column } = activeEditor.getControl().getPosition();
+ assert.deepEqual({ lineNumber, column }, { lineNumber: 11, column: 7 });
+ // @ts-ignore
+ assert.equal(activeEditor.getControl().getModel().getWordAtPosition({ lineNumber, column }).word, 'container');
+ });
+
+ it('editor.action.goToTypeDefinition', async function () {
+ const editor = await openEditor(serverUri);
+ // con|tainer.load(backendApplicationModule);
+ editor.getControl().setPosition({ lineNumber: 12, column: 4 });
+ // @ts-ignore
+ assert.equal(editor.getControl().getModel().getWordAtPosition(editor.getControl().getPosition()).word, 'container');
+
+ await commands.executeCommand('editor.action.goToTypeDefinition');
+
+ const activeEditor = /** @type {MonacoEditor} */ (MonacoEditor.get(editorManager.activeEditor));
+ assert.equal(activeEditor.uri.toString(), containerUri.toString());
+ // declare class |Container implements interfaces.Container {
+ // @ts-ignore
+ const { lineNumber, column } = activeEditor.getControl().getPosition();
+ assert.deepEqual({ lineNumber, column }, { lineNumber: 2, column: 15 });
+ // @ts-ignore
+ assert.equal(activeEditor.getControl().getModel().getWordAtPosition({ lineNumber, column }).word, 'Container');
+ });
+
+ it('run reference code lens', async function () {
+ // @ts-ignore
+ const globalValue = preferences.inspect('javascript.referencesCodeLens.enabled').globalValue;
+ toTearDown.push({ dispose: () => preferences.set('javascript.referencesCodeLens.enabled', globalValue, PreferenceScope.User) });
+
+ const editor = await openEditor(serverUri);
+
+ const codeLens = editor.getControl()._contributions['css.editor.codeLens'];
+ const codeLensNode = () => codeLens._lenses[0] && codeLens._lenses[0]._contentWidget && codeLens._lenses[0]._contentWidget._domNode;
+ const codeLensNodeVisible = () => {
+ const n = codeLensNode();
+ return !!n && n.style.visibility !== 'hidden';
+ };
+
+ assert.isFalse(codeLensNodeVisible());
+
+ // [export ]function load(raw) {
+ const position = { lineNumber: 16, column: 1 };
+ // @ts-ignore
+ editor.getControl().getModel().applyEdits([{
+ range: monaco.Range.fromPositions(position, position),
+ forceMoveMarkers: false,
+ text: 'export '
+ }]);
+ editor.getControl().revealPosition(position);
+ await preferences.set('javascript.referencesCodeLens.enabled', true, PreferenceScope.User);
+ await waitForAnimation(() => codeLensNodeVisible());
+
+ assert.isTrue(codeLensNodeVisible());
+ const node = codeLensNode();
+ if (node) {
+ assert.equal(nodeAsString(node), `
+SPAN {
+ A {
+ "20 references"
+ }
+}
+`);
+ const link = node.getElementsByTagName('a').item(0);
+ if (link) {
+ link.dispatchEvent(new MouseEvent('mouseup', { bubbles: true }));
+ await assertPeekOpened(editor);
+ await closePeek();
+ } else {
+ assert.isDefined(link);
+ }
+ } else {
+ assert.isDefined(node);
+ }
+ });
+
+ it('editor.action.quickFix', async function () {
+ const column = 6;
+ const lineNumber = 19;
+ const editor = await openEditor(serverUri);
+ // @ts-ignore
+ const currentChar = () => editor.getControl().getModel().getLineContent(lineNumber).charAt(column - 1);
+
+ // missing semicolon at
+ // )|
+ editor.getControl().setPosition({ lineNumber, column });
+ editor.getControl().revealPosition({ lineNumber, column });
+ assert.equal(currentChar(), '');
+
+ const quickFixController = editor.getControl()._contributions['editor.contrib.quickFixController'];
+ const lightBulbNode = () => {
+ const ui = quickFixController._ui.rawValue;
+ const lightBulb = ui && ui._lightBulbWidget.rawValue;
+ return lightBulb && lightBulb._domNode;
+ };
+ const lightBulbVisible = () => {
+ const node = lightBulbNode();
+ return !!node && node.style.visibility !== 'hidden';
+ };
+
+ assert.isFalse(lightBulbVisible());
+ await waitForAnimation(() => lightBulbVisible());
+
+ await commands.executeCommand('editor.action.quickFix');
+ await waitForAnimation(() => !!document.querySelector('.p-Widget.p-Menu'));
+ await animationFrame();
+
+ keybindings.dispatchKeyDown('ArrowDown');
+ keybindings.dispatchKeyDown('Enter');
+
+ await waitForAnimation(() => currentChar() === ';');
+ assert.equal(currentChar(), ';');
+
+ await waitForAnimation(() => !lightBulbVisible());
+ assert.isFalse(lightBulbVisible());
+ });
+
+ it('editor.action.formatDocument', async function () {
+ const lineNumber = 5;
+ const editor = await openEditor(serverUri);
+ // @ts-ignore
+ const originalLenght = editor.getControl().getModel().getLineLength(lineNumber);
+
+ // const { Container[ ] } = require('inversify');
+ // @ts-ignore
+ editor.getControl().getModel().applyEdits([{
+ range: monaco.Range.fromPositions({ lineNumber, column: 18 }, { lineNumber, column: 18 }),
+ forceMoveMarkers: false,
+ text: ' '
+ }]);
+
+ // @ts-ignore
+ assert.equal(editor.getControl().getModel().getLineLength(lineNumber), originalLenght + 1);
+
+ await commands.executeCommand('editor.action.formatDocument');
+
+ // @ts-ignore
+ assert.equal(editor.getControl().getModel().getLineLength(lineNumber), originalLenght);
+ });
+
+ it('editor.action.formatSelection', async function () {
+ const lineNumber = 5;
+ const editor = await openEditor(serverUri);
+ // @ts-ignore
+ const originalLenght = editor.getControl().getModel().getLineLength(lineNumber);
+
+ // const { Container[ } ]= require('inversify');
+ // @ts-ignore
+ editor.getControl().getModel().applyEdits([{
+ range: monaco.Range.fromPositions({ lineNumber, column: 18 }, { lineNumber, column: 21 }),
+ forceMoveMarkers: false,
+ text: ' } '
+ }]);
+
+ // @ts-ignore
+ assert.equal(editor.getControl().getModel().getLineLength(lineNumber), originalLenght + 2);
+
+ // [const { Container }] = require('inversify');
+ editor.getControl().setSelection({ startLineNumber: lineNumber, startColumn: 1, endLineNumber: lineNumber, endColumn: 21 });
+
+ await commands.executeCommand('editor.action.formatSelection');
+
+ // [const { Container }] = require('inversify');
+ // @ts-ignore
+ assert.equal(editor.getControl().getModel().getLineLength(lineNumber), originalLenght + 1);
+ });
+
+});
diff --git a/examples/api-tests/src/views.spec.js b/examples/api-tests/src/views.spec.js
index dc982997e4985..d3059ad2435ce 100644
--- a/examples/api-tests/src/views.spec.js
+++ b/examples/api-tests/src/views.spec.js
@@ -50,6 +50,7 @@ describe('Views', function () {
assert.notEqual(view, undefined);
assert.equal(shell.getAreaFor(view), contribution.defaultViewOptions.area);
assert.isDefined(shell.getTabBarFor(view));
+ // @ts-ignore
assert.equal(shell.getAreaFor(shell.getTabBarFor(view)), contribution.defaultViewOptions.area);
assert.isTrue(view.isVisible);
assert.equal(view, shell.activeWidget);
diff --git a/examples/browser/.eslintrc.js b/examples/browser/.eslintrc.js
deleted file mode 100644
index be9cf1a1b3dff..0000000000000
--- a/examples/browser/.eslintrc.js
+++ /dev/null
@@ -1,10 +0,0 @@
-/** @type {import('eslint').Linter.Config} */
-module.exports = {
- extends: [
- '../../configs/build.eslintrc.json'
- ],
- parserOptions: {
- tsconfigRootDir: __dirname,
- project: 'compile.tsconfig.json'
- }
-};
diff --git a/examples/browser/package.json b/examples/browser/package.json
index e97b0e99e3a88..b583fa6a2629c 100644
--- a/examples/browser/package.json
+++ b/examples/browser/package.json
@@ -56,13 +56,12 @@
},
"scripts": {
"prepare": "yarn run clean && yarn build",
- "lint": "theiaext lint",
"clean": "theia clean",
"build": "theia build --mode development",
"watch": "yarn build --watch",
"start": "theia start --plugins=local-dir:../../plugins",
"start:debug": "yarn start --log-level=debug",
- "test": "theia test . --test-spec=../api-tests/**/*.spec.js",
+ "test": "theia test . --plugins=local-dir:../../plugins --test-spec=../api-tests/**/*.spec.js",
"test:debug": "yarn test --test-inspect",
"coverage": "yarn test --test-coverage && yarn coverage:report",
"coverage:report": "nyc report --reporter=html",
@@ -71,4 +70,4 @@
"devDependencies": {
"@theia/cli": "^1.0.0"
}
-}
+}
\ No newline at end of file
diff --git a/packages/core/src/browser/keybinding.ts b/packages/core/src/browser/keybinding.ts
index 3b2d6a5f69136..429a1f50ac34c 100644
--- a/packages/core/src/browser/keybinding.ts
+++ b/packages/core/src/browser/keybinding.ts
@@ -571,6 +571,29 @@ export class KeybindingRegistry {
return true;
}
+ dispatchKeyDown(input: KeyboardEventInit | KeyCode | string, target: EventTarget = document.activeElement || window): void {
+ const eventInit = this.asKeyboardEventInit(input);
+ const emulatedKeyboardEvent = new KeyboardEvent('keydown', eventInit);
+ target.dispatchEvent(emulatedKeyboardEvent);
+ }
+ protected asKeyboardEventInit(input: KeyboardEventInit | KeyCode | string): KeyboardEventInit & Partial<{ keyCode: number }> {
+ if (typeof input === 'string') {
+ return this.asKeyboardEventInit(KeyCode.createKeyCode(input));
+ }
+ if (input instanceof KeyCode) {
+ return {
+ metaKey: input.meta,
+ shiftKey: input.shift,
+ altKey: input.alt,
+ ctrlKey: input.ctrl,
+ code: input.key && input.key.code,
+ key: (input && input.character) || (input.key && input.key.code),
+ keyCode: input.key && input.key.keyCode
+ };
+ }
+ return input;
+ }
+
/**
* Run the command matching to the given keyboard event.
*/
diff --git a/packages/core/src/browser/progress-status-bar-item.ts b/packages/core/src/browser/progress-status-bar-item.ts
index e1d18461a07c5..81970c6a77cd1 100644
--- a/packages/core/src/browser/progress-status-bar-item.ts
+++ b/packages/core/src/browser/progress-status-bar-item.ts
@@ -30,6 +30,14 @@ export class ProgressStatusBarItem implements ProgressClient {
@inject(StatusBar)
protected readonly statusBar: StatusBar;
+ protected messagesByProgress = new Map();
+
+ protected incomingQueue = new Array();
+
+ get currentProgress(): string | undefined {
+ return this.incomingQueue.slice(-1)[0];
+ }
+
showProgress(progressId: string, message: ProgressMessage, cancellationToken: CancellationToken): Promise {
const result = new Deferred();
cancellationToken.onCancellationRequested(() => {
@@ -39,21 +47,19 @@ export class ProgressStatusBarItem implements ProgressClient {
this.processEvent(progressId, 'start', message.text);
return result.promise;
}
- protected messagesByProgress = new Map();
- protected incomingQueue = new Array();
+
protected processEvent(progressId: string, event: 'start' | 'done', message?: string): void {
if (event === 'start') {
this.incomingQueue.push(progressId);
this.messagesByProgress.set(progressId, message);
} else {
this.incomingQueue = this.incomingQueue.filter(id => id !== progressId);
+ this.messagesByProgress.delete(progressId);
}
this.triggerUpdate();
}
- protected readonly triggerUpdate = throttle(() => {
- const pick: string | undefined = this.incomingQueue.slice(-1)[0];
- this.update(pick);
- }, 250, { leading: true, trailing: true });
+
+ protected readonly triggerUpdate = throttle(() => this.update(this.currentProgress), 250, { leading: true, trailing: true });
async reportProgress(progressId: string, update: ProgressUpdate, originalMessage: ProgressMessage, _cancellationToken: CancellationToken): Promise {
const newMessage = update.message ? `${originalMessage.text}: ${update.message}` : originalMessage.text;
diff --git a/packages/core/src/browser/saveable.ts b/packages/core/src/browser/saveable.ts
index e97783ef6733a..e6e09221693b6 100644
--- a/packages/core/src/browser/saveable.ts
+++ b/packages/core/src/browser/saveable.ts
@@ -105,6 +105,9 @@ export namespace Saveable {
saveable.onDirtyChanged(() => setDirty(widget, saveable.dirty));
const closeWidget = widget.close.bind(widget);
const closeWithoutSaving: SaveableWidget['closeWithoutSaving'] = async () => {
+ if (saveable.dirty && saveable.revert) {
+ await saveable.revert();
+ }
closeWidget();
return waitForClosed(widget);
};
diff --git a/packages/core/src/browser/shell/application-shell.ts b/packages/core/src/browser/shell/application-shell.ts
index 07bd73a2c6af6..83339b3ee8bc8 100644
--- a/packages/core/src/browser/shell/application-shell.ts
+++ b/packages/core/src/browser/shell/application-shell.ts
@@ -93,6 +93,7 @@ export class DockPanelRenderer implements DockLayout.IRenderer {
});
this.tabBarClasses.forEach(c => tabBar.addClass(c));
renderer.tabBar = tabBar;
+ tabBar.disposed.connect(() => renderer.dispose());
renderer.contextMenuPath = SHELL_TABBAR_CONTEXT_MENU;
tabBar.currentChanged.connect(this.onCurrentTabChanged, this);
return tabBar;
@@ -1325,27 +1326,28 @@ export class ApplicationShell extends Widget {
}
async closeWidget(id: string, options?: ApplicationShell.CloseOptions): Promise {
+ // TODO handle save for composite widgets, i.e. the preference widget has 2 editors
const stack = this.toTrackedStack(id);
- const widget = this.toTrackedStack(id).pop();
- if (!widget) {
+ const current = stack.pop();
+ if (!current) {
return undefined;
}
let pendingClose;
- if (SaveableWidget.is(widget)) {
+ if (SaveableWidget.is(current)) {
let shouldSave;
if (options && 'save' in options) {
shouldSave = () => options.save;
}
- pendingClose = widget.closeWithSaving({ shouldSave });
+ pendingClose = current.closeWithSaving({ shouldSave });
} else {
- widget.close();
- pendingClose = waitForClosed(widget);
+ current.close();
+ pendingClose = waitForClosed(current);
};
await Promise.all([
pendingClose,
this.pendingUpdates
]);
- return stack[0] || widget;
+ return stack[0] || current;
}
/**
diff --git a/packages/core/src/browser/shell/side-panel-handler.ts b/packages/core/src/browser/shell/side-panel-handler.ts
index ff72e3c1f24a6..6667c1516d00e 100644
--- a/packages/core/src/browser/shell/side-panel-handler.ts
+++ b/packages/core/src/browser/shell/side-panel-handler.ts
@@ -138,6 +138,7 @@ export class SidePanelHandler {
suppressScrollX: true
});
tabBarRenderer.tabBar = sideBar;
+ sideBar.disposed.connect(() => tabBarRenderer.dispose());
tabBarRenderer.contextMenuPath = SHELL_TABBAR_CONTEXT_MENU;
sideBar.addClass('theia-app-' + side);
sideBar.addClass(LEFT_RIGHT_AREA_CLASS);
diff --git a/packages/core/src/browser/shell/tab-bars.ts b/packages/core/src/browser/shell/tab-bars.ts
index 3189561a3111d..58404dd03308c 100644
--- a/packages/core/src/browser/shell/tab-bars.ts
+++ b/packages/core/src/browser/shell/tab-bars.ts
@@ -94,6 +94,10 @@ export class TabBarRenderer extends TabBar.Renderer {
}
}
+ dispose(): void {
+ this.toDispose.dispose();
+ }
+
protected _tabBar?: TabBar;
protected readonly toDisposeOnTabBar = new DisposableCollection();
/**
@@ -101,6 +105,9 @@ export class TabBarRenderer extends TabBar.Renderer {
* is requested.
*/
set tabBar(tabBar: TabBar | undefined) {
+ if (this.toDispose.disposed) {
+ throw new Error('disposed');
+ }
if (this._tabBar === tabBar) {
return;
}
@@ -453,11 +460,21 @@ export class ScrollableTabBar extends TabBar {
private scrollBarFactory: () => PerfectScrollbar;
private pendingReveal?: Promise;
+ protected readonly toDispose = new DisposableCollection();
+
constructor(options?: TabBar.IOptions & PerfectScrollbar.Options) {
super(options);
this.scrollBarFactory = () => new PerfectScrollbar(this.scrollbarHost, options);
}
+ dispose(): void {
+ if (this.isDisposed) {
+ return;
+ }
+ super.dispose();
+ this.toDispose.dispose();
+ }
+
protected onAfterAttach(msg: Message): void {
if (!this.scrollBar) {
this.scrollBar = this.scrollBarFactory();
@@ -571,7 +588,7 @@ export class ToolbarAwareTabBar extends ScrollableTabBar {
super(options);
this.rewireDOM();
- this.tabBarToolbarRegistry.onDidChange(() => this.update());
+ this.toDispose.push(this.tabBarToolbarRegistry.onDidChange(() => this.update()));
}
/**
diff --git a/packages/core/src/browser/widget-open-handler.ts b/packages/core/src/browser/widget-open-handler.ts
index 39c3015798a2c..1c8ddfbb4d731 100644
--- a/packages/core/src/browser/widget-open-handler.ts
+++ b/packages/core/src/browser/widget-open-handler.ts
@@ -132,16 +132,8 @@ export abstract class WidgetOpenHandler implements OpenHan
protected abstract createWidgetOptions(uri: URI, options?: WidgetOpenerOptions): Object;
async closeAll(options?: ApplicationShell.CloseOptions): Promise {
- const allClosed: W[] = [];
- await Promise.all(
- [this.all.map(async widget => {
- const closed = await this.shell.closeWidget(widget.id, options);
- if (closed) {
- allClosed.push(closed as W);
- }
- })]
- );
- return allClosed;
+ const closed = await Promise.all(this.all.map(widget => this.shell.closeWidget(widget.id, options)));
+ return closed.filter(widget => !!widget) as W[];
}
}
diff --git a/packages/core/src/common/reference.spec.ts b/packages/core/src/common/reference.spec.ts
index 4ea946508c75f..b80525344f68f 100644
--- a/packages/core/src/common/reference.spec.ts
+++ b/packages/core/src/common/reference.spec.ts
@@ -121,4 +121,25 @@ describe('reference', () => {
result.forEach(v => assert.ok(result[0].object === v.object));
});
+ it('should not dispose an object if a reference is pending', async () => {
+ let disposed = false;
+ const references = new ReferenceCollection(async key => ({
+ key, dispose: () => {
+ disposed = true;
+ }
+ }));
+ assert.ok(!disposed);
+
+ let reference = await references.acquire('a');
+
+ const pendingReference = references.acquire('a');
+ reference.dispose();
+
+ assert.ok(!disposed);
+
+ reference = await pendingReference;
+ reference.dispose();
+ assert.ok(disposed);
+ });
+
});
diff --git a/packages/core/src/common/reference.ts b/packages/core/src/common/reference.ts
index be03890d7e486..0386d73971f19 100644
--- a/packages/core/src/common/reference.ts
+++ b/packages/core/src/common/reference.ts
@@ -116,13 +116,17 @@ export class ReferenceCollection extends AbstractRefere
async acquire(args: K): Promise> {
const key = this.toKey(args);
+ const existing = this._values.get(key);
+ if (existing) {
+ return this.doAcquire(key, existing);
+ }
const object = await this.getOrCreateValue(key, args);
return this.doAcquire(key, object);
}
protected readonly pendingValues = new Map>();
protected async getOrCreateValue(key: string, args: K): Promise {
- const existing = this._values.get(key) || this.pendingValues.get(key);
+ const existing = this.pendingValues.get(key);
if (existing) {
return existing;
}
diff --git a/packages/languages/src/browser/language-client-services.ts b/packages/languages/src/browser/language-client-services.ts
index 6205afd9ad9c9..88160abe49034 100644
--- a/packages/languages/src/browser/language-client-services.ts
+++ b/packages/languages/src/browser/language-client-services.ts
@@ -28,7 +28,7 @@ export interface Language {
}
export interface WorkspaceSymbolProvider extends services.WorkspaceSymbolProvider {
- resolveWorkspaceSymbol?(symbol: services.SymbolInformation, token: services.CancellationToken): Thenable
+ resolveWorkspaceSymbol?(symbol: services.SymbolInformation, token: services.CancellationToken): Thenable
}
export const Languages = Symbol('Languages');
diff --git a/packages/monaco/src/typings/monaco/index.d.ts b/packages/monaco/src/typings/monaco/index.d.ts
index 1a33f412b66be..420106caf6e49 100644
--- a/packages/monaco/src/typings/monaco/index.d.ts
+++ b/packages/monaco/src/typings/monaco/index.d.ts
@@ -68,12 +68,50 @@ declare module monaco.editor {
readonly _contributions: {
'editor.controller.quickOpenController': monaco.quickOpen.QuickOpenController
'editor.contrib.referencesController': monaco.referenceSearch.ReferencesController
+ 'editor.contrib.hover': ModesHoverController
+ 'css.editor.codeLens': CodeLensContribution
+ 'editor.contrib.quickFixController': QuickFixController
}
readonly _modelData: {
cursor: ICursor
} | null;
}
+ // https://github.com/theia-ide/vscode/blob/d24b5f70c69b3e75cd10c6b5247a071265ccdd38/src/vs/editor/contrib/codeAction/codeActionCommands.ts#L69
+ export interface QuickFixController {
+ readonly _ui: {
+ rawValue?: CodeActionUi
+ }
+ }
+ export interface CodeActionUi {
+ readonly _lightBulbWidget: {
+ rawValue?: LightBulbWidget
+ }
+ }
+ export interface LightBulbWidget {
+ readonly _domNode: HTMLDivElement;
+ }
+
+ // https://github.com/theia-ide/vscode/blob/d24b5f70c69b3e75cd10c6b5247a071265ccdd38/src/vs/editor/contrib/codelens/codelensController.ts#L24
+ export interface CodeLensContribution {
+ readonly _lenses: CodeLensWidget[];
+ }
+ export interface CodeLensWidget {
+ readonly _contentWidget?: CodeLensContentWidget;
+ }
+ export interface CodeLensContentWidget {
+ readonly _domNode: HTMLElement;
+ }
+
+ // https://github.com/theia-ide/vscode/blob/standalone/0.19.x/src/vs/editor/contrib/hover/hover.ts#L31
+ export interface ModesHoverController {
+ readonly contentWidget: ModesContentHoverWidget
+ }
+ export interface ModesContentHoverWidget {
+ readonly isVisible: boolean;
+ readonly _domNode: HTMLElement;
+ }
+
// https://github.com/theia-ide/vscode/blob/standalone/0.19.x/src/vs/editor/common/controller/cursor.ts#L169
export interface ICursor {
trigger(source: string, handlerId: string, payload: any): void;
@@ -733,7 +771,12 @@ declare module monaco.referenceSearch {
show(range: IRange): void;
hide(): void;
focus(): void;
+ _tree: ReferenceTree
+ }
+ export interface ReferenceTree {
+ getFocus(): ReferenceTreeElement[]
}
+ export interface ReferenceTreeElement { }
// https://github.com/theia-ide/vscode/blob/standalone/0.19.x/src/vs/editor/contrib/gotoSymbol/peek/referencesController.ts#L30
export interface ReferencesController extends IDisposable {
diff --git a/packages/plugin-ext/src/common/plugin-api-rpc-model.ts b/packages/plugin-ext/src/common/plugin-api-rpc-model.ts
index 9e359bb8e21ef..1a2772fbb9fe7 100644
--- a/packages/plugin-ext/src/common/plugin-api-rpc-model.ts
+++ b/packages/plugin-ext/src/common/plugin-api-rpc-model.ts
@@ -17,7 +17,6 @@
import * as theia from '@theia/plugin';
import { UriComponents } from './uri-components';
import { FileStat } from '@theia/filesystem/lib/common';
-import { SymbolInformation } from 'vscode-languageserver-types';
// Should contains internal Plugin API types
@@ -452,11 +451,6 @@ export interface Breakpoint {
readonly functionName?: string;
}
-export interface WorkspaceSymbolProvider {
- provideWorkspaceSymbols(params: WorkspaceSymbolParams, token: monaco.CancellationToken): Thenable;
- resolveWorkspaceSymbol(symbol: SymbolInformation, token: monaco.CancellationToken): Thenable
-}
-
export interface WorkspaceSymbolParams {
query: string
}
diff --git a/packages/plugin-ext/src/common/plugin-api-rpc.ts b/packages/plugin-ext/src/common/plugin-api-rpc.ts
index 4883881957355..c090dcf57dfab 100644
--- a/packages/plugin-ext/src/common/plugin-api-rpc.ts
+++ b/packages/plugin-ext/src/common/plugin-api-rpc.ts
@@ -190,6 +190,8 @@ export interface PluginManagerExt {
$updateStoragePath(path: string | undefined): Promise;
$activateByEvent(event: string): Promise;
+
+ $activatePlugin(id: string): Promise;
}
export interface CommandRegistryMain {
@@ -1147,7 +1149,7 @@ export interface PluginInfo {
export interface LanguagesExt {
$provideCompletionItems(handle: number, resource: UriComponents, position: Position,
context: CompletionContext, token: CancellationToken): Promise;
- $resolveCompletionItem(handle: number, resource: UriComponents, position: Position, completion: Completion, token: CancellationToken): Promise;
+ $resolveCompletionItem(handle: number, resource: UriComponents, position: Position, completion: Completion, token: CancellationToken): Promise;
$releaseCompletionItems(handle: number, id: number): void;
$provideImplementation(handle: number, resource: UriComponents, position: Position, token: CancellationToken): Promise;
$provideTypeDefinition(handle: number, resource: UriComponents, position: Position, token: CancellationToken): Promise;
@@ -1187,7 +1189,7 @@ export interface LanguagesExt {
): Promise;
$provideDocumentSymbols(handle: number, resource: UriComponents, token: CancellationToken): Promise;
$provideWorkspaceSymbols(handle: number, query: string, token: CancellationToken): PromiseLike;
- $resolveWorkspaceSymbol(handle: number, symbol: SymbolInformation, token: CancellationToken): PromiseLike;
+ $resolveWorkspaceSymbol(handle: number, symbol: SymbolInformation, token: CancellationToken): PromiseLike;
$provideFoldingRange(
handle: number,
resource: UriComponents,
diff --git a/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts b/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts
index 94eefa791ac02..a3bac3f18dfc8 100644
--- a/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts
+++ b/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts
@@ -631,6 +631,14 @@ export class HostedPluginSupport {
}
}
+ async activatePlugin(id: string): Promise {
+ const activation = [];
+ for (const manager of this.managers.values()) {
+ activation.push(manager.$activatePlugin(id));
+ }
+ await Promise.all(activation);
+ }
+
protected createMeasurement(name: string): () => number {
const startMarker = `${name}-start`;
const endMarker = `${name}-end`;
diff --git a/packages/plugin-ext/src/main/browser/debug/debug-main.ts b/packages/plugin-ext/src/main/browser/debug/debug-main.ts
index cab3e34ad054b..a3183978fcadd 100644
--- a/packages/plugin-ext/src/main/browser/debug/debug-main.ts
+++ b/packages/plugin-ext/src/main/browser/debug/debug-main.ts
@@ -97,11 +97,11 @@ export class DebugMainImpl implements DebugMain, Disposable {
);
};
this.debugExt.$breakpointsDidChange(this.toTheiaPluginApiBreakpoints(this.breakpointsManager.getBreakpoints()), [], []);
- this.breakpointsManager.onDidChangeBreakpoints(fireDidChangeBreakpoints);
this.debugExt.$breakpointsDidChange(this.toTheiaPluginApiBreakpoints(this.breakpointsManager.getFunctionBreakpoints()), [], []);
- this.breakpointsManager.onDidChangeFunctionBreakpoints(fireDidChangeBreakpoints);
this.toDispose.pushAll([
+ this.breakpointsManager.onDidChangeBreakpoints(fireDidChangeBreakpoints),
+ this.breakpointsManager.onDidChangeFunctionBreakpoints(fireDidChangeBreakpoints),
this.sessionManager.onDidCreateDebugSession(debugSession => this.debugExt.$sessionDidCreate(debugSession.id)),
this.sessionManager.onDidDestroyDebugSession(debugSession => this.debugExt.$sessionDidDestroy(debugSession.id)),
this.sessionManager.onDidChangeActiveDebugSession(event => this.debugExt.$sessionDidChange(event.current && event.current.id)),
diff --git a/packages/plugin-ext/src/main/browser/languages-main.ts b/packages/plugin-ext/src/main/browser/languages-main.ts
index 8eec2f4d6e267..697abb88a3152 100644
--- a/packages/plugin-ext/src/main/browser/languages-main.ts
+++ b/packages/plugin-ext/src/main/browser/languages-main.ts
@@ -36,7 +36,7 @@ import {
} from '../../common/plugin-api-rpc';
import { injectable, inject } from 'inversify';
import {
- SerializedDocumentFilter, MarkerData, Range, WorkspaceSymbolProvider, RelatedInformation,
+ SerializedDocumentFilter, MarkerData, Range, RelatedInformation,
MarkerSeverity, DocumentLink, WorkspaceSymbolParams, CodeAction
} from '../../common/plugin-api-rpc-model';
import { RPCProtocol } from '../../common/rpc-protocol';
@@ -56,6 +56,7 @@ import { CallHierarchyService, CallHierarchyServiceProvider, Caller, Definition
import { toDefinition, toUriComponents, fromDefinition, fromPosition, toCaller } from './callhierarchy/callhierarchy-type-converters';
import { Position, DocumentUri } from 'vscode-languageserver-types';
import { ObjectIdentifier } from '../../common/object-identifier';
+import { WorkspaceSymbolProvider } from '@theia/languages/lib/browser/language-client-services';
@injectable()
export class LanguagesMainImpl implements LanguagesMain, Disposable {
@@ -355,7 +356,7 @@ export class LanguagesMainImpl implements LanguagesMain, Disposable {
return this.proxy.$provideWorkspaceSymbols(handle, params.query, token);
}
- protected resolveWorkspaceSymbol(handle: number, symbol: vst.SymbolInformation, token: monaco.CancellationToken): Thenable {
+ protected resolveWorkspaceSymbol(handle: number, symbol: vst.SymbolInformation, token: monaco.CancellationToken): Thenable {
return this.proxy.$resolveWorkspaceSymbol(handle, symbol, token);
}
diff --git a/packages/plugin-ext/src/main/browser/webview/webview.ts b/packages/plugin-ext/src/main/browser/webview/webview.ts
index bc2f348a8fafd..96254daaf5611 100644
--- a/packages/plugin-ext/src/main/browser/webview/webview.ts
+++ b/packages/plugin-ext/src/main/browser/webview/webview.ts
@@ -280,7 +280,7 @@ export class WebviewWidget extends BaseWidget implements StatefulWidget {
// Electron: workaround for https://github.com/electron/electron/issues/14258
// We have to detect keyboard events in the and dispatch them to our
// keybinding service because these events do not bubble to the parent window anymore.
- this.dispatchKeyDown(data);
+ this.keybindings.dispatchKeyDown(data, this.element);
}));
this.style();
@@ -395,17 +395,6 @@ export class WebviewWidget extends BaseWidget implements StatefulWidget {
this.doSend('styles', { styles, activeTheme });
}
- protected dispatchKeyDown(event: KeyboardEventInit): void {
- // Create a fake KeyboardEvent from the data provided
- const emulatedKeyboardEvent = new KeyboardEvent('keydown', event);
- // Force override the target
- Object.defineProperty(emulatedKeyboardEvent, 'target', {
- get: () => this.element,
- });
- // And re-dispatch
- this.keybindings.run(emulatedKeyboardEvent);
- }
-
protected openLink(link: URI): void {
const supported = this.toSupportedLink(link);
if (supported) {
diff --git a/packages/plugin-ext/src/plugin/languages.ts b/packages/plugin-ext/src/plugin/languages.ts
index 494fcfc1e6e2d..5fb27fbab1382 100644
--- a/packages/plugin-ext/src/plugin/languages.ts
+++ b/packages/plugin-ext/src/plugin/languages.ts
@@ -187,12 +187,15 @@ export class LanguagesExtImpl implements LanguagesExt {
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
- private withAdapter(handle: number, ctor: { new(...args: any[]): A }, callback: (adapter: A) => Promise): Promise {
+ private async withAdapter(handle: number, ctor: { new(...args: any[]): A }, callback: (adapter: A) => Promise, fallbackValue: R): Promise {
const adapter = this.adaptersMap.get(handle);
- if (!(adapter instanceof ctor)) {
- return Promise.reject(new Error('no adapter found'));
+ if (!adapter) {
+ return fallbackValue;
}
- return callback(adapter);
+ if (adapter instanceof ctor) {
+ return callback(adapter);
+ }
+ throw new Error('no adapter found');
}
private transformDocumentSelector(selector: theia.DocumentSelector): SerializedDocumentFilter[] {
@@ -226,15 +229,15 @@ export class LanguagesExtImpl implements LanguagesExt {
// ### Completion begin
$provideCompletionItems(handle: number, resource: UriComponents, position: Position,
context: CompletionContext, token: theia.CancellationToken): Promise {
- return this.withAdapter(handle, CompletionAdapter, adapter => adapter.provideCompletionItems(URI.revive(resource), position, context, token));
+ return this.withAdapter(handle, CompletionAdapter, adapter => adapter.provideCompletionItems(URI.revive(resource), position, context, token), undefined);
}
- $resolveCompletionItem(handle: number, resource: UriComponents, position: Position, completion: Completion, token: theia.CancellationToken): Promise {
- return this.withAdapter(handle, CompletionAdapter, adapter => adapter.resolveCompletionItem(URI.revive(resource), position, completion, token));
+ $resolveCompletionItem(handle: number, resource: UriComponents, position: Position, completion: Completion, token: theia.CancellationToken): Promise {
+ return this.withAdapter(handle, CompletionAdapter, adapter => adapter.resolveCompletionItem(URI.revive(resource), position, completion, token), undefined);
}
$releaseCompletionItems(handle: number, id: number): void {
- this.withAdapter(handle, CompletionAdapter, async adapter => adapter.releaseCompletionItems(id));
+ this.withAdapter(handle, CompletionAdapter, async adapter => adapter.releaseCompletionItems(id), undefined);
}
registerCompletionItemProvider(selector: theia.DocumentSelector, provider: theia.CompletionItemProvider, triggerCharacters: string[],
@@ -247,7 +250,7 @@ export class LanguagesExtImpl implements LanguagesExt {
// ### Definition provider begin
$provideDefinition(handle: number, resource: UriComponents, position: Position, token: theia.CancellationToken): Promise {
- return this.withAdapter(handle, DefinitionAdapter, adapter => adapter.provideDefinition(URI.revive(resource), position, token));
+ return this.withAdapter(handle, DefinitionAdapter, adapter => adapter.provideDefinition(URI.revive(resource), position, token), undefined);
}
registerDefinitionProvider(selector: theia.DocumentSelector, provider: theia.DefinitionProvider, pluginInfo: PluginInfo): theia.Disposable {
@@ -259,7 +262,7 @@ export class LanguagesExtImpl implements LanguagesExt {
// ### Declaration provider begin
$provideDeclaration(handle: number, resource: UriComponents, position: Position, token: theia.CancellationToken): Promise {
- return this.withAdapter(handle, DeclarationAdapter, adapter => adapter.provideDeclaration(URI.revive(resource), position, token));
+ return this.withAdapter(handle, DeclarationAdapter, adapter => adapter.provideDeclaration(URI.revive(resource), position, token), undefined);
}
registerDeclarationProvider(selector: theia.DocumentSelector, provider: theia.DeclarationProvider, pluginInfo: PluginInfo): theia.Disposable {
@@ -273,11 +276,11 @@ export class LanguagesExtImpl implements LanguagesExt {
$provideSignatureHelp(
handle: number, resource: UriComponents, position: Position, context: SignatureHelpContext, token: theia.CancellationToken
): Promise {
- return this.withAdapter(handle, SignatureHelpAdapter, adapter => adapter.provideSignatureHelp(URI.revive(resource), position, token, context));
+ return this.withAdapter(handle, SignatureHelpAdapter, adapter => adapter.provideSignatureHelp(URI.revive(resource), position, token, context), undefined);
}
$releaseSignatureHelp(handle: number, id: number): void {
- this.withAdapter(handle, SignatureHelpAdapter, async adapter => adapter.releaseSignatureHelp(id));
+ this.withAdapter(handle, SignatureHelpAdapter, async adapter => adapter.releaseSignatureHelp(id), undefined);
}
registerSignatureHelpProvider(selector: theia.DocumentSelector, provider: theia.SignatureHelpProvider, metadata: theia.SignatureHelpProviderMetadata,
@@ -300,7 +303,7 @@ export class LanguagesExtImpl implements LanguagesExt {
// ### Implementation provider begin
$provideImplementation(handle: number, resource: UriComponents, position: Position, token: theia.CancellationToken): Promise {
- return this.withAdapter(handle, ImplementationAdapter, adapter => adapter.provideImplementation(URI.revive(resource), position, token));
+ return this.withAdapter(handle, ImplementationAdapter, adapter => adapter.provideImplementation(URI.revive(resource), position, token), undefined);
}
registerImplementationProvider(selector: theia.DocumentSelector, provider: theia.ImplementationProvider, pluginInfo: PluginInfo): theia.Disposable {
@@ -312,7 +315,7 @@ export class LanguagesExtImpl implements LanguagesExt {
// ### Type Definition provider begin
$provideTypeDefinition(handle: number, resource: UriComponents, position: Position, token: theia.CancellationToken): Promise {
- return this.withAdapter(handle, TypeDefinitionAdapter, adapter => adapter.provideTypeDefinition(URI.revive(resource), position, token));
+ return this.withAdapter(handle, TypeDefinitionAdapter, adapter => adapter.provideTypeDefinition(URI.revive(resource), position, token), undefined);
}
registerTypeDefinitionProvider(selector: theia.DocumentSelector, provider: theia.TypeDefinitionProvider, pluginInfo: PluginInfo): theia.Disposable {
@@ -330,7 +333,7 @@ export class LanguagesExtImpl implements LanguagesExt {
}
$provideHover(handle: number, resource: UriComponents, position: Position, token: theia.CancellationToken): Promise {
- return this.withAdapter(handle, HoverAdapter, adapter => adapter.provideHover(URI.revive(resource), position, token));
+ return this.withAdapter(handle, HoverAdapter, adapter => adapter.provideHover(URI.revive(resource), position, token), undefined);
}
// ### Hover Provider end
@@ -342,7 +345,7 @@ export class LanguagesExtImpl implements LanguagesExt {
}
$provideDocumentHighlights(handle: number, resource: UriComponents, position: Position, token: theia.CancellationToken): Promise {
- return this.withAdapter(handle, DocumentHighlightAdapter, adapter => adapter.provideDocumentHighlights(URI.revive(resource), position, token));
+ return this.withAdapter(handle, DocumentHighlightAdapter, adapter => adapter.provideDocumentHighlights(URI.revive(resource), position, token), undefined);
}
// ### Document Highlight Provider end
@@ -354,11 +357,11 @@ export class LanguagesExtImpl implements LanguagesExt {
}
$provideWorkspaceSymbols(handle: number, query: string, token: theia.CancellationToken): PromiseLike {
- return this.withAdapter(handle, WorkspaceSymbolAdapter, adapter => adapter.provideWorkspaceSymbols(query, token));
+ return this.withAdapter(handle, WorkspaceSymbolAdapter, adapter => adapter.provideWorkspaceSymbols(query, token), []);
}
- $resolveWorkspaceSymbol(handle: number, symbol: SymbolInformation, token: theia.CancellationToken): PromiseLike {
- return this.withAdapter(handle, WorkspaceSymbolAdapter, adapter => adapter.resolveWorkspaceSymbol(symbol, token));
+ $resolveWorkspaceSymbol(handle: number, symbol: SymbolInformation, token: theia.CancellationToken): PromiseLike {
+ return this.withAdapter(handle, WorkspaceSymbolAdapter, adapter => adapter.resolveWorkspaceSymbol(symbol, token), undefined);
}
// ### WorkspaceSymbol Provider end
@@ -371,7 +374,7 @@ export class LanguagesExtImpl implements LanguagesExt {
$provideDocumentFormattingEdits(handle: number, resource: UriComponents,
options: FormattingOptions, token: theia.CancellationToken): Promise {
- return this.withAdapter(handle, DocumentFormattingAdapter, adapter => adapter.provideDocumentFormattingEdits(URI.revive(resource), options, token));
+ return this.withAdapter(handle, DocumentFormattingAdapter, adapter => adapter.provideDocumentFormattingEdits(URI.revive(resource), options, token), undefined);
}
// ### Document Formatting Edit end
@@ -385,7 +388,7 @@ export class LanguagesExtImpl implements LanguagesExt {
$provideDocumentRangeFormattingEdits(handle: number, resource: UriComponents, range: Range,
options: FormattingOptions, token: theia.CancellationToken): Promise {
- return this.withAdapter(handle, RangeFormattingAdapter, adapter => adapter.provideDocumentRangeFormattingEdits(URI.revive(resource), range, options, token));
+ return this.withAdapter(handle, RangeFormattingAdapter, adapter => adapter.provideDocumentRangeFormattingEdits(URI.revive(resource), range, options, token), undefined);
}
// ### Document Range Formatting Edit end
@@ -403,17 +406,17 @@ export class LanguagesExtImpl implements LanguagesExt {
$provideOnTypeFormattingEdits(handle: number, resource: UriComponents, position: Position, ch: string,
options: FormattingOptions, token: theia.CancellationToken): Promise {
- return this.withAdapter(handle, OnTypeFormattingAdapter, adapter => adapter.provideOnTypeFormattingEdits(URI.revive(resource), position, ch, options, token));
+ return this.withAdapter(handle, OnTypeFormattingAdapter, adapter => adapter.provideOnTypeFormattingEdits(URI.revive(resource), position, ch, options, token), undefined);
}
// ### On Type Formatting Edit end
// ### Document Link Provider begin
$provideDocumentLinks(handle: number, resource: UriComponents, token: theia.CancellationToken): Promise {
- return this.withAdapter(handle, LinkProviderAdapter, adapter => adapter.provideLinks(URI.revive(resource), token));
+ return this.withAdapter(handle, LinkProviderAdapter, adapter => adapter.provideLinks(URI.revive(resource), token), undefined);
}
$resolveDocumentLink(handle: number, link: DocumentLink, token: theia.CancellationToken): Promise {
- return this.withAdapter(handle, LinkProviderAdapter, adapter => adapter.resolveLink(link, token));
+ return this.withAdapter(handle, LinkProviderAdapter, adapter => adapter.resolveLink(link, token), undefined);
}
registerLinkProvider(selector: theia.DocumentSelector, provider: theia.DocumentLinkProvider, pluginInfo: PluginInfo): theia.Disposable {
@@ -423,7 +426,7 @@ export class LanguagesExtImpl implements LanguagesExt {
}
$releaseDocumentLinks(handle: number, ids: number[]): void {
- this.withAdapter(handle, LinkProviderAdapter, async adapter => adapter.releaseDocumentLinks(ids));
+ this.withAdapter(handle, LinkProviderAdapter, async adapter => adapter.releaseDocumentLinks(ids), undefined);
}
// ### Document Link Provider end
@@ -452,7 +455,7 @@ export class LanguagesExtImpl implements LanguagesExt {
context: CodeActionContext,
token: theia.CancellationToken
): Promise {
- return this.withAdapter(handle, CodeActionAdapter, adapter => adapter.provideCodeAction(URI.revive(resource), rangeOrSelection, context, token));
+ return this.withAdapter(handle, CodeActionAdapter, adapter => adapter.provideCodeAction(URI.revive(resource), rangeOrSelection, context, token), undefined);
}
// ### Code Actions Provider end
@@ -472,21 +475,21 @@ export class LanguagesExtImpl implements LanguagesExt {
}
$provideCodeLenses(handle: number, resource: UriComponents, token: theia.CancellationToken): Promise {
- return this.withAdapter(handle, CodeLensAdapter, adapter => adapter.provideCodeLenses(URI.revive(resource), token));
+ return this.withAdapter(handle, CodeLensAdapter, adapter => adapter.provideCodeLenses(URI.revive(resource), token), undefined);
}
$resolveCodeLens(handle: number, resource: UriComponents, symbol: CodeLensSymbol, token: theia.CancellationToken): Promise {
- return this.withAdapter(handle, CodeLensAdapter, adapter => adapter.resolveCodeLens(URI.revive(resource), symbol, token));
+ return this.withAdapter(handle, CodeLensAdapter, adapter => adapter.resolveCodeLens(URI.revive(resource), symbol, token), undefined);
}
$releaseCodeLenses(handle: number, ids: number[]): void {
- this.withAdapter(handle, CodeLensAdapter, async adapter => adapter.releaseCodeLenses(ids));
+ this.withAdapter(handle, CodeLensAdapter, async adapter => adapter.releaseCodeLenses(ids), undefined);
}
// ### Code Lens Provider end
// ### Code Reference Provider begin
$provideReferences(handle: number, resource: UriComponents, position: Position, context: ReferenceContext, token: theia.CancellationToken): Promise {
- return this.withAdapter(handle, ReferenceAdapter, adapter => adapter.provideReferences(URI.revive(resource), position, context, token));
+ return this.withAdapter(handle, ReferenceAdapter, adapter => adapter.provideReferences(URI.revive(resource), position, context, token), undefined);
}
registerReferenceProvider(selector: theia.DocumentSelector, provider: theia.ReferenceProvider, pluginInfo: PluginInfo): theia.Disposable {
@@ -504,7 +507,7 @@ export class LanguagesExtImpl implements LanguagesExt {
}
$provideDocumentSymbols(handle: number, resource: UriComponents, token: theia.CancellationToken): Promise {
- return this.withAdapter(handle, OutlineAdapter, adapter => adapter.provideDocumentSymbols(URI.revive(resource), token));
+ return this.withAdapter(handle, OutlineAdapter, adapter => adapter.provideDocumentSymbols(URI.revive(resource), token), undefined);
}
// ### Document Symbol Provider end
@@ -516,11 +519,11 @@ export class LanguagesExtImpl implements LanguagesExt {
}
$provideDocumentColors(handle: number, resource: UriComponents, token: theia.CancellationToken): Promise {
- return this.withAdapter(handle, ColorProviderAdapter, adapter => adapter.provideColors(URI.revive(resource), token));
+ return this.withAdapter(handle, ColorProviderAdapter, adapter => adapter.provideColors(URI.revive(resource), token), []);
}
$provideColorPresentations(handle: number, resource: UriComponents, colorInfo: RawColorInfo, token: theia.CancellationToken): Promise {
- return this.withAdapter(handle, ColorProviderAdapter, adapter => adapter.provideColorPresentations(URI.revive(resource), colorInfo, token));
+ return this.withAdapter(handle, ColorProviderAdapter, adapter => adapter.provideColorPresentations(URI.revive(resource), colorInfo, token), []);
}
// ### Color Provider end
@@ -537,7 +540,7 @@ export class LanguagesExtImpl implements LanguagesExt {
context: theia.FoldingContext,
token: theia.CancellationToken
): Promise {
- return this.withAdapter(callId, FoldingProviderAdapter, adapter => adapter.provideFoldingRanges(URI.revive(resource), context, token));
+ return this.withAdapter(callId, FoldingProviderAdapter, adapter => adapter.provideFoldingRanges(URI.revive(resource), context, token), undefined);
}
// ### Folding Range Provider end
@@ -549,11 +552,11 @@ export class LanguagesExtImpl implements LanguagesExt {
}
$provideRenameEdits(handle: number, resource: UriComponents, position: Position, newName: string, token: theia.CancellationToken): Promise {
- return this.withAdapter(handle, RenameAdapter, adapter => adapter.provideRenameEdits(URI.revive(resource), position, newName, token));
+ return this.withAdapter(handle, RenameAdapter, adapter => adapter.provideRenameEdits(URI.revive(resource), position, newName, token), undefined);
}
$resolveRenameLocation(handle: number, resource: UriComponents, position: Position, token: theia.CancellationToken): Promise {
- return this.withAdapter(handle, RenameAdapter, adapter => adapter.resolveRenameLocation(URI.revive(resource), position, token));
+ return this.withAdapter(handle, RenameAdapter, adapter => adapter.resolveRenameLocation(URI.revive(resource), position, token), undefined);
}
// ### Rename Provider end
@@ -565,11 +568,11 @@ export class LanguagesExtImpl implements LanguagesExt {
}
$provideRootDefinition(handle: number, resource: UriComponents, location: Position, token: theia.CancellationToken): Promise {
- return this.withAdapter(handle, CallHierarchyAdapter, adapter => adapter.provideRootDefinition(URI.revive(resource), location, token));
+ return this.withAdapter(handle, CallHierarchyAdapter, adapter => adapter.provideRootDefinition(URI.revive(resource), location, token), undefined);
}
$provideCallers(handle: number, definition: CallHierarchyDefinition, token: theia.CancellationToken): Promise {
- return this.withAdapter(handle, CallHierarchyAdapter, adapter => adapter.provideCallers(definition, token));
+ return this.withAdapter(handle, CallHierarchyAdapter, adapter => adapter.provideCallers(definition, token), undefined);
}
// ### Call Hierarchy Provider end
}
diff --git a/packages/plugin-ext/src/plugin/plugin-manager.ts b/packages/plugin-ext/src/plugin/plugin-manager.ts
index 0a33047a13887..a303d68464397 100644
--- a/packages/plugin-ext/src/plugin/plugin-manager.ts
+++ b/packages/plugin-ext/src/plugin/plugin-manager.ts
@@ -84,6 +84,7 @@ export class PluginManagerExtImpl implements PluginManagerExt, PluginManager {
'onWebviewPanel'
]);
+ private configStorage: ConfigStorage | undefined;
private readonly registry = new Map();
private readonly activations = new Map Promise)[] | undefined>();
/** promises to whether loading each plugin has been successful */
@@ -196,14 +197,16 @@ export class PluginManagerExtImpl implements PluginManagerExt, PluginManager {
}
async $start(params: PluginManagerStartParams): Promise {
+ this.configStorage = params.configStorage;
+
const [plugins, foreignPlugins] = await this.host.init(params.plugins);
// add foreign plugins
for (const plugin of foreignPlugins) {
- this.registerPlugin(plugin, params.configStorage);
+ this.registerPlugin(plugin);
}
// add own plugins, before initialization
for (const plugin of plugins) {
- this.registerPlugin(plugin, params.configStorage);
+ this.registerPlugin(plugin);
}
// run eager plugins
@@ -219,15 +222,10 @@ export class PluginManagerExtImpl implements PluginManagerExt, PluginManager {
this.fireOnDidChange();
}
- protected registerPlugin(plugin: Plugin, configStorage: ConfigStorage): void {
+ protected registerPlugin(plugin: Plugin): void {
this.registry.set(plugin.model.id, plugin);
if (plugin.pluginPath && Array.isArray(plugin.rawModel.activationEvents)) {
- const activation = async () => {
- const title = `Activating ${plugin.model.displayName || plugin.model.name}`;
- const id = await this.notificationMain.$startProgress({ title, location: 'window' });
- await this.loadPlugin(plugin, configStorage);
- this.notificationMain.$stopProgress(id);
- };
+ const activation = () => this.$activatePlugin(plugin.model.id);
// an internal activation event is a subject to change
this.setActivation(`onPlugin:${plugin.model.id}`, activation);
const unsupportedActivationEvents = plugin.rawModel.activationEvents.filter(e => !PluginManagerExtImpl.SUPPORTED_ACTIVATION_EVENTS.has(e.split(':')[0]));
@@ -261,31 +259,39 @@ export class PluginManagerExtImpl implements PluginManagerExt, PluginManager {
let loading = this.loadedPlugins.get(plugin.model.id);
if (!loading) {
loading = (async () => {
- if (plugin.rawModel.extensionDependencies) {
- for (const dependencyId of plugin.rawModel.extensionDependencies) {
- const dependency = this.registry.get(dependencyId.toLowerCase());
- const id = plugin.model.displayName || plugin.model.id;
- if (dependency) {
- const depId = dependency.model.displayName || dependency.model.id;
- const loadedSuccessfully = await this.loadPlugin(dependency, configStorage, visited);
- if (!loadedSuccessfully) {
- const message = `Cannot activate extension '${id}' because it depends on extension '${depId}', which failed to activate.`;
+ const progressId = await this.notificationMain.$startProgress({
+ title: `Activating ${plugin.model.displayName || plugin.model.name}`,
+ location: 'window'
+ });
+ try {
+ if (plugin.rawModel.extensionDependencies) {
+ for (const dependencyId of plugin.rawModel.extensionDependencies) {
+ const dependency = this.registry.get(dependencyId.toLowerCase());
+ const id = plugin.model.displayName || plugin.model.id;
+ if (dependency) {
+ const depId = dependency.model.displayName || dependency.model.id;
+ const loadedSuccessfully = await this.loadPlugin(dependency, configStorage, visited);
+ if (!loadedSuccessfully) {
+ const message = `Cannot activate extension '${id}' because it depends on extension '${depId}', which failed to activate.`;
+ this.messageRegistryProxy.$showMessage(MainMessageType.Error, message, {}, []);
+ return false;
+ }
+ } else {
+ const message = `Cannot activate the '${id}' extension because it depends on the '${dependencyId}' extension, which is not installed.`;
this.messageRegistryProxy.$showMessage(MainMessageType.Error, message, {}, []);
+ console.warn(message);
return false;
}
- } else {
- const message = `Cannot activate the '${id}' extension because it depends on the '${dependencyId}' extension, which is not installed.`;
- this.messageRegistryProxy.$showMessage(MainMessageType.Error, message, {}, []);
- console.warn(message);
- return false;
}
}
- }
- let pluginMain = this.host.loadPlugin(plugin);
- // see https://github.com/TypeFox/vscode/blob/70b8db24a37fafc77247de7f7cb5bb0195120ed0/src/vs/workbench/api/common/extHostExtensionService.ts#L372-L376
- pluginMain = pluginMain || {};
- return this.startPlugin(plugin, configStorage, pluginMain);
+ let pluginMain = this.host.loadPlugin(plugin);
+ // see https://github.com/TypeFox/vscode/blob/70b8db24a37fafc77247de7f7cb5bb0195120ed0/src/vs/workbench/api/common/extHostExtensionService.ts#L372-L376
+ pluginMain = pluginMain || {};
+ return await this.startPlugin(plugin, configStorage, pluginMain);
+ } finally {
+ this.notificationMain.$stopProgress(progressId);
+ }
})();
}
this.loadedPlugins.set(plugin.model.id, loading);
@@ -293,6 +299,9 @@ export class PluginManagerExtImpl implements PluginManagerExt, PluginManager {
}
async $updateStoragePath(path: string | undefined): Promise {
+ if (this.configStorage) {
+ this.configStorage.hostStoragePath = path;
+ }
this.pluginContextsMap.forEach((pluginContext: theia.PluginContext, pluginId: string) => {
pluginContext.storagePath = path ? join(path, pluginId) : undefined;
});
@@ -309,6 +318,13 @@ export class PluginManagerExtImpl implements PluginManagerExt, PluginManager {
}
}
+ async $activatePlugin(id: string): Promise {
+ const plugin = this.registry.get(id);
+ if (plugin && this.configStorage) {
+ await this.loadPlugin(plugin, this.configStorage);
+ }
+ }
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private async startPlugin(plugin: Plugin, configStorage: ConfigStorage, pluginMain: any): Promise {
const subscriptions: theia.Disposable[] = [];
diff --git a/tsconfig.json b/tsconfig.json
index e62e4145a55a9..4445c3b8712ac 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -8,9 +8,11 @@
"include": [
"dev-packages/*/src",
"packages/*/src",
- "examples/*/src"
+ "examples/*/src",
+ "examples/browser/src-gen"
],
"compilerOptions": {
+ "allowJs": true,
"baseUrl": ".",
"paths": {
"mv": [
@@ -165,4 +167,4 @@
]
}
}
-}
+}
\ No newline at end of file