Merge remote-tracking branch 'origin/main' into tyriar/142669

This commit is contained in:
Daniel Imms 2022-03-14 11:53:04 -07:00
commit 2ce4442722
94 changed files with 1759 additions and 955 deletions

View file

@ -1273,7 +1273,7 @@ export class Repository implements Disposable {
const diffEditorTabsToClose: Tab[] = [];
// Index
const tabs = window.tabGroups.all.map(g => g.tabs).flat(1);
const tabs = window.tabGroups.groups.map(g => g.tabs).flat(1);
diffEditorTabsToClose.push(...tabs
.filter(t =>
t.resource && t.resource.scheme === 'git' && t.kind === TabKind.Diff &&

View file

@ -6,7 +6,7 @@
"git": {
"name": "sumneko/lua.tmbundle",
"repositoryUrl": "https://github.com/sumneko/lua.tmbundle",
"commitHash": "9b79cbc54c3267065fca3d7b5b3845d1efbd091d"
"commitHash": "43da68bb99b948782e57985f633fa0c395190674"
}
},
"licenseDetail": [

View file

@ -4,7 +4,7 @@
"If you want to provide a fix or improvement, please create a pull request against the original repository.",
"Once accepted there, we are happy to receive an update request."
],
"version": "https://github.com/sumneko/lua.tmbundle/commit/9b79cbc54c3267065fca3d7b5b3845d1efbd091d",
"version": "https://github.com/sumneko/lua.tmbundle/commit/43da68bb99b948782e57985f633fa0c395190674",
"name": "Lua",
"scopeName": "source.lua",
"patterns": [
@ -170,10 +170,6 @@
"match": "(?<![^.]\\.|:)\\b(async)\\b(?!\\s*=(?!=))",
"name": "entity.name.tag.lua"
},
{
"match": "(?<![^.]\\.|:)\\b(unknown|any|boolean|number|integer|thread|table|string|userdata|lightuserdata|function|method)\\b(?!\\s*=(?!=))",
"name": "support.type.lua"
},
{
"match": "(?<![^.]\\.|:)\\b(coroutine\\.(create|isyieldable|close|resume|running|status|wrap|yield)|string\\.(byte|char|dump|find|format|gmatch|gsub|len|lower|match|pack|packsize|rep|reverse|sub|unpack|upper)|table\\.(concat|insert|maxn|move|pack|remove|sort|unpack)|math\\.(abs|acos|asin|atan2?|ceil|cosh?|deg|exp|floor|fmod|frexp|ldexp|log|log10|max|min|modf|pow|rad|random|randomseed|sinh?|sqrt|tanh?|tointeger|type)|io\\.(close|flush|input|lines|open|output|popen|read|tmpfile|type|write)|os\\.(clock|date|difftime|execute|exit|getenv|remove|rename|setlocale|time|tmpname)|package\\.(loadlib|seeall|searchpath)|debug\\.(debug|[gs]etfenv|[gs]ethook|getinfo|[gs]etlocal|[gs]etmetatable|getregistry|[gs]etupvalue|[gs]etuservalue|set[Cc]stacklimit|traceback|upvalueid|upvaluejoin)|bit32\\.(arshift|band|bnot|bor|btest|bxor|extract|replace|lrotate|lshift|rrotate|rshift)|utf8\\.(char|codes|codepoint|len|offset))\\b(?!\\s*=(?!=))",
"name": "support.function.library.lua"
@ -184,7 +180,7 @@
},
{
"match": "\\b([a-zA-Z_][a-zA-Z0-9_]*)\\b(?=\\s*(?:[({\"']|\\[\\[))",
"name": "entity.name.function.lua"
"name": "support.function.any-method.lua"
},
{
"match": "\\b([a-zA-Z_][a-zA-Z0-9_]*)\\b(?=\\s*\\??:)",

View file

@ -6,7 +6,7 @@
"git": {
"name": "seti-ui",
"repositoryUrl": "https://github.com/jesseweed/seti-ui",
"commitHash": "921e46b4c378059032c2a9247a6cc96d0f9e57b4"
"commitHash": "8dba1bc311dad1b9bc23c4779149f3bf9baa8cb0"
}
},
"version": "0.1.0"

View file

@ -1781,10 +1781,16 @@
"karma.conf.js": "_karma",
"karma.conf.coffee": "_karma",
"readme.md": "_info",
"readme.txt": "_info",
"readme": "_info",
"changelog.md": "_clock",
"changelog.txt": "_clock",
"changelog": "_clock",
"changes.md": "_clock",
"changes.txt": "_clock",
"changes": "_clock",
"version.md": "_clock",
"version.txt": "_clock",
"version": "_clock",
"mvnw": "_maven",
"tsconfig.json": "_tsconfig",
@ -1830,16 +1836,26 @@
"webpack.prod.cjs": "_webpack",
"license": "_license",
"licence": "_license",
"license.txt": "_license",
"licence.txt": "_license",
"license.md": "_license",
"licence.md": "_license",
"copying": "_license",
"copying.txt": "_license",
"copying.md": "_license",
"compiling": "_license_1",
"compiling.txt": "_license_1",
"compiling.md": "_license_1",
"contributing": "_license_2",
"contributing.txt": "_license_2",
"contributing.md": "_license_2",
"qmakefile": "_makefile_1",
"omakefile": "_makefile_2",
"cmakelists.txt": "_makefile_3",
"procfile": "_heroku",
"todo": "_todo",
"todo.txt": "_todo",
"todo.md": "_todo",
"npm-debug.log": "_npm_ignored"
},
"languageIds": {
@ -2239,10 +2255,16 @@
"karma.conf.js": "_karma_light",
"karma.conf.coffee": "_karma_light",
"readme.md": "_info_light",
"readme.txt": "_info_light",
"readme": "_info_light",
"changelog.md": "_clock_light",
"changelog.txt": "_clock_light",
"changelog": "_clock_light",
"changes.md": "_clock_light",
"changes.txt": "_clock_light",
"changes": "_clock_light",
"version.md": "_clock_light",
"version.txt": "_clock_light",
"version": "_clock_light",
"mvnw": "_maven_light",
"tsconfig.json": "_tsconfig_light",
@ -2288,11 +2310,19 @@
"webpack.prod.cjs": "_webpack_light",
"license": "_license_light",
"licence": "_license_light",
"license.txt": "_license_light",
"licence.txt": "_license_light",
"license.md": "_license_light",
"licence.md": "_license_light",
"copying": "_license_light",
"copying.txt": "_license_light",
"copying.md": "_license_light",
"compiling": "_license_1_light",
"compiling.txt": "_license_1_light",
"compiling.md": "_license_1_light",
"contributing": "_license_2_light",
"contributing.txt": "_license_2_light",
"contributing.md": "_license_2_light",
"qmakefile": "_makefile_1_light",
"omakefile": "_makefile_2_light",
"cmakelists.txt": "_makefile_3_light",
@ -2300,5 +2330,5 @@
"npm-debug.log": "_npm_ignored_light"
}
},
"version": "https://github.com/jesseweed/seti-ui/commit/921e46b4c378059032c2a9247a6cc96d0f9e57b4"
"version": "https://github.com/jesseweed/seti-ui/commit/8dba1bc311dad1b9bc23c4779149f3bf9baa8cb0"
}

View file

@ -112,15 +112,15 @@ suite('vscode API - commands', () => {
await commands.executeCommand('vscode.open', uri);
assert.strictEqual(window.activeTextEditor?.viewColumn, ViewColumn.One);
assert.strictEqual(window.tabGroups.all[0].activeTab?.viewColumn, ViewColumn.One);
assert.strictEqual(window.tabGroups.groups[0].activeTab?.viewColumn, ViewColumn.One);
await commands.executeCommand('vscode.open', uri, ViewColumn.Two);
assert.strictEqual(window.activeTextEditor?.viewColumn, ViewColumn.Two);
assert.strictEqual(window.tabGroups.all[1].activeTab?.viewColumn, ViewColumn.Two);
assert.strictEqual(window.tabGroups.groups[1].activeTab?.viewColumn, ViewColumn.Two);
await commands.executeCommand('vscode.open', uri, ViewColumn.One);
assert.strictEqual(window.activeTextEditor?.viewColumn, ViewColumn.One);
assert.strictEqual(window.tabGroups.all[0].activeTab?.viewColumn, ViewColumn.One);
assert.strictEqual(window.tabGroups.groups[0].activeTab?.viewColumn, ViewColumn.One);
let e1: Error | undefined = undefined;
try {

View file

@ -383,12 +383,12 @@ suite('vscode API - window', () => {
await window.showTextDocument(docC, { viewColumn: ViewColumn.Two, preview: false });
const tabGroups = window.tabGroups;
assert.strictEqual(tabGroups.all.length, 2);
assert.strictEqual(tabGroups.groups.length, 2);
const group1Tabs = tabGroups.all[0].tabs;
const group1Tabs = tabGroups.groups[0].tabs;
assert.strictEqual(group1Tabs.length, 2);
const group2Tabs = tabGroups.all[1].tabs;
const group2Tabs = tabGroups.groups[1].tabs;
assert.strictEqual(group2Tabs.length, 1);
await group1Tabs[0].move(1, ViewColumn.One);
@ -397,7 +397,7 @@ suite('vscode API - window', () => {
test('Tabs - vscode.open & vscode.diff', async function () {
// Simple function to get the active tab
const getActiveTab = () => {
return window.tabGroups.all.find(g => g.isActive)?.activeTab;
return window.tabGroups.groups.find(g => g.isActive)?.activeTab;
};
const [docA, docB, docC] = await Promise.all([
@ -420,7 +420,7 @@ suite('vscode API - window', () => {
await commands.executeCommand('vscode.diff', leftDiff, rightDiff, 'Diff', { viewColumn: ViewColumn.Four, preview: false });
assert.strictEqual(getActiveTab()?.viewColumn, ViewColumn.Four);
const tabs = window.tabGroups.all.map(g => g.tabs).flat(1);
const tabs = window.tabGroups.groups.map(g => g.tabs).flat(1);
assert.strictEqual(tabs.length, 5);
assert.strictEqual(tabs[0].resource?.toString(), docA.uri.toString());
assert.strictEqual(tabs[1].resource?.toString(), docB.uri.toString());
@ -451,7 +451,7 @@ suite('vscode API - window', () => {
const rightDiff = await createRandomFile();
await commands.executeCommand('vscode.diff', leftDiff, rightDiff, 'Diff', { viewColumn: ViewColumn.Three, preview: false });
const tabs = window.tabGroups.all.map(g => g.tabs).flat(1);
const tabs = window.tabGroups.groups.map(g => g.tabs).flat(1);
assert.strictEqual(tabs.length, 5);
// All resources should match the text documents as they're the only tabs currently open
@ -480,7 +480,7 @@ suite('vscode API - window', () => {
// Function to acquire the active tab within the active group
const getActiveTabInActiveGroup = () => {
const activeGroup = window.tabGroups.all.filter(group => group.isActive)[0];
const activeGroup = window.tabGroups.groups.filter(group => group.isActive)[0];
return activeGroup.activeTab;
};

View file

@ -409,14 +409,14 @@
},
{
"c": "fact",
"t": "source.lua entity.name.function.lua",
"t": "source.lua support.function.any-method.lua",
"r": {
"dark_plus": "entity.name.function: #DCDCAA",
"light_plus": "entity.name.function: #795E26",
"dark_plus": "support.function: #DCDCAA",
"light_plus": "support.function: #795E26",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "entity.name.function: #DCDCAA",
"hc_light": "entity.name.function: #795E26"
"hc_black": "support.function: #DCDCAA",
"hc_light": "support.function: #795E26"
}
},
{
@ -805,14 +805,14 @@
},
{
"c": "fact",
"t": "source.lua entity.name.function.lua",
"t": "source.lua support.function.any-method.lua",
"r": {
"dark_plus": "entity.name.function: #DCDCAA",
"light_plus": "entity.name.function: #795E26",
"dark_plus": "support.function: #DCDCAA",
"light_plus": "support.function: #795E26",
"dark_vs": "default: #D4D4D4",
"light_vs": "default: #000000",
"hc_black": "entity.name.function: #DCDCAA",
"hc_light": "entity.name.function: #795E26"
"hc_black": "support.function: #DCDCAA",
"hc_light": "support.function: #795E26"
}
},
{

View file

@ -1417,6 +1417,49 @@ export function detectFullscreen(): IDetectedFullscreen | null {
// -- sanitize and trusted html
/**
* Hooks dompurify using `afterSanitizeAttributes` to check that all `href` and `src`
* attributes are valid.
*/
export function hookDomPurifyHrefAndSrcSanitizer(allowedProtocols: readonly string[], allowDataImages = false): IDisposable {
// https://github.com/cure53/DOMPurify/blob/main/demos/hooks-scheme-allowlist.html
// build an anchor to map URLs to
const anchor = document.createElement('a');
dompurify.addHook('afterSanitizeAttributes', (node) => {
// check all href/src attributes for validity
for (const attr of ['href', 'src']) {
if (node.hasAttribute(attr)) {
const attrValue = node.getAttribute(attr) as string;
if (attr === 'href' && attrValue.startsWith('#')) {
// Allow fragment links
continue;
}
anchor.href = attrValue;
if (!allowedProtocols.includes(anchor.protocol.replace(/:$/, ''))) {
if (allowDataImages && attr === 'src' && anchor.href.startsWith('data:')) {
continue;
}
node.removeAttribute(attr);
}
}
}
});
return toDisposable(() => {
dompurify.removeHook('afterSanitizeAttributes');
});
}
const defaultSafeProtocols = [
Schemas.http,
Schemas.https,
Schemas.command,
];
/**
* Sanitizes the given `value` and reset the given `node` with it.
*/
@ -1428,29 +1471,12 @@ export function safeInnerHtml(node: HTMLElement, value: string): void {
RETURN_DOM_FRAGMENT: false,
};
const allowedProtocols = [Schemas.http, Schemas.https, Schemas.command];
// https://github.com/cure53/DOMPurify/blob/main/demos/hooks-scheme-allowlist.html
dompurify.addHook('afterSanitizeAttributes', (node) => {
// build an anchor to map URLs to
const anchor = document.createElement('a');
// check all href/src attributes for validity
for (const attr in ['href', 'src']) {
if (node.hasAttribute(attr)) {
anchor.href = node.getAttribute(attr) as string;
if (!allowedProtocols.includes(anchor.protocol)) {
node.removeAttribute(attr);
}
}
}
});
const hook = hookDomPurifyHrefAndSrcSanitizer(defaultSafeProtocols);
try {
const html = dompurify.sanitize(value, { ...options, RETURN_TRUSTED_TYPE: true });
node.innerHTML = html as unknown as string;
} finally {
dompurify.removeHook('afterSanitizeAttributes');
hook.dispose();
}
}

View file

@ -326,27 +326,13 @@ function sanitizeRenderedMarkdown(
}
});
// build an anchor to map URLs to
const anchor = document.createElement('a');
// https://github.com/cure53/DOMPurify/blob/main/demos/hooks-scheme-allowlist.html
dompurify.addHook('afterSanitizeAttributes', (node) => {
// check all href/src attributes for validity
for (const attr of ['href', 'src']) {
if (node.hasAttribute(attr)) {
anchor.href = node.getAttribute(attr) as string;
if (!allowedSchemes.includes(anchor.protocol.replace(/:$/, ''))) {
node.removeAttribute(attr);
}
}
}
});
const hook = DOM.hookDomPurifyHrefAndSrcSanitizer(allowedSchemes);
try {
return dompurify.sanitize(renderedMarkdown, { ...config, RETURN_TRUSTED_TYPE: true });
} finally {
dompurify.removeHook('uponSanitizeAttribute');
dompurify.removeHook('afterSanitizeAttributes');
hook.dispose();
}
}

View file

@ -161,11 +161,11 @@ export class SingleModelEditStackElement implements IResourceUndoRedoElement {
return this.model.uri;
}
public get label(): string {
return nls.localize('edit', "Typing");
}
constructor(model: ITextModel, beforeCursorState: Selection[] | null) {
constructor(
public readonly label: string,
model: ITextModel,
beforeCursorState: Selection[] | null
) {
this.model = model;
this._data = SingleModelEditStackData.create(model, beforeCursorState);
}
@ -413,7 +413,7 @@ export class EditStack {
if (isEditStackElement(lastElement) && lastElement.canAppend(this._model)) {
return lastElement;
}
const newElement = new SingleModelEditStackElement(this._model, beforeCursorState);
const newElement = new SingleModelEditStackElement(nls.localize('edit', "Typing"), this._model, beforeCursorState);
this._undoRedoService.pushElement(newElement);
return newElement;
}

View file

@ -10,6 +10,7 @@ import { basename, posix } from 'vs/base/common/path';
import { DataUri } from 'vs/base/common/resources';
import { startsWithUTF8BOM } from 'vs/base/common/strings';
import { URI } from 'vs/base/common/uri';
import { PLAINTEXT_LANGUAGE_ID } from 'vs/editor/common/languages/modesRegistry';
export interface ILanguageAssociation {
readonly id: string;
@ -119,11 +120,27 @@ export function clearConfiguredLanguageAssociations(): void {
userRegisteredAssociations = [];
}
interface IdAndMime {
id: string;
mime: string;
}
/**
* Given a file, return the best matching mime types for it
* based on the registered language associations.
*/
export function getMimeTypes(resource: URI | null, firstLine?: string): string[] {
return getAssociations(resource, firstLine).map(item => item.mime);
}
/**
* @see `getMimeTypes`
*/
export function getLanguageIds(resource: URI | null, firstLine?: string): string[] {
return getAssociations(resource, firstLine).map(item => item.id);
}
function getAssociations(resource: URI | null, firstLine?: string): IdAndMime[] {
let path: string | undefined;
if (resource) {
switch (resource.scheme) {
@ -141,7 +158,7 @@ export function getMimeTypes(resource: URI | null, firstLine?: string): string[]
}
if (!path) {
return [Mimes.unknown];
return [{ id: 'unknown', mime: Mimes.unknown }];
}
path = path.toLowerCase();
@ -149,29 +166,29 @@ export function getMimeTypes(resource: URI | null, firstLine?: string): string[]
const filename = basename(path);
// 1.) User configured mappings have highest priority
const configuredLanguage = getMimeByPath(path, filename, userRegisteredAssociations);
const configuredLanguage = getAssociationByPath(path, filename, userRegisteredAssociations);
if (configuredLanguage) {
return [configuredLanguage, Mimes.text];
return [configuredLanguage, { id: PLAINTEXT_LANGUAGE_ID, mime: Mimes.text }];
}
// 2.) Registered mappings have middle priority
const registeredLanguage = getMimeByPath(path, filename, nonUserRegisteredAssociations);
const registeredLanguage = getAssociationByPath(path, filename, nonUserRegisteredAssociations);
if (registeredLanguage) {
return [registeredLanguage, Mimes.text];
return [registeredLanguage, { id: PLAINTEXT_LANGUAGE_ID, mime: Mimes.text }];
}
// 3.) Firstline has lowest priority
if (firstLine) {
const firstlineLanguage = getMimeByFirstline(firstLine);
const firstlineLanguage = getAssociationByFirstline(firstLine);
if (firstlineLanguage) {
return [firstlineLanguage, Mimes.text];
return [firstlineLanguage, { id: PLAINTEXT_LANGUAGE_ID, mime: Mimes.text }];
}
}
return [Mimes.unknown];
return [{ id: 'unknown', mime: Mimes.unknown }];
}
function getMimeByPath(path: string, filename: string, associations: ILanguageAssociationItem[]): string | undefined {
function getAssociationByPath(path: string, filename: string, associations: ILanguageAssociationItem[]): ILanguageAssociationItem | undefined {
let filenameMatch: ILanguageAssociationItem | undefined = undefined;
let patternMatch: ILanguageAssociationItem | undefined = undefined;
let extensionMatch: ILanguageAssociationItem | undefined = undefined;
@ -209,23 +226,23 @@ function getMimeByPath(path: string, filename: string, associations: ILanguageAs
// 1.) Exact name match has second highest priority
if (filenameMatch) {
return filenameMatch.mime;
return filenameMatch;
}
// 2.) Match on pattern
if (patternMatch) {
return patternMatch.mime;
return patternMatch;
}
// 3.) Match on extension comes next
if (extensionMatch) {
return extensionMatch.mime;
return extensionMatch;
}
return undefined;
}
function getMimeByFirstline(firstLine: string): string | undefined {
function getAssociationByFirstline(firstLine: string): ILanguageAssociationItem | undefined {
if (startsWithUTF8BOM(firstLine)) {
firstLine = firstLine.substr(1);
}
@ -242,7 +259,7 @@ function getMimeByFirstline(firstLine: string): string | undefined {
const matches = firstLine.match(association.firstline);
if (matches && matches.length > 0) {
return association.mime;
return association;
}
}
}

View file

@ -3,12 +3,11 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { coalesce } from 'vs/base/common/arrays';
import { onUnexpectedError } from 'vs/base/common/errors';
import { Emitter, Event } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
import { compareIgnoreCase, regExpLeadsToEndlessLoop } from 'vs/base/common/strings';
import { clearPlatformLanguageAssociations, getMimeTypes, registerPlatformLanguageAssociation } from 'vs/editor/common/services/languagesAssociations';
import { clearPlatformLanguageAssociations, getLanguageIds, registerPlatformLanguageAssociation } from 'vs/editor/common/services/languagesAssociations';
import { URI } from 'vs/base/common/uri';
import { ILanguageIdCodec, LanguageId } from 'vs/editor/common/languages';
import { ModesRegistry, PLAINTEXT_LANGUAGE_ID } from 'vs/editor/common/languages/modesRegistry';
@ -362,7 +361,6 @@ export class LanguagesRegistry extends Disposable {
if (!resource && !firstLine) {
return [];
}
const mimeTypes = getMimeTypes(resource, firstLine);
return coalesce(mimeTypes.map(mimeType => this.getLanguageIdByMimeType(mimeType)));
return getLanguageIds(resource, firstLine);
}
}

View file

@ -64,7 +64,7 @@ export class MarkdownRenderer {
};
}
protected _getRenderOptions(markdown: IMarkdownString, disposeables: DisposableStore): MarkdownRenderOptions {
protected _getRenderOptions(markdown: IMarkdownString, disposables: DisposableStore): MarkdownRenderOptions {
return {
codeBlockRenderer: async (languageAlias, value) => {
// In markdown,
@ -98,7 +98,7 @@ export class MarkdownRenderer {
asyncRenderCallback: () => this._onDidRenderAsync.fire(),
actionHandler: {
callback: (content) => this._openerService.open(content, { fromUserGesture: true, allowContributedOpeners: true, allowCommands: markdown.isTrusted }).catch(onUnexpectedError),
disposables: disposeables
disposables: disposables
}
};
}

View file

@ -15,10 +15,10 @@ import { URI } from 'vs/base/common/uri';
import * as nls from 'vs/nls';
import {
DidUninstallExtensionEvent, ExtensionManagementError, IExtensionGalleryService, IExtensionIdentifier, IExtensionManagementParticipant, IExtensionManagementService, IGalleryExtension, IGalleryMetadata, ILocalExtension, InstallExtensionEvent, InstallExtensionResult, InstallOperation, InstallOptions,
InstallVSIXOptions, IExtensionsControlManifest, StatisticType, UninstallOptions, TargetPlatform, isTargetPlatformCompatible, TargetPlatformToString, ExtensionManagementErrorCode
InstallVSIXOptions, IExtensionsControlManifest, StatisticType, UninstallOptions, isTargetPlatformCompatible, TargetPlatformToString, ExtensionManagementErrorCode
} from 'vs/platform/extensionManagement/common/extensionManagement';
import { areSameExtensions, ExtensionKey, getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData, getMaliciousExtensionsSet } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { ExtensionType, IExtensionManifest } from 'vs/platform/extensions/common/extensions';
import { ExtensionType, IExtensionManifest, TargetPlatform } from 'vs/platform/extensions/common/extensions';
import { ILogService } from 'vs/platform/log/common/log';
import { IProductService } from 'vs/platform/product/common/productService';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';

View file

@ -16,9 +16,9 @@ import { URI } from 'vs/base/common/uri';
import { IRequestContext, IRequestOptions } from 'vs/base/parts/request/common/request';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { getFallbackTargetPlarforms, getTargetPlatform, IExtensionGalleryService, IExtensionIdentifier, IExtensionInfo, IGalleryExtension, IGalleryExtensionAsset, IGalleryExtensionAssets, IGalleryExtensionVersion, InstallOperation, IQueryOptions, IExtensionsControlManifest, isNotWebExtensionInWebTargetPlatform, isTargetPlatformCompatible, ITranslation, SortBy, SortOrder, StatisticType, TargetPlatform, toTargetPlatform, WEB_EXTENSION_TAG, IExtensionQueryOptions } from 'vs/platform/extensionManagement/common/extensionManagement';
import { getFallbackTargetPlarforms, getTargetPlatform, IExtensionGalleryService, IExtensionIdentifier, IExtensionInfo, IGalleryExtension, IGalleryExtensionAsset, IGalleryExtensionAssets, IGalleryExtensionVersion, InstallOperation, IQueryOptions, IExtensionsControlManifest, isNotWebExtensionInWebTargetPlatform, isTargetPlatformCompatible, ITranslation, SortBy, SortOrder, StatisticType, toTargetPlatform, WEB_EXTENSION_TAG, IExtensionQueryOptions } from 'vs/platform/extensionManagement/common/extensionManagement';
import { adoptToGalleryExtensionId, areSameExtensions, getGalleryExtensionId, getGalleryExtensionTelemetryData } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { IExtensionManifest } from 'vs/platform/extensions/common/extensions';
import { IExtensionManifest, TargetPlatform } from 'vs/platform/extensions/common/extensions';
import { isEngineValid } from 'vs/platform/extensions/common/extensionValidator';
import { IFileService } from 'vs/platform/files/common/files';
import { ILogService } from 'vs/platform/log/common/log';

View file

@ -11,7 +11,7 @@ import { Platform } from 'vs/base/common/platform';
import { URI } from 'vs/base/common/uri';
import { localize } from 'vs/nls';
import { adoptToGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { ExtensionType, IExtension, IExtensionManifest } from 'vs/platform/extensions/common/extensions';
import { ExtensionType, IExtension, IExtensionManifest, TargetPlatform } from 'vs/platform/extensions/common/extensions';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
export const EXTENSION_IDENTIFIER_PATTERN = '^([a-z0-9A-Z][a-z0-9-A-Z]*)\\.([a-z0-9A-Z][a-z0-9-A-Z]*)$';
@ -27,28 +27,6 @@ export function getIdAndVersion(id: string): [string, string | undefined] {
return [adoptToGalleryExtensionId(id), undefined];
}
export const enum TargetPlatform {
WIN32_X64 = 'win32-x64',
WIN32_IA32 = 'win32-ia32',
WIN32_ARM64 = 'win32-arm64',
LINUX_X64 = 'linux-x64',
LINUX_ARM64 = 'linux-arm64',
LINUX_ARMHF = 'linux-armhf',
ALPINE_X64 = 'alpine-x64',
ALPINE_ARM64 = 'alpine-arm64',
DARWIN_X64 = 'darwin-x64',
DARWIN_ARM64 = 'darwin-arm64',
WEB = 'web',
UNIVERSAL = 'universal',
UNKNOWN = 'unknown',
UNDEFINED = 'undefined',
}
export function TargetPlatformToString(targetPlatform: TargetPlatform) {
switch (targetPlatform) {
case TargetPlatform.WIN32_X64: return 'Windows 64 bit';
@ -278,7 +256,6 @@ export interface ILocalExtension extends IExtension {
installedTimestamp?: number;
isPreReleaseVersion: boolean;
preRelease: boolean;
targetPlatform: TargetPlatform;
}
export const enum SortBy {

View file

@ -9,8 +9,8 @@ import { cloneAndChange } from 'vs/base/common/objects';
import { URI, UriComponents } from 'vs/base/common/uri';
import { DefaultURITransformer, IURITransformer, transformAndReviveIncomingURIs } from 'vs/base/common/uriIpc';
import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc';
import { DidUninstallExtensionEvent, IExtensionIdentifier, IExtensionManagementService, IExtensionTipsService, IGalleryExtension, IGalleryMetadata, ILocalExtension, InstallExtensionEvent, InstallExtensionResult, InstallOptions, InstallVSIXOptions, IExtensionsControlManifest, isTargetPlatformCompatible, TargetPlatform, UninstallOptions } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ExtensionType, IExtensionManifest } from 'vs/platform/extensions/common/extensions';
import { DidUninstallExtensionEvent, IExtensionIdentifier, IExtensionManagementService, IExtensionTipsService, IGalleryExtension, IGalleryMetadata, ILocalExtension, InstallExtensionEvent, InstallExtensionResult, InstallOptions, InstallVSIXOptions, IExtensionsControlManifest, isTargetPlatformCompatible, UninstallOptions } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ExtensionType, IExtensionManifest, TargetPlatform } from 'vs/platform/extensions/common/extensions';
function transformIncomingURI(uri: UriComponents, transformer: IURITransformer | null): URI {
return URI.revive(transformer ? transformer.transformIncoming(uri) : uri);

View file

@ -4,8 +4,8 @@
*--------------------------------------------------------------------------------------------*/
import { compareIgnoreCase } from 'vs/base/common/strings';
import { IExtensionIdentifier, IGalleryExtension, ILocalExtension, IExtensionsControlManifest, TargetPlatform } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ExtensionIdentifier, IExtension } from 'vs/platform/extensions/common/extensions';
import { IExtensionIdentifier, IGalleryExtension, ILocalExtension, IExtensionsControlManifest } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ExtensionIdentifier, IExtension, TargetPlatform } from 'vs/platform/extensions/common/extensions';
export function areSameExtensions(a: IExtensionIdentifier, b: IExtensionIdentifier): boolean {
if (a.uuid && b.uuid) {
@ -23,7 +23,7 @@ export class ExtensionKey {
static create(extension: ILocalExtension | IGalleryExtension): ExtensionKey {
const version = (extension as ILocalExtension).manifest ? (extension as ILocalExtension).manifest.version : (extension as IGalleryExtension).version;
const targetPlatform = (extension as ILocalExtension).targetPlatform ? (extension as ILocalExtension).targetPlatform : (extension as IGalleryExtension).properties.targetPlatform;
const targetPlatform = (extension as ILocalExtension).manifest ? (extension as ILocalExtension).targetPlatform : (extension as IGalleryExtension).properties.targetPlatform;
return new ExtensionKey(extension.identifier, version, targetPlatform);
}

View file

@ -23,7 +23,7 @@ import { INativeEnvironmentService } from 'vs/platform/environment/common/enviro
import { AbstractExtensionManagementService, AbstractExtensionTask, IInstallExtensionTask, IUninstallExtensionTask, joinErrors, UninstallExtensionTaskOptions } from 'vs/platform/extensionManagement/common/abstractExtensionManagementService';
import {
ExtensionManagementError, ExtensionManagementErrorCode, getTargetPlatform, IExtensionGalleryService, IExtensionIdentifier, IExtensionManagementService, IGalleryExtension, IGalleryMetadata, ILocalExtension, InstallOperation, InstallOptions,
InstallVSIXOptions, Metadata, TargetPlatform
InstallVSIXOptions, Metadata
} from 'vs/platform/extensionManagement/common/extensionManagement';
import { areSameExtensions, ExtensionKey, getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { ExtensionsDownloader } from 'vs/platform/extensionManagement/node/extensionDownloader';
@ -32,7 +32,7 @@ import { getManifest } from 'vs/platform/extensionManagement/node/extensionManag
import { ExtensionsManifestCache } from 'vs/platform/extensionManagement/node/extensionsManifestCache';
import { ExtensionsScanner, ILocalExtensionManifest } from 'vs/platform/extensionManagement/node/extensionsScanner';
import { ExtensionsWatcher } from 'vs/platform/extensionManagement/node/extensionsWatcher';
import { ExtensionType, IExtensionManifest } from 'vs/platform/extensions/common/extensions';
import { ExtensionType, IExtensionManifest, TargetPlatform } from 'vs/platform/extensions/common/extensions';
import { isEngineValid } from 'vs/platform/extensions/common/extensionValidator';
import { IFileService } from 'vs/platform/files/common/files';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';

View file

@ -20,17 +20,17 @@ import * as pfs from 'vs/base/node/pfs';
import { extract, ExtractError } from 'vs/base/node/zip';
import { localize } from 'vs/nls';
import { INativeEnvironmentService } from 'vs/platform/environment/common/environment';
import { ExtensionManagementError, ExtensionManagementErrorCode, Metadata, ILocalExtension, TargetPlatform } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ExtensionManagementError, ExtensionManagementErrorCode, Metadata, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement';
import { areSameExtensions, ExtensionKey, getGalleryExtensionId, groupByExtension } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { localizeManifest } from 'vs/platform/extensionManagement/common/extensionNls';
import { ExtensionType, IExtensionIdentifier, IExtensionManifest, UNDEFINED_PUBLISHER } from 'vs/platform/extensions/common/extensions';
import { ExtensionType, IExtensionIdentifier, IExtensionManifest, TargetPlatform, UNDEFINED_PUBLISHER } from 'vs/platform/extensions/common/extensions';
import { IFileService } from 'vs/platform/files/common/files';
import { ILogService } from 'vs/platform/log/common/log';
import { IProductService } from 'vs/platform/product/common/productService';
export type ILocalExtensionManifest = IExtensionManifest & { __metadata?: Metadata };
type IRelaxedLocalExtension = Omit<ILocalExtension, 'isBuiltin'> & { isBuiltin: boolean };
type IRelaxedLocalExtension = Omit<ILocalExtension, 'isBuiltin'> & { isBuiltin: boolean; targetPlatform: TargetPlatform };
export class ExtensionsScanner extends Disposable {

View file

@ -13,7 +13,6 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IRawGalleryExtensionVersion, sortExtensionVersions } from 'vs/platform/extensionManagement/common/extensionGalleryService';
import { TargetPlatform } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IFileService } from 'vs/platform/files/common/files';
import { FileService } from 'vs/platform/files/common/fileService';
import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider';
@ -23,6 +22,7 @@ import { IProductService } from 'vs/platform/product/common/productService';
import { resolveMarketplaceHeaders } from 'vs/platform/externalServices/common/marketplace';
import { InMemoryStorageService, IStorageService } from 'vs/platform/storage/common/storage';
import { TelemetryConfiguration, TELEMETRY_SETTING_ID } from 'vs/platform/telemetry/common/telemetry';
import { TargetPlatform } from 'vs/platform/extensions/common/extensions';
class EnvironmentServiceMock extends mock<IEnvironmentService>() {
override readonly serviceMachineIdResource: URI;

View file

@ -3,8 +3,9 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { EXTENSION_IDENTIFIER_PATTERN, TargetPlatform } from 'vs/platform/extensionManagement/common/extensionManagement';
import { EXTENSION_IDENTIFIER_PATTERN } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ExtensionKey } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { TargetPlatform } from 'vs/platform/extensions/common/extensions';
suite('Extension Identifier Pattern', () => {

View file

@ -6,7 +6,6 @@
import * as strings from 'vs/base/common/strings';
import { URI } from 'vs/base/common/uri';
import { ExtensionKind } from 'vs/platform/environment/common/environment';
import { TargetPlatform } from 'vs/platform/extensionManagement/common/extensionManagement';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { getRemoteName } from 'vs/platform/remote/common/remoteHosts';
@ -288,12 +287,35 @@ export const enum ExtensionType {
User
}
export const enum TargetPlatform {
WIN32_X64 = 'win32-x64',
WIN32_IA32 = 'win32-ia32',
WIN32_ARM64 = 'win32-arm64',
LINUX_X64 = 'linux-x64',
LINUX_ARM64 = 'linux-arm64',
LINUX_ARMHF = 'linux-armhf',
ALPINE_X64 = 'alpine-x64',
ALPINE_ARM64 = 'alpine-arm64',
DARWIN_X64 = 'darwin-x64',
DARWIN_ARM64 = 'darwin-arm64',
WEB = 'web',
UNIVERSAL = 'universal',
UNKNOWN = 'unknown',
UNDEFINED = 'undefined',
}
export interface IExtension {
readonly type: ExtensionType;
readonly isBuiltin: boolean;
readonly identifier: IExtensionIdentifier;
readonly manifest: IExtensionManifest;
readonly location: URI;
readonly targetPlatform: TargetPlatform;
readonly readmeUrl?: URI;
readonly changelogUrl?: URI;
}
@ -356,7 +378,7 @@ export class ExtensionIdentifier {
export interface IExtensionDescription extends IExtensionManifest {
readonly identifier: ExtensionIdentifier;
readonly uuid?: string;
readonly targetPlatform?: TargetPlatform;
readonly targetPlatform: TargetPlatform;
readonly isBuiltin: boolean;
readonly isUserBuiltin: boolean;
readonly isUnderDevelopment: boolean;

View file

@ -33,6 +33,7 @@ export const serverOptions: OptionDescriptions<ServerParsedArgs> = {
'user-data-dir': OPTIONS['user-data-dir'],
'driver': OPTIONS['driver'],
'disable-telemetry': OPTIONS['disable-telemetry'],
'disable-workspace-trust': OPTIONS['disable-workspace-trust'],
'file-watcher-polling': { type: 'string', deprecates: ['fileWatcherPolling'] },
'log': OPTIONS['log'],
'logsPath': OPTIONS['logsPath'],
@ -130,6 +131,8 @@ export interface ServerParsedArgs {
'telemetry-level'?: string;
'disable-workspace-trust'?: boolean;
/* ----- vs code options ----- */
'user-data-dir'?: string;

View file

@ -278,6 +278,7 @@ export class WebClientServer {
_wrapWebWorkerExtHostInIframe,
developmentOptions: { enableSmokeTestDriver: this._environmentService.driverHandle === 'web' ? true : undefined },
settingsSyncOptions: !this._environmentService.isBuilt && this._environmentService.args['enable-sync'] ? { enabled: true } : undefined,
enableWorkspaceTrust: !this._environmentService.args['disable-workspace-trust'],
productConfiguration: <Partial<IProductConfiguration>>{
embedderIdentifier: 'server-distro',
extensionsGallery: this._webExtensionResourceUrlTemplate ? {

View file

@ -197,9 +197,9 @@ class ExtensionHostProxy implements IExtensionHostProxy {
resolveAuthority(remoteAuthority: string, resolveAttempt: number): Promise<IResolveAuthorityResult> {
return this._actual.$resolveAuthority(remoteAuthority, resolveAttempt);
}
async getCanonicalURI(remoteAuthority: string, uri: URI): Promise<URI> {
async getCanonicalURI(remoteAuthority: string, uri: URI): Promise<URI | null> {
const uriComponents = await this._actual.$getCanonicalURI(remoteAuthority, uri);
return URI.revive(uriComponents);
return (uriComponents ? URI.revive(uriComponents) : uriComponents);
}
startExtensionHost(enabledExtensionIds: ExtensionIdentifier[]): Promise<void> {
return this._actual.$startExtensionHost(enabledExtensionIds);

View file

@ -1339,7 +1339,10 @@ export interface ExtHostSearchShape {
export interface ExtHostExtensionServiceShape {
$resolveAuthority(remoteAuthority: string, resolveAttempt: number): Promise<IResolveAuthorityResult>;
$getCanonicalURI(remoteAuthority: string, uri: UriComponents): Promise<UriComponents>;
/**
* Returns `null` if no resolver for `remoteAuthority` is found.
*/
$getCanonicalURI(remoteAuthority: string, uri: UriComponents): Promise<UriComponents | null>;
$startExtensionHost(enabledExtensionIds: ExtensionIdentifier[]): Promise<void>;
$extensionTestsExecute(): Promise<number>;
$extensionTestsExit(code: number): Promise<void>;

View file

@ -953,7 +953,7 @@ export class ExtHostVariableResolverService extends AbstractVariableResolverServ
if (activeEditor) {
return activeEditor.document.uri;
}
const activeTab = editorTabs.tabGroups.all.find(group => group.isActive)?.activeTab;
const activeTab = editorTabs.tabGroups.groups.find(group => group.isActive)?.activeTab;
if (activeTab !== undefined) {
// Resolve a resource from the tab
const asSideBySideResource = activeTab.resource as { primary?: URI; secondary?: URI } | undefined;

View file

@ -33,7 +33,7 @@ export interface IEditorTabGroup {
}
export interface IEditorTabGroups {
all: IEditorTabGroup[];
groups: IEditorTabGroup[];
activeTabGroup: IEditorTabGroup | undefined;
readonly onDidChangeTabGroup: Event<void>;
readonly onDidChangeActiveTabGroup: Event<IEditorTabGroup | undefined>;
@ -55,7 +55,7 @@ export class ExtHostEditorTabs implements IExtHostEditorTabs {
private readonly _onDidChangeActiveTabGroup = new Emitter<IEditorTabGroup | undefined>();
private _tabGroups: IEditorTabGroups = {
all: [],
groups: [],
activeTabGroup: undefined,
onDidChangeTabGroup: this._onDidChangeTabGroup.event,
onDidChangeActiveTabGroup: this._onDidChangeActiveTabGroup.event
@ -71,7 +71,7 @@ export class ExtHostEditorTabs implements IExtHostEditorTabs {
$acceptEditorTabModel(tabGroups: IEditorTabGroupDto[]): void {
// Clears the tab groups array
this._tabGroups.all.length = 0;
this._tabGroups.groups.length = 0;
let activeGroupFound = false;
for (const group of tabGroups) {
let activeTab: IEditorTab | undefined;
@ -82,7 +82,7 @@ export class ExtHostEditorTabs implements IExtHostEditorTabs {
}
return extHostTab;
});
this._tabGroups.all.push(Object.freeze({
this._tabGroups.groups.push(Object.freeze({
isActive: group.isActive,
viewColumn: typeConverters.ViewColumn.to(group.viewColumn),
activeTab,
@ -93,7 +93,7 @@ export class ExtHostEditorTabs implements IExtHostEditorTabs {
if (group.isActive) {
activeGroupFound = true;
const oldActiveTabGroup = this._tabGroups.activeTabGroup;
this._tabGroups.activeTabGroup = this._tabGroups.all[this._tabGroups.all.length - 1];
this._tabGroups.activeTabGroup = this._tabGroups.groups[this._tabGroups.groups.length - 1];
// Diff the old and current active group to decide if we should fire a change event
if (this.groupDiff(oldActiveTabGroup, this._tabGroups.activeTabGroup)) {
this._onDidChangeActiveTabGroup.fire(this._tabGroups.activeTabGroup);

View file

@ -753,12 +753,13 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
}
}
public async $getCanonicalURI(remoteAuthority: string, uriComponents: UriComponents): Promise<UriComponents> {
public async $getCanonicalURI(remoteAuthority: string, uriComponents: UriComponents): Promise<UriComponents | null> {
this._logService.info(`$getCanonicalURI invoked for authority (${getRemoteAuthorityPrefix(remoteAuthority)})`);
const { authorityPrefix, resolver } = await this._activateAndGetResolver(remoteAuthority);
const { resolver } = await this._activateAndGetResolver(remoteAuthority);
if (!resolver) {
throw new Error(`Cannot get canonical URI because no remote extension is installed to resolve ${authorityPrefix}`);
// Return `null` if no resolver for `remoteAuthority` is found.
return null;
}
const uri = URI.revive(uriComponents);

View file

@ -21,7 +21,7 @@ suite('ExtHostEditorTabs', function () {
})
);
assert.strictEqual(extHostEditorTabs.tabGroups.all.length, 0);
assert.strictEqual(extHostEditorTabs.tabGroups.groups.length, 0);
assert.strictEqual(extHostEditorTabs.tabGroups.activeTabGroup, undefined);
});
@ -50,8 +50,8 @@ suite('ExtHostEditorTabs', function () {
tabs: [tab],
activeTab: { ...tab }
}]);
assert.strictEqual(extHostEditorTabs.tabGroups.all.length, 1);
const [first] = extHostEditorTabs.tabGroups.all;
assert.strictEqual(extHostEditorTabs.tabGroups.groups.length, 1);
const [first] = extHostEditorTabs.tabGroups.groups;
assert.ok(first.activeTab);
assert.strictEqual(first.tabs.indexOf(first.activeTab), 0);
@ -63,8 +63,8 @@ suite('ExtHostEditorTabs', function () {
tabs: [tab],
activeTab: undefined! // TODO@lramos15 unused
}]);
assert.strictEqual(extHostEditorTabs.tabGroups.all.length, 1);
const [first] = extHostEditorTabs.tabGroups.all;
assert.strictEqual(extHostEditorTabs.tabGroups.groups.length, 1);
const [first] = extHostEditorTabs.tabGroups.groups;
assert.ok(first.activeTab);
assert.strictEqual(first.tabs.indexOf(first.activeTab), 0);
}
@ -84,8 +84,8 @@ suite('ExtHostEditorTabs', function () {
tabs: [],
activeTab: undefined
}]);
assert.strictEqual(extHostEditorTabs.tabGroups.all.length, 1);
const [first] = extHostEditorTabs.tabGroups.all;
assert.strictEqual(extHostEditorTabs.tabGroups.groups.length, 1);
const [first] = extHostEditorTabs.tabGroups.groups;
assert.strictEqual(first.activeTab, undefined);
assert.strictEqual(first.tabs.length, 0);
});
@ -101,7 +101,7 @@ suite('ExtHostEditorTabs', function () {
extHostEditorTabs.tabGroups.onDidChangeTabGroup(() => count++);
assert.strictEqual(extHostEditorTabs.tabGroups.all.length, 0);
assert.strictEqual(extHostEditorTabs.tabGroups.groups.length, 0);
assert.strictEqual(extHostEditorTabs.tabGroups.activeTabGroup, undefined);
assert.strictEqual(count, 0);
extHostEditorTabs.$acceptEditorTabModel([{
@ -113,7 +113,7 @@ suite('ExtHostEditorTabs', function () {
}]);
assert.ok(extHostEditorTabs.tabGroups.activeTabGroup);
const activeTabGroup: IEditorTabGroup = extHostEditorTabs.tabGroups.activeTabGroup;
assert.strictEqual(extHostEditorTabs.tabGroups.all.length, 1);
assert.strictEqual(extHostEditorTabs.tabGroups.groups.length, 1);
assert.strictEqual(activeTabGroup.tabs.length, 0);
assert.strictEqual(count, 1);
});
@ -143,8 +143,8 @@ suite('ExtHostEditorTabs', function () {
tabs: [tab],
activeTab: { ...tab }
}]);
assert.strictEqual(extHostEditorTabs.tabGroups.all.length, 1);
const [first] = extHostEditorTabs.tabGroups.all;
assert.strictEqual(extHostEditorTabs.tabGroups.groups.length, 1);
const [first] = extHostEditorTabs.tabGroups.groups;
assert.ok(first.activeTab);
assert.strictEqual(first.tabs.indexOf(first.activeTab), 0);
assert.strictEqual(first.activeTab, first.tabs[0]);
@ -166,7 +166,7 @@ suite('ExtHostEditorTabs', function () {
});
assert.strictEqual(extHostEditorTabs.tabGroups.all.length, 0);
assert.strictEqual(extHostEditorTabs.tabGroups.groups.length, 0);
assert.strictEqual(extHostEditorTabs.tabGroups.activeTabGroup, undefined);
assert.strictEqual(count, 0);
const tabModel = [{
@ -240,7 +240,7 @@ suite('ExtHostEditorTabs', function () {
tabs: [tabDto],
activeTab: undefined // NOT needed
}]);
let all = extHostEditorTabs.tabGroups.all.map(group => group.tabs).flat();
let all = extHostEditorTabs.tabGroups.groups.map(group => group.tabs).flat();
assert.strictEqual(all.length, 1);
const apiTab1 = all[0];
assert.strictEqual(apiTab1.resource?.toString(), URI.revive(tabDto.resource)?.toString());
@ -258,7 +258,7 @@ suite('ExtHostEditorTabs', function () {
activeTab: undefined // NOT needed
}]);
all = extHostEditorTabs.tabGroups.all.map(group => group.tabs).flat();
all = extHostEditorTabs.tabGroups.groups.map(group => group.tabs).flat();
assert.strictEqual(all.length, 1);
const apiTab2 = all[0];
assert.strictEqual(apiTab2.resource?.toString(), URI.revive(tabDto.resource)?.toString());

View file

@ -6,7 +6,7 @@
import * as assert from 'assert';
import { timeout } from 'vs/base/common/async';
import { URI } from 'vs/base/common/uri';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { ExtensionIdentifier, IExtensionDescription, TargetPlatform } from 'vs/platform/extensions/common/extensions';
import { NullLogService } from 'vs/platform/log/common/log';
import { ActivatedExtension, EmptyExtension, ExtensionActivationTimes, ExtensionsActivator, IExtensionsActivatorHost } from 'vs/workbench/api/common/extHostExtensionActivator';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
@ -261,6 +261,7 @@ suite('ExtensionsActivator', () => {
isUserBuiltin: false,
activationEvents,
main: 'index.js',
targetPlatform: TargetPlatform.UNDEFINED,
extensionDependencies: deps.map(d => d.value)
};
}

View file

@ -223,7 +223,7 @@ MenuRegistry.appendMenuItem(MenuId.LayoutControlMenu, {
title: localize('configureLayout', "Configure Layout"),
icon: configureLayoutIcon,
group: '1_workbench_layout',
when: ContextKeyExpr.or(ContextKeyExpr.equals('config.workbench.experimental.layoutControl.type', 'menu'), ContextKeyExpr.equals('config.workbench.experimental.layoutControl.type', 'both'))
when: ContextKeyExpr.equals('config.workbench.experimental.layoutControl.type', 'menu')
});
@ -1134,8 +1134,8 @@ if (!isMacintosh || !isNative) {
ToggleVisibilityActions.push(...[
CreateToggleLayoutItem(ToggleActivityBarVisibilityAction.ID, ContextKeyExpr.equals('config.workbench.activityBar.visible', true), localize('activityBar', "Activity Bar"), { whenA: ContextKeyExpr.equals('config.workbench.sideBar.location', 'left'), iconA: activityBarLeftIcon, iconB: activityBarRightIcon }),
CreateToggleLayoutItem(ToggleSidebarVisibilityAction.ID, SideBarVisibleContext, localize('sideBar', "Primary Side Bar"), { whenA: ContextKeyExpr.equals('config.workbench.sideBar.location', 'left'), iconA: panelLeftIcon, iconB: panelRightIcon }),
CreateToggleLayoutItem(TogglePanelAction.ID, PanelVisibleContext, localize('panel', "Panel"), panelIcon),
CreateToggleLayoutItem(ToggleAuxiliaryBarAction.ID, AuxiliaryBarVisibleContext, localize('secondarySideBar', "Secondary Side Bar"), { whenA: ContextKeyExpr.equals('config.workbench.sideBar.location', 'left'), iconA: panelRightIcon, iconB: panelLeftIcon }),
CreateToggleLayoutItem(TogglePanelAction.ID, PanelVisibleContext, localize('panel', "Panel"), panelIcon),
CreateToggleLayoutItem(ToggleStatusbarVisibilityAction.ID, ContextKeyExpr.equals('config.workbench.statusBar.visible', true), localize('statusBar', "Status Bar"), statusBarIcon),
]);
@ -1170,10 +1170,16 @@ registerAction2(class CustomizeLayoutAction extends Action2 {
id: 'workbench.action.customizeLayout',
title: localize('customizeLayout', "Customize Layout..."),
f1: true,
icon: configureLayoutIcon,
menu: [
{
id: MenuId.LayoutControlMenuSubmenu,
group: 'z_end',
},
{
id: MenuId.LayoutControlMenu,
when: ContextKeyExpr.equals('config.workbench.experimental.layoutControl.type', 'both'),
group: 'z_end'
}
]
});

View file

@ -77,7 +77,7 @@ MenuRegistry.appendMenuItems([
title: localize('miShowAuxiliaryBarNoMnemonic', "Show Secondary Side Bar"),
toggled: AuxiliaryBarVisibleContext
},
order: 4
order: 2
}
},
{

View file

@ -170,6 +170,11 @@ export interface IWorkbenchConstructionOptions {
*/
readonly additionalTrustedDomains?: string[];
/**
* Enable workspace trust feature for the current window
*/
readonly enableWorkspaceTrust?: boolean;
/**
* Urls that will be opened externally that are allowed access
* to the opener window. This is primarily used to allow

View file

@ -398,7 +398,7 @@ const registry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Con
localize('layoutcontrol.type.both', "Shows both the dropdown and toggle buttons."),
],
'tags': ['experimental'],
'default': 'menu',
'default': 'both',
'description': localize('layoutControlType', "Controls whether the layout control in the custom title bar is displayed as a single menu button or with multiple UI toggles."),
},
}

View file

@ -346,7 +346,7 @@ export interface IViewContainerModel {
readonly onDidMoveVisibleViewDescriptors: Event<{ from: IViewDescriptorRef; to: IViewDescriptorRef }>;
isVisible(id: string): boolean;
setVisible(id: string, visible: boolean, size?: number): void;
setVisible(id: string, visible: boolean): void;
isCollapsed(id: string): boolean;
setCollapsed(id: string, collapsed: boolean): void;

View file

@ -249,7 +249,7 @@ export class BulkTextEdits {
// This edit touches a single model => keep things simple
const task = tasks[0];
if (!task.isNoOp()) {
const singleModelEditStackElement = new SingleModelEditStackElement(task.model, task.getBeforeCursorState());
const singleModelEditStackElement = new SingleModelEditStackElement(this._label, task.model, task.getBeforeCursorState());
this._undoRedoService.pushElement(singleModelEditStackElement, this._undoRedoGroup, this._undoRedoSource);
task.apply();
singleModelEditStackElement.close();
@ -259,7 +259,7 @@ export class BulkTextEdits {
// prepare multi model undo element
const multiModelEditStackElement = new MultiModelEditStackElement(
this._label,
tasks.map(t => new SingleModelEditStackElement(t.model, t.getBeforeCursorState()))
tasks.map(t => new SingleModelEditStackElement(this._label, t.model, t.getBeforeCursorState()))
);
this._undoRedoService.pushElement(multiModelEditStackElement, this._undoRedoGroup, this._undoRedoSource);
for (const task of tasks) {

View file

@ -6,7 +6,7 @@
import { URI } from 'vs/base/common/uri';
import { IRange } from 'vs/editor/common/core/range';
import { Comment, CommentThread, CommentThreadChangedEvent } from 'vs/editor/common/languages';
import { groupBy, flatten } from 'vs/base/common/arrays';
import { groupBy } from 'vs/base/common/arrays';
import { localize } from 'vs/nls';
export interface ICommentThreadChangedEvent extends CommentThreadChangedEvent {
@ -71,9 +71,16 @@ export class CommentsModel {
this.commentThreadsMap = new Map<string, ResourceWithCommentThreads[]>();
}
private updateResourceCommentThreads() {
this.resourceCommentThreads = [...this.commentThreadsMap.values()].flat();
this.resourceCommentThreads.sort((a, b) => {
return a.resource.toString() > b.resource.toString() ? 1 : -1;
});
}
public setCommentThreads(owner: string, commentThreads: CommentThread[]): void {
this.commentThreadsMap.set(owner, this.groupByResource(owner, commentThreads));
this.resourceCommentThreads = flatten([...this.commentThreadsMap.values()]);
this.updateResourceCommentThreads();
}
public updateCommentThreads(event: ICommentThreadChangedEvent): boolean {
@ -123,7 +130,7 @@ export class CommentsModel {
});
this.commentThreadsMap.set(owner, threadsForOwner);
this.resourceCommentThreads = flatten([...this.commentThreadsMap.values()]);
this.updateResourceCommentThreads();
return removed.length > 0 || changed.length > 0 || added.length > 0;
}

View file

@ -12,7 +12,7 @@ import { TestConfigurationService } from 'vs/platform/configuration/test/common/
import { URI } from 'vs/base/common/uri';
import { ExecutableDebugAdapter } from 'vs/workbench/contrib/debug/node/debugAdapter';
import { TestTextResourcePropertiesService } from 'vs/editor/test/common/services/testTextResourcePropertiesService';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { ExtensionIdentifier, IExtensionDescription, TargetPlatform } from 'vs/platform/extensions/common/extensions';
suite('Debug - Debugger', () => {
@ -58,6 +58,7 @@ suite('Debug - Debugger', () => {
isUserBuiltin: false,
isUnderDevelopment: false,
engines: null!,
targetPlatform: TargetPlatform.UNDEFINED,
contributes: {
'debuggers': [
debuggerContribution
@ -76,6 +77,7 @@ suite('Debug - Debugger', () => {
isUserBuiltin: false,
isUnderDevelopment: false,
engines: null!,
targetPlatform: TargetPlatform.UNDEFINED,
contributes: {
'debuggers': [
{
@ -100,6 +102,7 @@ suite('Debug - Debugger', () => {
isUserBuiltin: false,
isUnderDevelopment: false,
engines: null!,
targetPlatform: TargetPlatform.UNDEFINED,
contributes: {
'debuggers': [
{

View file

@ -11,7 +11,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { IExtensionsWorkbenchService, IExtension } from 'vs/workbench/contrib/extensions/common/extensions';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IExtensionService, IExtensionsStatus, IExtensionHostProfile, ExtensionRunningLocation } from 'vs/workbench/services/extensions/common/extensions';
import { IExtensionService, IExtensionsStatus, IExtensionHostProfile, LocalWebWorkerRunningLocation } from 'vs/workbench/services/extensions/common/extensions';
import { IListVirtualDelegate, IListRenderer } from 'vs/base/browser/ui/list/list';
import { WorkbenchList } from 'vs/platform/list/browser/listService';
import { append, $, Dimension, clearNode, addDisposableListener } from 'vs/base/browser/dom';
@ -36,6 +36,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic
import { RuntimeExtensionsInput } from 'vs/workbench/contrib/extensions/common/runtimeExtensionsInput';
import { Action2, MenuId } from 'vs/platform/actions/common/actions';
import { CATEGORIES } from 'vs/workbench/common/actions';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
interface IExtensionProfileInformation {
/**
@ -80,6 +81,7 @@ export abstract class AbstractRuntimeExtensionsEditor extends EditorPane {
@IStorageService storageService: IStorageService,
@ILabelService private readonly _labelService: ILabelService,
@IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService,
@IClipboardService private readonly _clipboardService: IClipboardService,
) {
super(AbstractRuntimeExtensionsEditor.ID, telemetryService, themeService, storageService);
@ -370,7 +372,7 @@ export abstract class AbstractRuntimeExtensionsEditor extends EditorPane {
}
let extraLabel: string | null = null;
if (element.status.runningLocation === ExtensionRunningLocation.LocalWebWorker) {
if (element.status.runningLocation && element.status.runningLocation.equals(new LocalWebWorkerRunningLocation())) {
extraLabel = `$(globe) web worker`;
} else if (element.description.extensionLocation.scheme === Schemas.vscodeRemote) {
const hostLabel = this._labelService.getHostLabel(Schemas.vscodeRemote, this._environmentService.remoteAuthority);
@ -379,6 +381,8 @@ export abstract class AbstractRuntimeExtensionsEditor extends EditorPane {
} else {
extraLabel = `$(remote) ${element.description.extensionLocation.authority}`;
}
} else if (element.status.runningLocation && element.status.runningLocation.affinity > 0) {
extraLabel = `$(server-process) local process ${element.status.runningLocation.affinity + 1}`;
}
if (extraLabel) {
@ -427,11 +431,21 @@ export abstract class AbstractRuntimeExtensionsEditor extends EditorPane {
const actions: IAction[] = [];
actions.push(new Action(
'runtimeExtensionsEditor.action.copyId',
nls.localize('copy id', "Copy id ({0})", e.element!.description.identifier.value),
undefined,
true,
() => {
this._clipboardService.writeText(e.element!.description.identifier.value);
}
));
const reportExtensionIssueAction = this._createReportExtensionIssueAction(e.element);
if (reportExtensionIssueAction) {
actions.push(reportExtensionIssueAction);
actions.push(new Separator());
}
actions.push(new Separator());
if (e.element!.marketplaceInfo) {
actions.push(new Action('runtimeExtensionsEditor.action.disableWorkspace', nls.localize('disable workspace', "Disable (Workspace)"), undefined, true, () => this._extensionsWorkbenchService.setEnablement(e.element!.marketplaceInfo!, EnablementState.DisabledWorkspace)));

View file

@ -4,38 +4,12 @@
*--------------------------------------------------------------------------------------------*/
import { Action } from 'vs/base/common/actions';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IExtensionService, IExtensionHostProfile } from 'vs/workbench/services/extensions/common/extensions';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { ILabelService } from 'vs/platform/label/common/label';
import { IExtensionHostProfile } from 'vs/workbench/services/extensions/common/extensions';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { AbstractRuntimeExtensionsEditor, IRuntimeExtension } from 'vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor';
export class RuntimeExtensionsEditor extends AbstractRuntimeExtensionsEditor {
constructor(
@ITelemetryService telemetryService: ITelemetryService,
@IThemeService themeService: IThemeService,
@IContextKeyService contextKeyService: IContextKeyService,
@IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService,
@IExtensionService extensionService: IExtensionService,
@INotificationService notificationService: INotificationService,
@IContextMenuService contextMenuService: IContextMenuService,
@IInstantiationService instantiationService: IInstantiationService,
@IStorageService storageService: IStorageService,
@ILabelService labelService: ILabelService,
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,
) {
super(telemetryService, themeService, contextKeyService, extensionsWorkbenchService, extensionService, notificationService, contextMenuService, instantiationService, storageService, labelService, environmentService);
}
protected _getProfileInfo(): IExtensionHostProfile | null {
return null;
}

View file

@ -15,7 +15,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import {
IExtensionGalleryService, ILocalExtension, IGalleryExtension, IQueryOptions,
InstallExtensionEvent, DidUninstallExtensionEvent, IExtensionIdentifier, InstallOperation, InstallOptions, WEB_EXTENSION_TAG, InstallExtensionResult,
IExtensionsControlManifest, InstallVSIXOptions, IExtensionInfo, IExtensionQueryOptions, TargetPlatform
IExtensionsControlManifest, InstallVSIXOptions, IExtensionInfo, IExtensionQueryOptions
} from 'vs/platform/extensionManagement/common/extensionManagement';
import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer, IWorkbenchExtensionManagementService, DefaultIconPath } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData, areSameExtensions, groupByExtension, ExtensionKey, getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
@ -34,7 +34,7 @@ import * as resources from 'vs/base/common/resources';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { IFileService } from 'vs/platform/files/common/files';
import { IExtensionManifest, ExtensionType, IExtension as IPlatformExtension } from 'vs/platform/extensions/common/extensions';
import { IExtensionManifest, ExtensionType, IExtension as IPlatformExtension, TargetPlatform } from 'vs/platform/extensions/common/extensions';
import { ILanguageService } from 'vs/editor/common/languages/language';
import { IProductService } from 'vs/platform/product/common/productService';
import { FileAccess } from 'vs/base/common/network';
@ -221,7 +221,11 @@ export class Extension implements IExtension {
}
get outdatedTargetPlatform(): boolean {
return !!this.local && !!this.gallery && this.local.targetPlatform !== TargetPlatform.UNDEFINED && this.local.targetPlatform !== this.gallery.properties.targetPlatform && semver.eq(this.latestVersion, this.version);
return !!this.local && !!this.gallery
&& ![TargetPlatform.UNDEFINED, TargetPlatform.WEB].includes(this.local.targetPlatform)
&& this.gallery.properties.targetPlatform !== TargetPlatform.WEB
&& this.local.targetPlatform !== this.gallery.properties.targetPlatform
&& semver.eq(this.latestVersion, this.version);
}
get telemetryData(): any {

View file

@ -6,7 +6,7 @@
import * as nls from 'vs/nls';
import { IProductService } from 'vs/platform/product/common/productService';
import { Action } from 'vs/base/common/actions';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { ExtensionHostKind, IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { INativeHostService } from 'vs/platform/native/electron-sandbox/native';
import { IDebugService } from 'vs/workbench/contrib/debug/common/debug';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
@ -29,8 +29,8 @@ export class DebugExtensionHostAction extends Action {
override async run(): Promise<any> {
const inspectPort = await this._extensionService.getInspectPort(false);
if (!inspectPort) {
const inspectPorts = await this._extensionService.getInspectPorts(ExtensionHostKind.LocalProcess, false);
if (inspectPorts.length === 0) {
const res = await this._dialogService.confirm({
type: 'info',
message: nls.localize('restart1', "Profile Extensions"),
@ -45,11 +45,16 @@ export class DebugExtensionHostAction extends Action {
return;
}
if (inspectPorts.length > 1) {
// TODO
console.warn(`There are multiple extension hosts available for debugging. Picking the first one...`);
}
return this._debugService.startDebugging(undefined, {
type: 'node',
name: nls.localize('debugExtensionHost.launch.name', "Attach Extension Host"),
request: 'attach',
port: inspectPort
port: inspectPorts[0]
});
}
}

View file

@ -6,7 +6,7 @@
import * as nls from 'vs/nls';
import { Event, Emitter } from 'vs/base/common/event';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IExtensionHostProfile, ProfileSession, IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IExtensionHostProfile, ProfileSession, IExtensionService, ExtensionHostKind } from 'vs/workbench/services/extensions/common/extensions';
import { Disposable, toDisposable, MutableDisposable } from 'vs/base/common/lifecycle';
import { onUnexpectedError } from 'vs/base/common/errors';
import { StatusbarAlignment, IStatusbarService, IStatusbarEntryAccessor, IStatusbarEntry } from 'vs/workbench/services/statusbar/browser/statusbar';
@ -116,8 +116,9 @@ export class ExtensionHostProfileService extends Disposable implements IExtensio
return null;
}
const inspectPort = await this._extensionService.getInspectPort(true);
if (!inspectPort) {
const inspectPorts = await this._extensionService.getInspectPorts(ExtensionHostKind.LocalProcess, true);
if (inspectPorts.length === 0) {
return this._dialogService.confirm({
type: 'info',
message: nls.localize('restart1', "Profile Extensions"),
@ -131,9 +132,14 @@ export class ExtensionHostProfileService extends Disposable implements IExtensio
});
}
if (inspectPorts.length > 1) {
// TODO
console.warn(`There are multiple extension hosts available for profiling. Picking the first one...`);
}
this._setState(ProfileSessionState.Starting);
return this._instantiationService.createInstance(ExtensionHostProfiler, inspectPort).start().then((value) => {
return this._instantiationService.createInstance(ExtensionHostProfiler, inspectPorts[0]).start().then((value) => {
this._profileSession = value;
this._setState(ProfileSessionState.Running);
}, (err) => {

View file

@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IExtensionService, IResponsiveStateChangeEvent, IExtensionHostProfile, ProfileSession } from 'vs/workbench/services/extensions/common/extensions';
import { IExtensionService, IResponsiveStateChangeEvent, IExtensionHostProfile, ProfileSession, ExtensionHostKind } from 'vs/workbench/services/extensions/common/extensions';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { Disposable } from 'vs/base/common/lifecycle';
import { ILogService } from 'vs/platform/log/common/log';
@ -46,8 +46,11 @@ export class ExtensionsAutoProfiler extends Disposable implements IWorkbenchCont
}
private async _onDidChangeResponsiveChange(event: IResponsiveStateChangeEvent): Promise<void> {
if (event.extensionHostKind !== ExtensionHostKind.LocalProcess) {
return;
}
const port = await this._extensionService.getInspectPort(true);
const port = await this._extensionService.getInspectPort(event.extensionHostId, true);
if (!port) {
return;

View file

@ -26,6 +26,7 @@ import { URI } from 'vs/base/common/uri';
import { IFileService } from 'vs/platform/files/common/files';
import { INativeHostService } from 'vs/platform/native/electron-sandbox/native';
import { IV8Profile, Utils } from 'vs/platform/profiling/common/profiling';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
export const IExtensionHostProfileService = createDecorator<IExtensionHostProfileService>('extensionHostProfileService');
export const CONTEXT_PROFILE_SESSION_STATE = new RawContextKey<string>('profileSessionState', 'none');
@ -72,9 +73,10 @@ export class RuntimeExtensionsEditor extends AbstractRuntimeExtensionsEditor {
@IStorageService storageService: IStorageService,
@ILabelService labelService: ILabelService,
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,
@IClipboardService clipboardService: IClipboardService,
@IExtensionHostProfileService private readonly _extensionHostProfileService: IExtensionHostProfileService,
) {
super(telemetryService, themeService, contextKeyService, extensionsWorkbenchService, extensionService, notificationService, contextMenuService, instantiationService, storageService, labelService, environmentService);
super(telemetryService, themeService, contextKeyService, extensionsWorkbenchService, extensionService, notificationService, contextMenuService, instantiationService, storageService, labelService, environmentService, clipboardService);
this._profileInfo = this._extensionHostProfileService.lastProfile;
this._extensionsHostRecorded = CONTEXT_EXTENSION_HOST_PROFILE_RECORDED.bindTo(contextKeyService);
this._profileSessionState = CONTEXT_PROFILE_SESSION_STATE.bindTo(contextKeyService);

View file

@ -6,8 +6,8 @@
import * as assert from 'assert';
import { ExtensionState } from 'vs/workbench/contrib/extensions/common/extensions';
import { Extension } from 'vs/workbench/contrib/extensions/browser/extensionsWorkbenchService';
import { IGalleryExtension, IGalleryExtensionProperties, ILocalExtension, TargetPlatform } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ExtensionType, IExtensionManifest } from 'vs/platform/extensions/common/extensions';
import { IGalleryExtension, IGalleryExtensionProperties, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ExtensionType, IExtensionManifest, TargetPlatform } from 'vs/platform/extensions/common/extensions';
import { URI } from 'vs/base/common/uri';
import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { generateUuid } from 'vs/base/common/uuid';
@ -56,6 +56,16 @@ suite('Extension Test', () => {
assert.strictEqual(extension.outdated, true);
});
test('extension is not outdated when local and gallery are on same version and local is on web', () => {
const extension = instantiationService.createInstance(Extension, () => ExtensionState.Installed, undefined, aLocalExtension('somext', {}, { targetPlatform: TargetPlatform.WEB }), aGalleryExtension('somext'));
assert.strictEqual(extension.outdated, false);
});
test('extension is not outdated when local and gallery are on same version and gallery is on web', () => {
const extension = instantiationService.createInstance(Extension, () => ExtensionState.Installed, undefined, aLocalExtension('somext'), aGalleryExtension('somext', {}, { targetPlatform: TargetPlatform.WEB }));
assert.strictEqual(extension.outdated, false);
});
test('extension is not outdated when local is not pre-release but gallery is pre-release', () => {
const extension = instantiationService.createInstance(Extension, () => ExtensionState.Installed, undefined, aLocalExtension('somext', { version: '1.0.0' }), aGalleryExtension('somext', { version: '1.0.1' }, { isPreReleaseVersion: true }));
assert.strictEqual(extension.outdated, false);

View file

@ -3,11 +3,12 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { hookDomPurifyHrefAndSrcSanitizer } from 'vs/base/browser/dom';
import * as dompurify from 'vs/base/browser/dompurify/dompurify';
import { marked } from 'vs/base/common/marked/marked';
import { Schemas } from 'vs/base/common/network';
import { tokenizeToString } from 'vs/editor/common/languages/textToHtmlTokenizer';
import { ILanguageService } from 'vs/editor/common/languages/language';
import { tokenizeToString } from 'vs/editor/common/languages/textToHtmlTokenizer';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
export const DEFAULT_MARKDOWN_STYLES = `
@ -152,21 +153,7 @@ code > div {
const allowedProtocols = [Schemas.http, Schemas.https, Schemas.command];
function sanitize(documentContent: string, allowUnknownProtocols: boolean): string {
// https://github.com/cure53/DOMPurify/blob/main/demos/hooks-scheme-allowlist.html
dompurify.addHook('afterSanitizeAttributes', (node) => {
// build an anchor to map URLs to
const anchor = document.createElement('a');
// check all href/src attributes for validity
for (const attr of ['href', 'src']) {
if (node.hasAttribute(attr)) {
anchor.href = node.getAttribute(attr) as string;
if (!allowedProtocols.includes(anchor.protocol.replace(/:$/, ''))) {
node.removeAttribute(attr);
}
}
}
});
const hook = hookDomPurifyHrefAndSrcSanitizer(allowedProtocols, true);
try {
return dompurify.sanitize(documentContent, {
@ -186,7 +173,7 @@ function sanitize(documentContent: string, allowUnknownProtocols: boolean): stri
...(allowUnknownProtocols ? { ALLOW_UNKNOWN_PROTOCOLS: true } : {}),
});
} finally {
dompurify.removeHook('afterSanitizeAttributes');
hook.dispose();
}
}

View file

@ -531,7 +531,7 @@
flex-direction: row-reverse;
}
.monaco-workbench .notebookOverlay .monaco-list:focus-within .monaco-list-row.focused .codicon:not(.suggest-icon) {
.monaco-workbench .notebookOverlay .monaco-list:focus-within .monaco-list-row .codicon:not(.suggest-icon) {
color: inherit;
}

View file

@ -44,11 +44,11 @@ export class CellDragAndDropController extends Disposable {
constructor(
private readonly notebookEditor: INotebookEditorDelegate,
insertionIndicatorContainer: HTMLElement
private readonly notebookListContainer: HTMLElement
) {
super();
this.listInsertionIndicator = DOM.append(insertionIndicatorContainer, $('.cell-list-insertion-indicator'));
this.listInsertionIndicator = DOM.append(notebookListContainer, $('.cell-list-insertion-indicator'));
this._register(DOM.addDisposableListener(document.body, DOM.EventType.DRAG_START, this.onGlobalDragStart.bind(this), true));
this._register(DOM.addDisposableListener(document.body, DOM.EventType.DRAG_END, this.onGlobalDragEnd.bind(this), true));
@ -110,7 +110,7 @@ export class CellDragAndDropController extends Disposable {
}
private toCellDragEvent(event: DragEvent): CellDragEvent | undefined {
const targetTop = this.notebookEditor.getDomNode().getBoundingClientRect().top;
const targetTop = this.notebookListContainer.getBoundingClientRect().top;
const dragOffset = this.list.scrollTop + event.clientY - targetTop;
const draggedOverCell = this.list.elementAt(dragOffset);
if (!draggedOverCell) {

View file

@ -32,6 +32,7 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace
import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust';
import { asWebviewUri, webviewGenericCspSource } from 'vs/workbench/common/webview';
import { CellEditState, ICellOutputViewModel, ICellViewModel, ICommonCellInfo, IDisplayOutputLayoutUpdateRequest, IDisplayOutputViewModel, IFocusNotebookCellOptions, IGenericCellViewModel, IInsetRenderOutput, INotebookEditorCreationOptions, INotebookWebviewMessage, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { NOTEBOOK_WEBVIEW_BOUNDARY } from 'vs/workbench/contrib/notebook/browser/view/notebookCellList';
import { preloadsScriptStr, RendererMetadata } from 'vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads';
import { transformWebviewThemeVars } from 'vs/workbench/contrib/notebook/browser/view/renderers/webviewThemeMapping';
import { MarkupCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel';
@ -563,273 +564,250 @@ var requirejs = (function() {
}
switch (data.type) {
case 'initialized':
case 'initialized': {
this.initializeWebViewState();
break;
case 'dimension':
{
for (const update of data.updates) {
const height = update.height;
if (update.isOutput) {
const resolvedResult = this.resolveOutputId(update.id);
if (resolvedResult) {
const { cellInfo, output } = resolvedResult;
this.notebookEditor.updateOutputHeight(cellInfo, output, height, !!update.init, 'webview#dimension');
this.notebookEditor.scheduleOutputHeightAck(cellInfo, update.id, height);
}
} else {
this.notebookEditor.updateMarkupCellHeight(update.id, height, !!update.init);
}
case 'dimension': {
for (const update of data.updates) {
const height = update.height;
if (update.isOutput) {
const resolvedResult = this.resolveOutputId(update.id);
if (resolvedResult) {
const { cellInfo, output } = resolvedResult;
this.notebookEditor.updateOutputHeight(cellInfo, output, height, !!update.init, 'webview#dimension');
this.notebookEditor.scheduleOutputHeightAck(cellInfo, update.id, height);
}
} else {
this.notebookEditor.updateMarkupCellHeight(update.id, height, !!update.init);
}
break;
}
case 'mouseenter':
{
const resolvedResult = this.resolveOutputId(data.id);
if (resolvedResult) {
const latestCell = this.notebookEditor.getCellByInfo(resolvedResult.cellInfo);
if (latestCell) {
latestCell.outputIsHovered = true;
}
break;
}
case 'mouseenter': {
const resolvedResult = this.resolveOutputId(data.id);
if (resolvedResult) {
const latestCell = this.notebookEditor.getCellByInfo(resolvedResult.cellInfo);
if (latestCell) {
latestCell.outputIsHovered = true;
}
break;
}
case 'mouseleave':
{
const resolvedResult = this.resolveOutputId(data.id);
if (resolvedResult) {
const latestCell = this.notebookEditor.getCellByInfo(resolvedResult.cellInfo);
if (latestCell) {
latestCell.outputIsHovered = false;
}
break;
}
case 'mouseleave': {
const resolvedResult = this.resolveOutputId(data.id);
if (resolvedResult) {
const latestCell = this.notebookEditor.getCellByInfo(resolvedResult.cellInfo);
if (latestCell) {
latestCell.outputIsHovered = false;
}
break;
}
case 'outputFocus':
{
const resolvedResult = this.resolveOutputId(data.id);
if (resolvedResult) {
const latestCell = this.notebookEditor.getCellByInfo(resolvedResult.cellInfo);
if (latestCell) {
latestCell.outputIsFocused = true;
}
break;
}
case 'outputFocus': {
const resolvedResult = this.resolveOutputId(data.id);
if (resolvedResult) {
const latestCell = this.notebookEditor.getCellByInfo(resolvedResult.cellInfo);
if (latestCell) {
latestCell.outputIsFocused = true;
}
break;
}
case 'outputBlur':
{
const resolvedResult = this.resolveOutputId(data.id);
if (resolvedResult) {
const latestCell = this.notebookEditor.getCellByInfo(resolvedResult.cellInfo);
if (latestCell) {
latestCell.outputIsFocused = false;
}
break;
}
case 'outputBlur': {
const resolvedResult = this.resolveOutputId(data.id);
if (resolvedResult) {
const latestCell = this.notebookEditor.getCellByInfo(resolvedResult.cellInfo);
if (latestCell) {
latestCell.outputIsFocused = false;
}
break;
}
case 'scroll-ack':
{
// const date = new Date();
// const top = data.data.top;
// console.log('ack top ', top, ' version: ', data.version, ' - ', date.getMinutes() + ':' + date.getSeconds() + ':' + date.getMilliseconds());
break;
}
case 'scroll-to-reveal':
{
this.notebookEditor.setScrollTop(data.scrollTop);
break;
}
case 'did-scroll-wheel':
{
this.notebookEditor.triggerScroll({
...data.payload,
preventDefault: () => { },
stopPropagation: () => { }
});
break;
}
case 'focus-editor':
{
const cell = this.notebookEditor.getCellById(data.cellId);
if (cell) {
if (data.focusNext) {
this.notebookEditor.focusNextNotebookCell(cell, 'editor');
} else {
this.notebookEditor.focusNotebookCell(cell, 'editor');
}
break;
}
case 'scroll-ack': {
// const date = new Date();
// const top = data.data.top;
// console.log('ack top ', top, ' version: ', data.version, ' - ', date.getMinutes() + ':' + date.getSeconds() + ':' + date.getMilliseconds());
break;
}
case 'scroll-to-reveal': {
this.notebookEditor.setScrollTop(data.scrollTop - NOTEBOOK_WEBVIEW_BOUNDARY);
break;
}
case 'did-scroll-wheel': {
this.notebookEditor.triggerScroll({
...data.payload,
preventDefault: () => { },
stopPropagation: () => { }
});
break;
}
case 'focus-editor': {
const cell = this.notebookEditor.getCellById(data.cellId);
if (cell) {
if (data.focusNext) {
this.notebookEditor.focusNextNotebookCell(cell, 'editor');
} else {
this.notebookEditor.focusNotebookCell(cell, 'editor');
}
break;
}
case 'clicked-data-url':
{
this._onDidClickDataLink(data);
break;
}
case 'clicked-link':
{
let linkToOpen: URI | string | undefined;
if (matchesScheme(data.href, Schemas.command)) {
const ret = /command\:workbench\.action\.openLargeOutput\?(.*)/.exec(data.href);
if (ret && ret.length === 2) {
const outputId = ret[1];
const group = this.editorGroupService.activeGroup;
break;
}
case 'clicked-data-url': {
this._onDidClickDataLink(data);
break;
}
case 'clicked-link': {
let linkToOpen: URI | string | undefined;
if (matchesScheme(data.href, Schemas.command)) {
const ret = /command\:workbench\.action\.openLargeOutput\?(.*)/.exec(data.href);
if (ret && ret.length === 2) {
const outputId = ret[1];
const group = this.editorGroupService.activeGroup;
if (group) {
if (group.activeEditor) {
group.pinEditor(group.activeEditor);
}
if (group) {
if (group.activeEditor) {
group.pinEditor(group.activeEditor);
}
}
this.openerService.open(CellUri.generateCellOutputUri(this.documentUri, outputId));
this.openerService.open(CellUri.generateCellOutputUri(this.documentUri, outputId));
return;
}
}
if (matchesSomeScheme(data.href, Schemas.http, Schemas.https, Schemas.mailto, Schemas.command, Schemas.vscodeNotebookCell, Schemas.vscodeNotebook)) {
linkToOpen = data.href;
} else if (!/^[\w\-]+:/.test(data.href)) {
if (this.documentUri.scheme === Schemas.untitled) {
const folders = this.workspaceContextService.getWorkspace().folders;
if (!folders.length) {
return;
}
}
if (matchesSomeScheme(data.href, Schemas.http, Schemas.https, Schemas.mailto, Schemas.command, Schemas.vscodeNotebookCell, Schemas.vscodeNotebook)) {
linkToOpen = data.href;
} else if (!/^[\w\-]+:/.test(data.href)) {
if (this.documentUri.scheme === Schemas.untitled) {
const folders = this.workspaceContextService.getWorkspace().folders;
if (!folders.length) {
return;
}
linkToOpen = URI.joinPath(folders[0].uri, data.href);
} else {
if (data.href.startsWith('/')) {
// Resolve relative to workspace
let folder = this.workspaceContextService.getWorkspaceFolder(this.documentUri);
if (!folder) {
const folders = this.workspaceContextService.getWorkspace().folders;
if (!folders.length) {
return;
}
folder = folders[0];
linkToOpen = URI.joinPath(folders[0].uri, data.href);
} else {
if (data.href.startsWith('/')) {
// Resolve relative to workspace
let folder = this.workspaceContextService.getWorkspaceFolder(this.documentUri);
if (!folder) {
const folders = this.workspaceContextService.getWorkspace().folders;
if (!folders.length) {
return;
}
linkToOpen = URI.joinPath(folder.uri, data.href);
} else {
// Resolve relative to notebook document
linkToOpen = URI.joinPath(dirname(this.documentUri), data.href);
folder = folders[0];
}
}
}
if (linkToOpen) {
this.openerService.open(linkToOpen, { fromUserGesture: true, allowCommands: true });
}
break;
}
case 'customKernelMessage':
{
this._onMessage.fire({ message: data.message });
break;
}
case 'customRendererMessage':
{
this.rendererMessaging?.postMessage(data.rendererId, data.message);
break;
}
case 'clickMarkupCell':
{
const cell = this.notebookEditor.getCellById(data.cellId);
if (cell) {
if (data.shiftKey || (isMacintosh ? data.metaKey : data.ctrlKey)) {
// Modify selection
this.notebookEditor.toggleNotebookCellSelection(cell, /* fromPrevious */ data.shiftKey);
linkToOpen = URI.joinPath(folder.uri, data.href);
} else {
// Normal click
this.notebookEditor.focusNotebookCell(cell, 'container', { skipReveal: true });
// Resolve relative to notebook document
linkToOpen = URI.joinPath(dirname(this.documentUri), data.href);
}
}
break;
}
case 'contextMenuMarkupCell':
{
const cell = this.notebookEditor.getCellById(data.cellId);
if (cell) {
// Focus the cell first
if (linkToOpen) {
this.openerService.open(linkToOpen, { fromUserGesture: true, allowCommands: true });
}
break;
}
case 'customKernelMessage': {
this._onMessage.fire({ message: data.message });
break;
}
case 'customRendererMessage': {
this.rendererMessaging?.postMessage(data.rendererId, data.message);
break;
}
case 'clickMarkupCell': {
const cell = this.notebookEditor.getCellById(data.cellId);
if (cell) {
if (data.shiftKey || (isMacintosh ? data.metaKey : data.ctrlKey)) {
// Modify selection
this.notebookEditor.toggleNotebookCellSelection(cell, /* fromPrevious */ data.shiftKey);
} else {
// Normal click
this.notebookEditor.focusNotebookCell(cell, 'container', { skipReveal: true });
}
}
break;
}
case 'contextMenuMarkupCell': {
const cell = this.notebookEditor.getCellById(data.cellId);
if (cell) {
// Focus the cell first
this.notebookEditor.focusNotebookCell(cell, 'container', { skipReveal: true });
// Then show the context menu
const webviewRect = this.element.getBoundingClientRect();
this.contextMenuService.showContextMenu({
getActions: () => {
const result: IAction[] = [];
const menu = this.menuService.createMenu(MenuId.NotebookCellTitle, this.contextKeyService);
createAndFillInContextMenuActions(menu, undefined, result);
menu.dispose();
return result;
},
getAnchor: () => ({
x: webviewRect.x + data.clientX,
y: webviewRect.y + data.clientY
})
});
}
break;
}
case 'toggleMarkupPreview':
{
const cell = this.notebookEditor.getCellById(data.cellId);
if (cell && !this.notebookEditor.creationOptions.isReadOnly) {
this.notebookEditor.setMarkupCellEditState(data.cellId, CellEditState.Editing);
this.notebookEditor.focusNotebookCell(cell, 'editor', { skipReveal: true });
}
break;
}
case 'mouseEnterMarkupCell':
{
const cell = this.notebookEditor.getCellById(data.cellId);
if (cell instanceof MarkupCellViewModel) {
cell.cellIsHovered = true;
}
break;
}
case 'mouseLeaveMarkupCell':
{
const cell = this.notebookEditor.getCellById(data.cellId);
if (cell instanceof MarkupCellViewModel) {
cell.cellIsHovered = false;
}
break;
}
case 'cell-drag-start':
{
this.notebookEditor.didStartDragMarkupCell(data.cellId, data);
break;
}
case 'cell-drag':
{
this.notebookEditor.didDragMarkupCell(data.cellId, data);
break;
}
case 'cell-drop':
{
this.notebookEditor.didDropMarkupCell(data.cellId, {
dragOffsetY: data.dragOffsetY,
ctrlKey: data.ctrlKey,
altKey: data.altKey,
// Then show the context menu
const webviewRect = this.element.getBoundingClientRect();
this.contextMenuService.showContextMenu({
getActions: () => {
const result: IAction[] = [];
const menu = this.menuService.createMenu(MenuId.NotebookCellTitle, this.contextKeyService);
createAndFillInContextMenuActions(menu, undefined, result);
menu.dispose();
return result;
},
getAnchor: () => ({
x: webviewRect.x + data.clientX,
y: webviewRect.y + data.clientY
})
});
break;
}
case 'cell-drag-end':
{
this.notebookEditor.didEndDragMarkupCell(data.cellId);
break;
break;
}
case 'toggleMarkupPreview': {
const cell = this.notebookEditor.getCellById(data.cellId);
if (cell && !this.notebookEditor.creationOptions.isReadOnly) {
this.notebookEditor.setMarkupCellEditState(data.cellId, CellEditState.Editing);
this.notebookEditor.focusNotebookCell(cell, 'editor', { skipReveal: true });
}
break;
}
case 'mouseEnterMarkupCell': {
const cell = this.notebookEditor.getCellById(data.cellId);
if (cell instanceof MarkupCellViewModel) {
cell.cellIsHovered = true;
}
break;
}
case 'mouseLeaveMarkupCell': {
const cell = this.notebookEditor.getCellById(data.cellId);
if (cell instanceof MarkupCellViewModel) {
cell.cellIsHovered = false;
}
break;
}
case 'cell-drag-start': {
this.notebookEditor.didStartDragMarkupCell(data.cellId, data);
break;
}
case 'cell-drag': {
this.notebookEditor.didDragMarkupCell(data.cellId, data);
break;
}
case 'cell-drop': {
this.notebookEditor.didDropMarkupCell(data.cellId, {
dragOffsetY: data.dragOffsetY,
ctrlKey: data.ctrlKey,
altKey: data.altKey,
});
break;
}
case 'cell-drag-end': {
this.notebookEditor.didEndDragMarkupCell(data.cellId);
break;
}
case 'renderedMarkup': {
const cell = this.notebookEditor.getCellById(data.cellId);
if (cell instanceof MarkupCellViewModel) {
cell.renderedHtml = data.html;
}
case 'renderedMarkup':
{
const cell = this.notebookEditor.getCellById(data.cellId);
if (cell instanceof MarkupCellViewModel) {
cell.renderedHtml = data.html;
}
this._handleHighlightCodeBlock(data.codeBlocks);
break;
}
case 'renderedCellOutput':
{
this._handleHighlightCodeBlock(data.codeBlocks);
break;
}
this._handleHighlightCodeBlock(data.codeBlocks);
break;
}
case 'renderedCellOutput': {
this._handleHighlightCodeBlock(data.codeBlocks);
break;
}
}
}));
}

View file

@ -317,7 +317,7 @@ export class NotebookEditorToolbar extends Disposable {
}
}));
this._reigsterNotebookActionsToolbar();
this._registerNotebookActionsToolbar();
}
private _buildBody() {
@ -338,7 +338,7 @@ export class NotebookEditorToolbar extends Disposable {
DOM.append(this.domNode, this._notebookTopRightToolbarContainer);
}
private _reigsterNotebookActionsToolbar() {
private _registerNotebookActionsToolbar() {
this._notebookGlobalActionsMenu = this._register(this.menuService.createMenu(this.notebookEditor.creationOptions.menuIds.notebookToolbar, this.contextKeyService));
this._register(this._notebookGlobalActionsMenu);

View file

@ -788,7 +788,7 @@ export class SettingsEditor2 extends EditorPane {
private createSettingsTree(container: HTMLElement): void {
this.settingRenderers = this.instantiationService.createInstance(SettingTreeRenderers);
this._register(this.settingRenderers.onDidChangeSetting(e => this.onDidChangeSetting(e.key, e.value, e.type)));
this._register(this.settingRenderers.onDidChangeSetting(e => this.onDidChangeSetting(e.key, e.value, e.type, e.manualReset)));
this._register(this.settingRenderers.onDidOpenSettings(settingKey => {
this.openSettingsFile({ revealSetting: { key: settingKey, edit: true } });
}));
@ -869,16 +869,16 @@ export class SettingsEditor2 extends EditorPane {
}));
}
private onDidChangeSetting(key: string, value: any, type: SettingValueType | SettingValueType[]): void {
private onDidChangeSetting(key: string, value: any, type: SettingValueType | SettingValueType[], manualReset: boolean): void {
if (this.pendingSettingUpdate && this.pendingSettingUpdate.key !== key) {
this.updateChangedSetting(key, value);
this.updateChangedSetting(key, value, manualReset);
}
this.pendingSettingUpdate = { key, value };
if (SettingsEditor2.shouldSettingUpdateFast(type)) {
this.settingFastUpdateDelayer.trigger(() => this.updateChangedSetting(key, value));
this.settingFastUpdateDelayer.trigger(() => this.updateChangedSetting(key, value, manualReset));
} else {
this.settingSlowUpdateDelayer.trigger(() => this.updateChangedSetting(key, value));
this.settingSlowUpdateDelayer.trigger(() => this.updateChangedSetting(key, value, manualReset));
}
}
@ -948,7 +948,7 @@ export class SettingsEditor2 extends EditorPane {
return ancestors.reverse();
}
private updateChangedSetting(key: string, value: any): Promise<void> {
private updateChangedSetting(key: string, value: any, manualReset: boolean): Promise<void> {
// ConfigurationService displays the error if this fails.
// Force a render afterwards because onDidConfigurationUpdate doesn't fire if the update doesn't result in an effective setting value change
const settingsTarget = this.settingsTargetsWidget.settingsTarget;
@ -956,11 +956,13 @@ export class SettingsEditor2 extends EditorPane {
const configurationTarget = <ConfigurationTarget>(resource ? ConfigurationTarget.WORKSPACE_FOLDER : settingsTarget);
const overrides: IConfigurationOverrides = { resource };
const isManualReset = value === undefined;
const configurationTargetIsWorkspace = configurationTarget === ConfigurationTarget.WORKSPACE || configurationTarget === ConfigurationTarget.WORKSPACE_FOLDER;
// If the user is changing the value back to the default, do a 'reset' instead
const isManualReset = configurationTargetIsWorkspace ? manualReset : value === undefined;
// If the user is changing the value back to the default, and we're not targeting a workspace scope, do a 'reset' instead
const inspected = this.configurationService.inspect(key, overrides);
if (inspected.defaultValue === value) {
if (!configurationTargetIsWorkspace && inspected.defaultValue === value) {
value = undefined;
}

View file

@ -656,6 +656,7 @@ export interface ISettingChangeEvent {
key: string;
value: any; // undefined => reset/unconfigure
type: SettingValueType | SettingValueType[];
manualReset: boolean;
}
export interface ISettingLinkClickEvent {
@ -782,7 +783,7 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre
const descriptionElement = DOM.append(container, $('.setting-item-description'));
const modifiedIndicatorElement = DOM.append(container, $('.setting-item-modified-indicator'));
modifiedIndicatorElement.title = localize('modified', "Modified");
modifiedIndicatorElement.title = localize('modified', "The setting has been configured in the current scope.");
const valueElement = DOM.append(container, $('.setting-item-value'));
const controlElement = DOM.append(valueElement, $('div.setting-item-control'));
@ -883,7 +884,7 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre
template.miscLabel.updateOtherOverrides(element, template.elementDisposables, this._onDidClickOverrideElement);
const onChange = (value: any) => this._onDidChangeSetting.fire({ key: element.setting.key, value, type: template.context!.valueType });
const onChange = (value: any) => this._onDidChangeSetting.fire({ key: element.setting.key, value, type: template.context!.valueType, manualReset: false });
const deprecationText = element.setting.deprecationMessage || '';
if (deprecationText && element.setting.deprecationMessageIsMarkdown) {
const disposables = new DisposableStore();
@ -1449,7 +1450,8 @@ export class SettingExcludeRenderer extends AbstractSettingRenderer implements I
this._onDidChangeSetting.fire({
key: template.context.setting.key,
value: Object.keys(newValue).length === 0 ? undefined : sortKeys(newValue),
type: template.context.valueType
type: template.context.valueType,
manualReset: false
});
}
}
@ -1765,7 +1767,7 @@ export class SettingBoolRenderer extends AbstractSettingRenderer implements ITre
const controlElement = DOM.append(descriptionAndValueElement, $('.setting-item-bool-control'));
const descriptionElement = DOM.append(descriptionAndValueElement, $('.setting-item-description'));
const modifiedIndicatorElement = DOM.append(container, $('.setting-item-modified-indicator'));
modifiedIndicatorElement.title = localize('modified', "Modified");
modifiedIndicatorElement.title = localize('modified', "The setting has been configured in the current scope.");
const deprecationWarningElement = DOM.append(container, $('.setting-item-deprecation-message'));
@ -1904,7 +1906,7 @@ export class SettingTreeRenderers {
new Action('settings.resetSetting', localize('resetSettingLabel', "Reset Setting"), undefined, undefined, async context => {
if (context instanceof SettingsTreeSettingElement) {
if (!context.isUntrusted) {
this._onDidChangeSetting.fire({ key: context.setting.key, value: undefined, type: context.setting.type as SettingValueType });
this._onDidChangeSetting.fire({ key: context.setting.key, value: undefined, type: context.setting.type as SettingValueType, manualReset: true });
}
}
}),

View file

@ -20,7 +20,7 @@ import { URI } from 'vs/base/common/uri';
import { ISCMService, ISCMRepository, ISCMProvider } from 'vs/workbench/contrib/scm/common/scm';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
import { registerThemingParticipant, IColorTheme, ICssStyleCollector, themeColorFromId, IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService';
import { editorErrorForeground, registerColor, transparent } from 'vs/platform/theme/common/colorRegistry';
import { editorBackground, editorErrorForeground, registerColor, transparent } from 'vs/platform/theme/common/colorRegistry';
import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser';
import { registerEditorAction, registerEditorContribution, ServicesAccessor, EditorAction } from 'vs/editor/browser/editorExtensions';
import { PeekViewWidget, getOuterEditor, peekViewBorder, peekViewTitleBackground, peekViewTitleForeground, peekViewTitleInfoForeground } from 'vs/editor/contrib/peekView/browser/peekView';
@ -1461,15 +1461,22 @@ export class DirtyDiffWorkbenchController extends Disposable implements ext.IWor
registerEditorContribution(DirtyDiffController.ID, DirtyDiffController);
registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => {
const editorBackgroundColor = theme.getColor(editorBackground);
const editorGutterModifiedBackgroundColor = theme.getColor(editorGutterModifiedBackground);
const linearGradient = `-45deg, ${editorGutterModifiedBackgroundColor} 25%, ${editorBackgroundColor} 25%, ${editorBackgroundColor} 50%, ${editorGutterModifiedBackgroundColor} 50%, ${editorGutterModifiedBackgroundColor} 75%, ${editorBackgroundColor} 75%, ${editorBackgroundColor}`;
if (editorGutterModifiedBackgroundColor) {
collector.addRule(`
.monaco-editor .dirty-diff-modified {
border-left: 3px solid ${editorGutterModifiedBackgroundColor};
background-size: 3px 3px;
background-repeat-x: no-repeat;
background-image: linear-gradient(${linearGradient});
transition: opacity 0.5s;
}
.monaco-editor .dirty-diff-modified:before {
background: ${editorGutterModifiedBackgroundColor};
transform: translateX(3px);
background-size: 3px 3px;
background-image: linear-gradient(${linearGradient});
}
.monaco-editor .margin:hover .dirty-diff-modified {
opacity: 1;

View file

@ -28,7 +28,7 @@
height: 100%;
width: 0;
left: -2px;
transition: width 80ms linear, left 80ms linear;
transition: width 80ms linear, left 80ms linear, transform 80ms linear;
}
.monaco-editor .dirty-diff-deleted:before {

View file

@ -83,14 +83,32 @@ preexec() {
update_prompt
prompt_cmd_original() {
STATUS="$?"
$ORIGINAL_PROMPT_COMMAND
if [[ "$ORIGINAL_PROMPT_COMMAND" =~ .+\;.+ ]]; then
IFS=';'
else
IFS=' '
fi
read -ra ADDR <<<"$ORIGINAL_PROMPT_COMMAND"
for ((i = 0; i < ${#ADDR[@]}; i++)); do
eval ${ADDR[i]}
done
IFS=''
precmd
}
prompt_cmd() {
STATUS="$?"
precmd
}
ORIGINAL_PROMPT_COMMAND=$PROMPT_COMMAND
if [[ "$PROMPT_COMMAND" =~ (.+\;.+) ]]; then
# item1;item2...
ORIGINAL_PROMPT_COMMAND="$PROMPT_COMMAND"
else
# (item1, item2...)
ORIGINAL_PROMPT_COMMAND=${PROMPT_COMMAND[@]}
fi
if [[ -n "$ORIGINAL_PROMPT_COMMAND" && "$ORIGINAL_PROMPT_COMMAND" != "prompt_cmd" ]]; then
PROMPT_COMMAND=prompt_cmd_original
else

View file

@ -93,6 +93,10 @@
display: block;
}
.monaco-workbench .editor-instance .xterm {
padding-left: 20px !important;
}
.monaco-workbench .pane-body.integrated-terminal .terminal-group .monaco-split-view2.horizontal .split-view-view:first-child .xterm,
.integrated-terminal.shell-integration .xterm {
padding-left: 20px !important;

View file

@ -649,7 +649,7 @@ export interface ITerminalInstance {
/**
* Copies the terminal selection to the clipboard.
*/
copySelection(): Promise<void>;
copySelection(asHtml?: boolean): Promise<void>;
/**
* Current selection in the terminal.

View file

@ -2113,6 +2113,20 @@ export function registerTerminalActions() {
await accessor.get(ITerminalService).activeInstance?.copySelection();
}
});
registerAction2(class extends Action2 {
constructor() {
super({
id: TerminalCommandId.CopySelectionAsHtml,
title: { value: localize('workbench.action.terminal.copySelectionAsHtml', "Copy Selection as HTML"), original: 'Copy Selection as HTML' },
f1: true,
category,
precondition: ContextKeyExpr.and(ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), TerminalContextKeys.textSelected)
});
}
async run(accessor: ServicesAccessor) {
await accessor.get(ITerminalService).activeInstance?.copySelection(true);
}
});
}
if (BrowserFeatures.clipboard.readText) {

View file

@ -1127,10 +1127,21 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
return this.xterm ? this.xterm.raw.hasSelection() : false;
}
async copySelection(): Promise<void> {
async copySelection(asHtml?: boolean): Promise<void> {
const xterm = await this._xtermReadyPromise;
if (this.hasSelection()) {
await this._clipboardService.writeText(xterm.raw.getSelection());
if (asHtml) {
const selectionAsHtml = await xterm.getSelectionAsHtml();
function listener(e: any) {
e.clipboardData.setData('text/html', selectionAsHtml);
e.preventDefault();
}
document.addEventListener('copy', listener);
document.execCommand('copy');
document.removeEventListener('copy', listener);
} else {
await this._clipboardService.writeText(xterm.raw.getSelection());
}
} else {
this._notificationService.warn(nls.localize('terminal.integrated.copySelection.noSelection', 'The terminal has no selection to copy'));
}

View file

@ -137,6 +137,17 @@ export function setupTerminalMenus(): void {
order: 1
}
},
{
id: MenuId.TerminalInstanceContext,
item: {
command: {
id: TerminalCommandId.CopySelectionAsHtml,
title: localize('workbench.action.terminal.copySelectionAsHtml', "Copy as HTML")
},
group: ContextMenuGroup.Edit,
order: 2
}
},
{
id: MenuId.TerminalInstanceContext,
item: {
@ -145,7 +156,7 @@ export function setupTerminalMenus(): void {
title: localize('workbench.action.terminal.paste.short', "Paste")
},
group: ContextMenuGroup.Edit,
order: 2
order: 3
}
},
{
@ -237,6 +248,17 @@ export function setupTerminalMenus(): void {
order: 1
}
},
{
id: MenuId.TerminalEditorInstanceContext,
item: {
command: {
id: TerminalCommandId.CopySelectionAsHtml,
title: localize('workbench.action.terminal.copySelectionAsHtml', "Copy as HTML")
},
group: ContextMenuGroup.Edit,
order: 2
}
},
{
id: MenuId.TerminalEditorInstanceContext,
item: {
@ -245,7 +267,7 @@ export function setupTerminalMenus(): void {
title: localize('workbench.action.terminal.paste.short', "Paste")
},
group: ContextMenuGroup.Edit,
order: 2
order: 3
}
},
{

View file

@ -7,6 +7,7 @@ import type { IBuffer, ITheme, RendererType, Terminal as RawXtermTerminal } from
import type { ISearchOptions, SearchAddon as SearchAddonType } from 'xterm-addon-search';
import type { Unicode11Addon as Unicode11AddonType } from 'xterm-addon-unicode11';
import type { WebglAddon as WebglAddonType } from 'xterm-addon-webgl';
import { SerializeAddon as SerializeAddonType } from 'xterm-addon-serialize';
import { IXtermCore } from 'vs/workbench/contrib/terminal/browser/xterm-private';
import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper';
@ -42,6 +43,7 @@ const NUMBER_OF_FRAMES_TO_MEASURE = 20;
let SearchAddon: typeof SearchAddonType;
let Unicode11Addon: typeof Unicode11AddonType;
let WebglAddon: typeof WebglAddonType;
let SerializeAddon: typeof SerializeAddonType;
/**
* Wraps the xterm object with additional functionality. Interaction with the backing process is out
@ -64,6 +66,7 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal {
private _searchAddon?: SearchAddonType;
private _unicode11Addon?: Unicode11AddonType;
private _webglAddon?: WebglAddonType;
private _serializeAddon?: SerializeAddonType;
private readonly _onDidRequestRunCommand = new Emitter<string>();
readonly onDidRequestRunCommand = this._onDidRequestRunCommand.event;
@ -167,6 +170,15 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal {
this.raw.loadAddon(this._decorationAddon);
}
async getSelectionAsHtml(): Promise<string> {
if (!this._serializeAddon) {
const Addon = await this._getSerializeAddonConstructor();
this._serializeAddon = new Addon();
this.raw.loadAddon(this._serializeAddon);
}
return this._serializeAddon.serializeAsHTML({ onlySelection: true });
}
attachToElement(container: HTMLElement): HTMLElement {
// Update the theme when attaching as the terminal location could have changed
this._updateTheme();
@ -404,6 +416,13 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal {
return WebglAddon;
}
protected async _getSerializeAddonConstructor(): Promise<typeof SerializeAddonType> {
if (!SerializeAddon) {
SerializeAddon = (await import('xterm-addon-serialize')).SerializeAddon;
}
return SerializeAddon;
}
private _disposeOfWebglRenderer(): void {
try {
this._webglAddon?.dispose();

View file

@ -472,6 +472,7 @@ export const enum TerminalCommandId {
RunRecentCommand = 'workbench.action.terminal.runRecentCommand',
GoToRecentDirectory = 'workbench.action.terminal.goToRecentDirectory',
CopySelection = 'workbench.action.terminal.copySelection',
CopySelectionAsHtml = 'workbench.action.terminal.copySelectionAsHtml',
SelectAll = 'workbench.action.terminal.selectAll',
DeleteWordLeft = 'workbench.action.terminal.deleteWordLeft',
DeleteWordRight = 'workbench.action.terminal.deleteWordRight',
@ -563,6 +564,7 @@ export const DEFAULT_COMMANDS_TO_SKIP_SHELL: string[] = [
TerminalCommandId.ClearSelection,
TerminalCommandId.Clear,
TerminalCommandId.CopySelection,
TerminalCommandId.CopySelectionAsHtml,
TerminalCommandId.DeleteToLineStart,
TerminalCommandId.DeleteWordLeft,
TerminalCommandId.DeleteWordRight,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View file

@ -29,8 +29,6 @@ import { IEditorSerializer, IEditorFactoryRegistry, EditorExtensions } from 'vs/
import { EditorInput } from 'vs/workbench/common/editor/editorInput';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, IWorkspaceContextService, IWorkspaceFoldersWillChangeEvent, toWorkspaceIdentifier, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { isWeb } from 'vs/base/common/platform';
import { IsWebContext } from 'vs/platform/contextkey/common/contextkeys';
import { dirname, resolve } from 'vs/base/common/path';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IMarkdownString, MarkdownString } from 'vs/base/common/htmlContent';
@ -630,7 +628,7 @@ registerAction2(class extends Action2 {
super({
id: CONFIGURE_TRUST_COMMAND_ID,
title: { original: 'Configure Workspace Trust', value: localize('configureWorkspaceTrust', "Configure Workspace Trust") },
precondition: ContextKeyExpr.and(WorkspaceTrustContext.IsEnabled, IsWebContext.negate(), ContextKeyExpr.equals(`config.${WORKSPACE_TRUST_ENABLED}`, true)),
precondition: ContextKeyExpr.and(WorkspaceTrustContext.IsEnabled, ContextKeyExpr.equals(`config.${WORKSPACE_TRUST_ENABLED}`, true)),
category: localize('workspacesCategory', "Workspaces"),
f1: true
});
@ -648,14 +646,14 @@ registerAction2(class extends Action2 {
super({
id: MANAGE_TRUST_COMMAND_ID,
title: { original: 'Manage Workspace Trust', value: localize('manageWorkspaceTrust', "Manage Workspace Trust") },
precondition: ContextKeyExpr.and(WorkspaceTrustContext.IsEnabled, IsWebContext.negate(), ContextKeyExpr.equals(`config.${WORKSPACE_TRUST_ENABLED}`, true)),
precondition: ContextKeyExpr.and(WorkspaceTrustContext.IsEnabled, ContextKeyExpr.equals(`config.${WORKSPACE_TRUST_ENABLED}`, true)),
category: localize('workspacesCategory', "Workspaces"),
f1: true,
menu: {
id: MenuId.GlobalActivity,
group: '6_workspace_trust',
order: 40,
when: ContextKeyExpr.and(WorkspaceTrustContext.IsEnabled, IsWebContext.negate(), ContextKeyExpr.equals(`config.${WORKSPACE_TRUST_ENABLED}`, true))
when: ContextKeyExpr.and(WorkspaceTrustContext.IsEnabled, ContextKeyExpr.equals(`config.${WORKSPACE_TRUST_ENABLED}`, true))
},
});
}
@ -686,7 +684,6 @@ Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration)
[WORKSPACE_TRUST_ENABLED]: {
type: 'boolean',
default: true,
included: !isWeb,
description: localize('workspace.trust.description', "Controls whether or not workspace trust is enabled within VS Code."),
tags: [WORKSPACE_TRUST_SETTING_TAG],
scope: ConfigurationScope.APPLICATION,
@ -694,7 +691,6 @@ Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration)
[WORKSPACE_TRUST_STARTUP_PROMPT]: {
type: 'string',
default: 'once',
included: !isWeb,
description: localize('workspace.trust.startupPrompt.description', "Controls when the startup prompt to trust a workspace is shown."),
tags: [WORKSPACE_TRUST_SETTING_TAG],
scope: ConfigurationScope.APPLICATION,
@ -708,7 +704,6 @@ Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration)
[WORKSPACE_TRUST_BANNER]: {
type: 'string',
default: 'untilDismissed',
included: !isWeb,
description: localize('workspace.trust.banner.description', "Controls when the restricted mode banner is shown."),
tags: [WORKSPACE_TRUST_SETTING_TAG],
scope: ConfigurationScope.APPLICATION,
@ -722,7 +717,6 @@ Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration)
[WORKSPACE_TRUST_UNTRUSTED_FILES]: {
type: 'string',
default: 'prompt',
included: !isWeb,
markdownDescription: localize('workspace.trust.untrustedFiles.description', "Controls how to handle opening untrusted files in a trusted workspace. This setting also applies to opening files in an empty window which is trusted via `#{0}#`.", WORKSPACE_TRUST_EMPTY_WINDOW),
tags: [WORKSPACE_TRUST_SETTING_TAG],
scope: ConfigurationScope.APPLICATION,
@ -736,7 +730,6 @@ Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration)
[WORKSPACE_TRUST_EMPTY_WINDOW]: {
type: 'boolean',
default: true,
included: !isWeb,
markdownDescription: localize('workspace.trust.emptyWindow.description', "Controls whether or not the empty window is trusted by default within VS Code. When used with `#{0}#`, you can enable the full functionality of VS Code without prompting in an empty window.", WORKSPACE_TRUST_UNTRUSTED_FILES),
tags: [WORKSPACE_TRUST_SETTING_TAG],
scope: ConfigurationScope.APPLICATION

View file

@ -588,7 +588,12 @@ interface ITrustedUriHostColumnTemplateData {
}
function getHostLabel(labelService: ILabelService, item: ITrustedUriItem): string {
return item.uri.authority ? labelService.getHostLabel(item.uri.scheme, item.uri.authority) : localize('localAuthority', "Local");
const hostLabel = item.uri.authority ? labelService.getHostLabel(item.uri.scheme, item.uri.authority) : localize('localAuthority', "Local");
if (hostLabel === undefined || hostLabel.length === 0) {
return item.uri.authority;
}
return hostLabel;
}
class TrustedUriHostColumnRenderer implements ITableRenderer<ITrustedUriItem, ITrustedUriHostColumnTemplateData> {

View file

@ -205,7 +205,7 @@ export class BrowserWorkbenchEnvironmentService implements IBrowserWorkbenchEnvi
get skipWelcome(): boolean { return this.payload?.get('skipWelcome') === 'true'; }
@memoize
get disableWorkspaceTrust(): boolean { return true; }
get disableWorkspaceTrust(): boolean { return !this.options.enableWorkspaceTrust; }
private payload: Map<string, string> | undefined;

View file

@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IBuiltinExtensionsScannerService, ExtensionType, IExtensionManifest, IExtension } from 'vs/platform/extensions/common/extensions';
import { IBuiltinExtensionsScannerService, ExtensionType, IExtensionManifest, IExtension, TargetPlatform } from 'vs/platform/extensions/common/extensions';
import { isWeb } from 'vs/base/common/platform';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity';
@ -57,6 +57,7 @@ export class BuiltinExtensionsScannerService implements IBuiltinExtensionsScanne
manifest: e.packageNLS ? localizeManifest(e.packageJSON, e.packageNLS) : e.packageJSON,
readmeUrl: e.readmePath ? uriIdentityService.extUri.joinPath(builtinExtensionsServiceUrl!, e.readmePath) : undefined,
changelogUrl: e.changelogPath ? uriIdentityService.extUri.joinPath(builtinExtensionsServiceUrl!, e.changelogPath) : undefined,
targetPlatform: TargetPlatform.WEB,
}));
}
}

View file

@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IBuiltinExtensionsScannerService, ExtensionType, IExtensionIdentifier, IExtension, IExtensionManifest } from 'vs/platform/extensions/common/extensions';
import { IBuiltinExtensionsScannerService, ExtensionType, IExtensionIdentifier, IExtension, IExtensionManifest, TargetPlatform } from 'vs/platform/extensions/common/extensions';
import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService';
import { IScannedExtension, IWebExtensionsScannerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { isWeb } from 'vs/base/common/platform';
@ -15,7 +15,7 @@ import { Queue } from 'vs/base/common/async';
import { VSBuffer } from 'vs/base/common/buffer';
import { ILogService } from 'vs/platform/log/common/log';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IExtensionGalleryService, IGalleryExtension, IGalleryMetadata, Metadata, TargetPlatform } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IExtensionGalleryService, IGalleryExtension, IGalleryMetadata, Metadata } from 'vs/platform/extensionManagement/common/extensionManagement';
import { groupByExtension, areSameExtensions, getGalleryExtensionId, getExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { Disposable } from 'vs/base/common/lifecycle';
import { localizeManifest } from 'vs/platform/extensionManagement/common/extensionNls';
@ -465,7 +465,8 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten
isBuiltin,
readmeUrl: webExtension.readmeUri,
changelogUrl: webExtension.changelogUri,
metadata: webExtension.metadata
metadata: webExtension.metadata,
targetPlatform: TargetPlatform.WEB
};
}

View file

@ -5,10 +5,10 @@
import { Event, EventMultiplexer } from 'vs/base/common/event';
import {
ILocalExtension, IGalleryExtension, IExtensionIdentifier, IExtensionsControlManifest, IGalleryMetadata, IExtensionGalleryService, InstallOptions, UninstallOptions, InstallVSIXOptions, InstallExtensionResult, TargetPlatform, ExtensionManagementError, ExtensionManagementErrorCode
ILocalExtension, IGalleryExtension, IExtensionIdentifier, IExtensionsControlManifest, IGalleryMetadata, IExtensionGalleryService, InstallOptions, UninstallOptions, InstallVSIXOptions, InstallExtensionResult, ExtensionManagementError, ExtensionManagementErrorCode
} from 'vs/platform/extensionManagement/common/extensionManagement';
import { DidUninstallExtensionOnServerEvent, IExtensionManagementServer, IExtensionManagementServerService, InstallExtensionOnServerEvent, IWorkbenchExtensionManagementService, UninstallExtensionOnServerEvent } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { ExtensionType, isLanguagePackExtension, IExtensionManifest, getWorkspaceSupportTypeMessage } from 'vs/platform/extensions/common/extensions';
import { ExtensionType, isLanguagePackExtension, IExtensionManifest, getWorkspaceSupportTypeMessage, TargetPlatform } from 'vs/platform/extensions/common/extensions';
import { URI } from 'vs/base/common/uri';
import { Disposable } from 'vs/base/common/lifecycle';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';

View file

@ -3,8 +3,8 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ExtensionType, IExtensionIdentifier, IExtensionManifest } from 'vs/platform/extensions/common/extensions';
import { IExtensionManagementService, ILocalExtension, IGalleryExtension, IGalleryMetadata, InstallOperation, IExtensionGalleryService, InstallOptions, Metadata, TargetPlatform } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ExtensionType, IExtensionIdentifier, IExtensionManifest, TargetPlatform } from 'vs/platform/extensions/common/extensions';
import { IExtensionManagementService, ILocalExtension, IGalleryExtension, IGalleryMetadata, InstallOperation, IExtensionGalleryService, InstallOptions, Metadata } from 'vs/platform/extensionManagement/common/extensionManagement';
import { URI } from 'vs/base/common/uri';
import { areSameExtensions, getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { IScannedExtension, IWebExtensionsScannerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';

View file

@ -9,14 +9,14 @@ import { IWorkbenchExtensionEnablementService, IWebExtensionsScannerService } fr
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IExtensionService, IExtensionHost, toExtensionDescription, ExtensionRunningLocation, extensionRunningLocationToString } from 'vs/workbench/services/extensions/common/extensions';
import { IExtensionService, IExtensionHost, toExtensionDescription, ExtensionRunningLocation, ExtensionHostKind, extensionHostKindToString } from 'vs/workbench/services/extensions/common/extensions';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IFileService } from 'vs/platform/files/common/files';
import { IProductService } from 'vs/platform/product/common/productService';
import { AbstractExtensionService, ExtensionRunningPreference, extensionRunningPreferenceToString } from 'vs/workbench/services/extensions/common/abstractExtensionService';
import { RemoteExtensionHost, IRemoteExtensionHostDataProvider, IRemoteExtensionHostInitData } from 'vs/workbench/services/extensions/common/remoteExtensionHost';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { WebWorkerExtensionHost } from 'vs/workbench/services/extensions/browser/webWorkerExtensionHost';
import { IWebWorkerExtensionHostDataProvider, WebWorkerExtensionHost } from 'vs/workbench/services/extensions/browser/webWorkerExtensionHost';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ExtensionIdentifier, IExtensionDescription, IExtension, ExtensionType } from 'vs/platform/extensions/common/extensions';
import { ExtensionKind } from 'vs/platform/environment/common/environment';
@ -72,8 +72,6 @@ export class ExtensionService extends AbstractExtensionService implements IExten
logService
);
this._runningLocation = new Map<string, ExtensionRunningLocation>();
// Initialize installed extensions first and do it only after workbench is ready
this._lifecycleService.when(LifecyclePhase.Ready).then(async () => {
await this._userDataInitializationService.initializeInstalledExtensions(this._instantiationService);
@ -107,11 +105,11 @@ export class ExtensionService extends AbstractExtensionService implements IExten
this._disposables.add(this._fileService.registerProvider(Schemas.https, provider));
}
private _createLocalExtensionHostDataProvider() {
private _createLocalExtensionHostDataProvider(desiredRunningLocation: ExtensionRunningLocation): IWebWorkerExtensionHostDataProvider {
return {
getInitData: async () => {
const allExtensions = await this.getExtensions();
const localWebWorkerExtensions = filterByRunningLocation(allExtensions, this._runningLocation, ExtensionRunningLocation.LocalWebWorker);
const localWebWorkerExtensions = this._filterByRunningLocation(allExtensions, desiredRunningLocation);
return {
autoStart: true,
extensions: localWebWorkerExtensions
@ -130,20 +128,20 @@ export class ExtensionService extends AbstractExtensionService implements IExten
};
}
protected _pickRunningLocation(extensionId: ExtensionIdentifier, extensionKinds: ExtensionKind[], isInstalledLocally: boolean, isInstalledRemotely: boolean, preference: ExtensionRunningPreference): ExtensionRunningLocation {
protected _pickExtensionHostKind(extensionId: ExtensionIdentifier, extensionKinds: ExtensionKind[], isInstalledLocally: boolean, isInstalledRemotely: boolean, preference: ExtensionRunningPreference): ExtensionHostKind | null {
const result = ExtensionService.pickRunningLocation(extensionKinds, isInstalledLocally, isInstalledRemotely, preference);
this._logService.trace(`pickRunningLocation for ${extensionId.value}, extension kinds: [${extensionKinds.join(', ')}], isInstalledLocally: ${isInstalledLocally}, isInstalledRemotely: ${isInstalledRemotely}, preference: ${extensionRunningPreferenceToString(preference)} => ${extensionRunningLocationToString(result)}`);
this._logService.trace(`pickRunningLocation for ${extensionId.value}, extension kinds: [${extensionKinds.join(', ')}], isInstalledLocally: ${isInstalledLocally}, isInstalledRemotely: ${isInstalledRemotely}, preference: ${extensionRunningPreferenceToString(preference)} => ${extensionHostKindToString(result)}`);
return result;
}
public static pickRunningLocation(extensionKinds: ExtensionKind[], isInstalledLocally: boolean, isInstalledRemotely: boolean, preference: ExtensionRunningPreference): ExtensionRunningLocation {
const result: ExtensionRunningLocation[] = [];
public static pickRunningLocation(extensionKinds: ExtensionKind[], isInstalledLocally: boolean, isInstalledRemotely: boolean, preference: ExtensionRunningPreference): ExtensionHostKind | null {
const result: ExtensionHostKind[] = [];
let canRunRemotely = false;
for (const extensionKind of extensionKinds) {
if (extensionKind === 'ui' && isInstalledRemotely) {
// ui extensions run remotely if possible (but only as a last resort)
if (preference === ExtensionRunningPreference.Remote) {
return ExtensionRunningLocation.Remote;
return ExtensionHostKind.Remote;
} else {
canRunRemotely = true;
}
@ -151,39 +149,42 @@ export class ExtensionService extends AbstractExtensionService implements IExten
if (extensionKind === 'workspace' && isInstalledRemotely) {
// workspace extensions run remotely if possible
if (preference === ExtensionRunningPreference.None || preference === ExtensionRunningPreference.Remote) {
return ExtensionRunningLocation.Remote;
return ExtensionHostKind.Remote;
} else {
result.push(ExtensionRunningLocation.Remote);
result.push(ExtensionHostKind.Remote);
}
}
if (extensionKind === 'web' && (isInstalledLocally || isInstalledRemotely)) {
// web worker extensions run in the local web worker if possible
if (preference === ExtensionRunningPreference.None || preference === ExtensionRunningPreference.Local) {
return ExtensionRunningLocation.LocalWebWorker;
return ExtensionHostKind.LocalWebWorker;
} else {
result.push(ExtensionRunningLocation.LocalWebWorker);
result.push(ExtensionHostKind.LocalWebWorker);
}
}
}
if (canRunRemotely) {
result.push(ExtensionRunningLocation.Remote);
result.push(ExtensionHostKind.Remote);
}
return (result.length > 0 ? result[0] : ExtensionRunningLocation.None);
return (result.length > 0 ? result[0] : null);
}
protected _createExtensionHosts(_isInitialStart: boolean): IExtensionHost[] {
const result: IExtensionHost[] = [];
const webWorkerExtHost = this._instantiationService.createInstance(WebWorkerExtensionHost, false, this._createLocalExtensionHostDataProvider());
result.push(webWorkerExtHost);
const remoteAgentConnection = this._remoteAgentService.getConnection();
if (remoteAgentConnection) {
const remoteExtHost = this._instantiationService.createInstance(RemoteExtensionHost, this._createRemoteExtensionHostDataProvider(remoteAgentConnection.remoteAuthority), this._remoteAgentService.socketFactory);
result.push(remoteExtHost);
protected _createExtensionHost(runningLocation: ExtensionRunningLocation, _isInitialStart: boolean): IExtensionHost | null {
switch (runningLocation.kind) {
case ExtensionHostKind.LocalProcess: {
return null;
}
case ExtensionHostKind.LocalWebWorker: {
return this._instantiationService.createInstance(WebWorkerExtensionHost, runningLocation, false, this._createLocalExtensionHostDataProvider(runningLocation));
}
case ExtensionHostKind.Remote: {
const remoteAgentConnection = this._remoteAgentService.getConnection();
if (remoteAgentConnection) {
return this._instantiationService.createInstance(RemoteExtensionHost, runningLocation, this._createRemoteExtensionHostDataProvider(remoteAgentConnection.remoteAuthority), this._remoteAgentService.socketFactory);
}
return null;
}
}
return result;
}
protected async _scanAndHandleExtensions(): Promise<void> {
@ -199,12 +200,12 @@ export class ExtensionService extends AbstractExtensionService implements IExten
const remoteAgentConnection = this._remoteAgentService.getConnection();
// `determineRunningLocation` will look at the complete picture (e.g. an extension installed on both sides),
// takes care of duplicates and picks a running location for each extension
this._runningLocation = this._runningLocationClassifier.determineRunningLocation(localExtensions, remoteExtensions);
this._initializeRunningLocation(localExtensions, remoteExtensions);
// Some remote extensions could run locally in the web worker, so store them
const remoteExtensionsThatNeedToRunLocally = filterByRunningLocation(remoteExtensions, this._runningLocation, ExtensionRunningLocation.LocalWebWorker);
localExtensions = filterByRunningLocation(localExtensions, this._runningLocation, ExtensionRunningLocation.LocalWebWorker);
remoteExtensions = filterByRunningLocation(remoteExtensions, this._runningLocation, ExtensionRunningLocation.Remote);
const remoteExtensionsThatNeedToRunLocally = this._filterByExtensionHostKind(remoteExtensions, ExtensionHostKind.LocalWebWorker);
localExtensions = this._filterByExtensionHostKind(localExtensions, ExtensionHostKind.LocalWebWorker);
remoteExtensions = this._filterByExtensionHostKind(remoteExtensions, ExtensionHostKind.Remote);
// Add locally the remote extensions that need to run locally in the web worker
for (const ext of remoteExtensionsThatNeedToRunLocally) {
@ -246,10 +247,6 @@ export class ExtensionService extends AbstractExtensionService implements IExten
}
}
function filterByRunningLocation(extensions: IExtensionDescription[], runningLocation: Map<string, ExtensionRunningLocation>, desiredRunningLocation: ExtensionRunningLocation): IExtensionDescription[] {
return extensions.filter(ext => runningLocation.get(ExtensionIdentifier.toKey(ext.identifier)) === desiredRunningLocation);
}
function includes(extensions: IExtensionDescription[], identifier: ExtensionIdentifier): boolean {
for (const extension of extensions) {
if (ExtensionIdentifier.equals(extension.identifier, identifier)) {

View file

@ -16,7 +16,7 @@ import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'
import * as platform from 'vs/base/common/platform';
import * as dom from 'vs/base/browser/dom';
import { URI } from 'vs/base/common/uri';
import { IExtensionHost, ExtensionHostLogFileName, ExtensionHostKind } from 'vs/workbench/services/extensions/common/extensions';
import { IExtensionHost, ExtensionHostLogFileName, LocalWebWorkerRunningLocation } from 'vs/workbench/services/extensions/common/extensions';
import { IProductService } from 'vs/platform/product/common/productService';
import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService';
import { joinPath } from 'vs/base/common/resources';
@ -30,6 +30,7 @@ import { ILayoutService } from 'vs/platform/layout/browser/layoutService';
import { FileAccess } from 'vs/base/common/network';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { parentOriginHash } from 'vs/workbench/browser/webview';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
export interface IWebWorkerExtensionHostInitData {
readonly autoStart: boolean;
@ -42,9 +43,9 @@ export interface IWebWorkerExtensionHostDataProvider {
export class WebWorkerExtensionHost extends Disposable implements IExtensionHost {
public readonly kind = ExtensionHostKind.LocalWebWorker;
public readonly remoteAuthority = null;
public readonly lazyStart: boolean;
public readonly extensions = new ExtensionDescriptionRegistry([]);
private readonly _onDidExit = this._register(new Emitter<[number, string | null]>());
public readonly onExit: Event<[number, string | null]> = this._onDidExit.event;
@ -57,6 +58,7 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost
private readonly _extensionHostLogFile: URI;
constructor(
public readonly runningLocation: LocalWebWorkerRunningLocation,
lazyStart: boolean,
private readonly _initDataProvider: IWebWorkerExtensionHostDataProvider,
@ITelemetryService private readonly _telemetryService: ITelemetryService,
@ -265,6 +267,7 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost
private async _createExtHostInitData(): Promise<IExtensionHostInitData> {
const [telemetryInfo, initData] = await Promise.all([this._telemetryService.getTelemetryInfo(), this._initDataProvider.getInitData()]);
const workspace = this._contextService.getWorkspace();
this.extensions.deltaExtensions(initData.extensions, []);
return {
commit: this._productService.commit,
version: this._productService.version,
@ -288,7 +291,7 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost
},
resolvedExtensions: [],
hostExtensions: [],
extensions: initData.extensions,
extensions: this.extensions.getAllExtensionDescriptions(),
telemetryInfo,
logLevel: this._logService.getLevel(),
logsLocation: this._extensionHostLogsLocation,

View file

@ -15,7 +15,7 @@ import { IWebExtensionsScannerService, IWorkbenchExtensionEnablementService } fr
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { ActivationTimes, ExtensionPointContribution, IExtensionService, IExtensionsStatus, IMessage, IWillActivateEvent, IResponsiveStateChangeEvent, toExtension, IExtensionHost, ActivationKind, ExtensionHostKind, toExtensionDescription, ExtensionRunningLocation, extensionHostKindToString, ExtensionActivationReason, IInternalExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { ActivationTimes, ExtensionPointContribution, IExtensionService, IExtensionsStatus, IMessage, IWillActivateEvent, IResponsiveStateChangeEvent, toExtension, IExtensionHost, ActivationKind, ExtensionHostKind, toExtensionDescription, ExtensionRunningLocation, extensionHostKindToString, ExtensionActivationReason, IInternalExtensionService, RemoteRunningLocation, LocalProcessRunningLocation, LocalWebWorkerRunningLocation } from 'vs/workbench/services/extensions/common/extensions';
import { ExtensionMessageCollector, ExtensionPoint, ExtensionsRegistry, IExtensionPoint, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
import { ResponsiveState } from 'vs/workbench/services/extensions/common/rpcProtocol';
@ -132,29 +132,28 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
public _serviceBrand: undefined;
protected readonly _onDidRegisterExtensions: Emitter<void> = this._register(new Emitter<void>());
private readonly _onDidRegisterExtensions: Emitter<void> = this._register(new Emitter<void>());
public readonly onDidRegisterExtensions = this._onDidRegisterExtensions.event;
protected readonly _onDidChangeExtensionsStatus: Emitter<ExtensionIdentifier[]> = this._register(new Emitter<ExtensionIdentifier[]>());
private readonly _onDidChangeExtensionsStatus: Emitter<ExtensionIdentifier[]> = this._register(new Emitter<ExtensionIdentifier[]>());
public readonly onDidChangeExtensionsStatus: Event<ExtensionIdentifier[]> = this._onDidChangeExtensionsStatus.event;
protected readonly _onDidChangeExtensions: Emitter<void> = this._register(new Emitter<void>({ leakWarningThreshold: 400 }));
private readonly _onDidChangeExtensions: Emitter<void> = this._register(new Emitter<void>({ leakWarningThreshold: 400 }));
public readonly onDidChangeExtensions: Event<void> = this._onDidChangeExtensions.event;
protected readonly _onWillActivateByEvent = this._register(new Emitter<IWillActivateEvent>());
private readonly _onWillActivateByEvent = this._register(new Emitter<IWillActivateEvent>());
public readonly onWillActivateByEvent: Event<IWillActivateEvent> = this._onWillActivateByEvent.event;
protected readonly _onDidChangeResponsiveChange = this._register(new Emitter<IResponsiveStateChangeEvent>());
private readonly _onDidChangeResponsiveChange = this._register(new Emitter<IResponsiveStateChangeEvent>());
public readonly onDidChangeResponsiveChange: Event<IResponsiveStateChangeEvent> = this._onDidChangeResponsiveChange.event;
protected readonly _runningLocationClassifier: ExtensionRunningLocationClassifier;
protected readonly _registry: ExtensionDescriptionRegistry;
private readonly _registryLock: Lock;
private readonly _installedExtensionsReady: Barrier;
protected readonly _isDev: boolean;
private readonly _isDev: boolean;
private readonly _extensionsMessages: Map<string, IMessage[]>;
protected readonly _allRequestedActivateEvents = new Set<string>();
private readonly _allRequestedActivateEvents = new Set<string>();
private readonly _proposedApiController: ProposedApiController;
private readonly _isExtensionDevHost: boolean;
protected readonly _isExtensionDevTestFromCli: boolean;
@ -162,10 +161,12 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
private _deltaExtensionsQueue: DeltaExtensionsQueueItem[];
private _inHandleDeltaExtensions: boolean;
protected _runningLocation: Map<string, ExtensionRunningLocation>;
private _runningLocation: Map<string, ExtensionRunningLocation | null>;
private _lastExtensionHostId: number = 0;
private _maxLocalProcessAffinity: number = 0;
// --- Members used per extension host process
protected _extensionHostManagers: IExtensionHostManager[];
private _extensionHostManagers: IExtensionHostManager[];
protected _extensionHostActiveExtensions: Map<string, ExtensionIdentifier>;
private _extensionHostActivationTimes: Map<string, ActivationTimes>;
private _extensionHostExtensionRuntimeErrors: Map<string, Error[]>;
@ -187,11 +188,6 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
) {
super();
this._runningLocationClassifier = new ExtensionRunningLocationClassifier(
(extension) => this._getExtensionKind(extension),
(extensionId, extensionKinds, isInstalledLocally, isInstalledRemotely, preference) => this._pickRunningLocation(extensionId, extensionKinds, isInstalledLocally, isInstalledRemotely, preference)
);
// help the file service to activate providers by activating extensions by file system event
this._register(this._fileService.onWillActivateFileSystemProvider(e => {
if (e.scheme !== Schemas.vscodeRemote) {
@ -264,17 +260,247 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
return this._extensionManifestPropertiesService.getExtensionKind(extensionDescription);
}
protected abstract _pickRunningLocation(extensionId: ExtensionIdentifier, extensionKinds: ExtensionKind[], isInstalledLocally: boolean, isInstalledRemotely: boolean, preference: ExtensionRunningPreference): ExtensionRunningLocation;
protected abstract _pickExtensionHostKind(extensionId: ExtensionIdentifier, extensionKinds: ExtensionKind[], isInstalledLocally: boolean, isInstalledRemotely: boolean, preference: ExtensionRunningPreference): ExtensionHostKind | null;
protected _getExtensionHostManager(kind: ExtensionHostKind): IExtensionHostManager | null {
protected _getExtensionHostManagers(kind: ExtensionHostKind): IExtensionHostManager[] {
return this._extensionHostManagers.filter(extHostManager => extHostManager.kind === kind);
}
protected _getExtensionHostManagerByRunningLocation(runningLocation: ExtensionRunningLocation): IExtensionHostManager | null {
for (const extensionHostManager of this._extensionHostManagers) {
if (extensionHostManager.kind === kind) {
if (extensionHostManager.representsRunningLocation(runningLocation)) {
return extensionHostManager;
}
}
return null;
}
//#region running location
private _computeAffinity(inputExtensions: IExtensionDescription[], extensionHostKind: ExtensionHostKind, isInitialAllocation: boolean): { affinities: Map<string, number>; maxAffinity: number } {
// Only analyze extensions that can execute
const extensions = new Map<string, IExtensionDescription>();
for (const extension of inputExtensions) {
if (extension.main || extension.browser) {
extensions.set(ExtensionIdentifier.toKey(extension.identifier), extension);
}
}
// Also add existing extensions of the same kind that can execute
for (const extension of this._registry.getAllExtensionDescriptions()) {
if (extension.main || extension.browser) {
const runningLocation = this._runningLocation.get(ExtensionIdentifier.toKey(extension.identifier));
if (runningLocation && runningLocation.kind === extensionHostKind) {
extensions.set(ExtensionIdentifier.toKey(extension.identifier), extension);
}
}
}
// Initially, each extension belongs to its own group
const groups = new Map<string, number>();
let groupNumber = 0;
for (const [_, extension] of extensions) {
groups.set(ExtensionIdentifier.toKey(extension.identifier), ++groupNumber);
}
const changeGroup = (from: number, to: number) => {
for (const [key, group] of groups) {
if (group === from) {
groups.set(key, to);
}
}
};
// We will group things together when there are dependencies
for (const [_, extension] of extensions) {
if (!extension.extensionDependencies) {
continue;
}
const myGroup = groups.get(ExtensionIdentifier.toKey(extension.identifier))!;
for (const depId of extension.extensionDependencies) {
const depGroup = groups.get(ExtensionIdentifier.toKey(depId));
if (!depGroup) {
// probably can't execute, so it has no impact
continue;
}
if (depGroup === myGroup) {
// already in the same group
continue;
}
changeGroup(depGroup, myGroup);
}
}
// Initialize with existing affinities
const resultingAffinities = new Map<number, number>();
let lastAffinity = 0;
for (const [_, extension] of extensions) {
const runningLocation = this._runningLocation.get(ExtensionIdentifier.toKey(extension.identifier));
if (runningLocation) {
const group = groups.get(ExtensionIdentifier.toKey(extension.identifier))!;
resultingAffinities.set(group, runningLocation.affinity);
lastAffinity = Math.max(lastAffinity, runningLocation.affinity);
}
}
// Go through each configured affinity and try to accomodate it
const configuredAffinities = this._configurationService.getValue<{ [extensionId: string]: number } | undefined>('extensions.experimental.affinity') || {};
const configuredExtensionIds = Object.keys(configuredAffinities);
const configuredAffinityToResultingAffinity = new Map<number, number>();
for (const extensionId of configuredExtensionIds) {
const configuredAffinity = configuredAffinities[extensionId];
if (typeof configuredAffinity !== 'number' || configuredAffinity <= 0 || Math.floor(configuredAffinity) !== configuredAffinity) {
this._logService.info(`Ignoring configured affinity for '${extensionId}' because the value is not a positive integer.`);
continue;
}
const group = groups.get(ExtensionIdentifier.toKey(extensionId));
if (!group) {
this._logService.info(`Ignoring configured affinity for '${extensionId}' because the extension is unknown or cannot execute.`);
continue;
}
const affinity1 = resultingAffinities.get(group);
if (affinity1) {
// Affinity for this group is already established
configuredAffinityToResultingAffinity.set(configuredAffinity, affinity1);
continue;
}
const affinity2 = configuredAffinityToResultingAffinity.get(configuredAffinity);
if (affinity2) {
// Affinity for this configuration is already established
resultingAffinities.set(group, affinity2);
continue;
}
if (!isInitialAllocation) {
this._logService.info(`Ignoring configured affinity for '${extensionId}' because extension host(s) are already running. Reload window.`);
continue;
}
const affinity3 = ++lastAffinity;
configuredAffinityToResultingAffinity.set(configuredAffinity, affinity3);
resultingAffinities.set(group, affinity3);
}
const result = new Map<string, number>();
for (const extension of inputExtensions) {
const group = groups.get(ExtensionIdentifier.toKey(extension.identifier)) || 0;
const affinity = resultingAffinities.get(group) || 0;
result.set(ExtensionIdentifier.toKey(extension.identifier), affinity);
}
if (lastAffinity > 0 && isInitialAllocation) {
for (let affinity = 1; affinity <= lastAffinity; affinity++) {
const extensionIds: ExtensionIdentifier[] = [];
for (const extension of inputExtensions) {
if (result.get(ExtensionIdentifier.toKey(extension.identifier)) === affinity) {
extensionIds.push(extension.identifier);
}
}
this._logService.info(`Placing extension(s) ${extensionIds.map(e => e.value).join(', ')} on a separate extension host.`);
}
}
return { affinities: result, maxAffinity: lastAffinity };
}
private _computeRunningLocation(localExtensions: IExtensionDescription[], remoteExtensions: IExtensionDescription[], isInitialAllocation: boolean): { runningLocation: Map<string, ExtensionRunningLocation | null>; maxLocalProcessAffinity: number } {
const extensionHostKinds = ExtensionHostKindClassifier.determineExtensionHostKinds(
localExtensions,
remoteExtensions,
(extension) => this._getExtensionKind(extension),
(extensionId, extensionKinds, isInstalledLocally, isInstalledRemotely, preference) => this._pickExtensionHostKind(extensionId, extensionKinds, isInstalledLocally, isInstalledRemotely, preference)
);
const extensions = new Map<string, IExtensionDescription>();
for (const extension of localExtensions) {
extensions.set(ExtensionIdentifier.toKey(extension.identifier), extension);
}
for (const extension of remoteExtensions) {
extensions.set(ExtensionIdentifier.toKey(extension.identifier), extension);
}
const result = new Map<string, ExtensionRunningLocation | null>();
const localProcessExtensions: IExtensionDescription[] = [];
for (const [extensionIdKey, extensionHostKind] of extensionHostKinds) {
let runningLocation: ExtensionRunningLocation | null = null;
if (extensionHostKind === ExtensionHostKind.LocalProcess) {
const extensionDescription = extensions.get(ExtensionIdentifier.toKey(extensionIdKey));
if (extensionDescription) {
localProcessExtensions.push(extensionDescription);
}
} else if (extensionHostKind === ExtensionHostKind.LocalWebWorker) {
runningLocation = new LocalWebWorkerRunningLocation();
} else if (extensionHostKind === ExtensionHostKind.Remote) {
runningLocation = new RemoteRunningLocation();
}
result.set(extensionIdKey, runningLocation);
}
const { affinities, maxAffinity } = this._computeAffinity(localProcessExtensions, ExtensionHostKind.LocalProcess, isInitialAllocation);
for (const extension of localProcessExtensions) {
const affinity = affinities.get(ExtensionIdentifier.toKey(extension.identifier)) || 0;
result.set(ExtensionIdentifier.toKey(extension.identifier), new LocalProcessRunningLocation(affinity));
}
return { runningLocation: result, maxLocalProcessAffinity: maxAffinity };
}
protected _determineRunningLocation(localExtensions: IExtensionDescription[]): Map<string, ExtensionRunningLocation | null> {
return this._computeRunningLocation(localExtensions, [], false).runningLocation;
}
protected _initializeRunningLocation(localExtensions: IExtensionDescription[], remoteExtensions: IExtensionDescription[]): void {
const { runningLocation, maxLocalProcessAffinity } = this._computeRunningLocation(localExtensions, remoteExtensions, true);
this._runningLocation = runningLocation;
this._maxLocalProcessAffinity = maxLocalProcessAffinity;
this._startExtensionHostsIfNecessary(true, []);
}
/**
* Update `this._runningLocation` with running locations for newly enabled/installed extensions.
*/
private _updateRunningLocationForAddedExtensions(toAdd: IExtensionDescription[]): void {
// Determine new running location
const localProcessExtensions: IExtensionDescription[] = [];
for (const extension of toAdd) {
const extensionKind = this._getExtensionKind(extension);
const isRemote = extension.extensionLocation.scheme === Schemas.vscodeRemote;
const extensionHostKind = this._pickExtensionHostKind(extension.identifier, extensionKind, !isRemote, isRemote, ExtensionRunningPreference.None);
let runningLocation: ExtensionRunningLocation | null = null;
if (extensionHostKind === ExtensionHostKind.LocalProcess) {
localProcessExtensions.push(extension);
} else if (extensionHostKind === ExtensionHostKind.LocalWebWorker) {
runningLocation = new LocalWebWorkerRunningLocation();
} else if (extensionHostKind === ExtensionHostKind.Remote) {
runningLocation = new RemoteRunningLocation();
}
this._runningLocation.set(ExtensionIdentifier.toKey(extension.identifier), runningLocation);
}
const { affinities } = this._computeAffinity(localProcessExtensions, ExtensionHostKind.LocalProcess, false);
for (const extension of localProcessExtensions) {
const affinity = affinities.get(ExtensionIdentifier.toKey(extension.identifier)) || 0;
this._runningLocation.set(ExtensionIdentifier.toKey(extension.identifier), new LocalProcessRunningLocation(affinity));
}
}
protected _filterByRunningLocation(extensions: IExtensionDescription[], desiredRunningLocation: ExtensionRunningLocation): IExtensionDescription[] {
return filterByRunningLocation(extensions, this._runningLocation, desiredRunningLocation);
}
protected _filterByExtensionHostKind(extensions: IExtensionDescription[], desiredExtensionHostKind: ExtensionHostKind): IExtensionDescription[] {
return filterByExtensionHostKind(extensions, this._runningLocation, desiredExtensionHostKind);
}
protected _filterByExtensionHostManager(extensions: IExtensionDescription[], extensionHostManager: IExtensionHostManager): IExtensionDescription[] {
return filterByExtensionHostManager(extensions, this._runningLocation, extensionHostManager);
}
//#endregion
//#region deltaExtensions
private async _handleDeltaExtensions(item: DeltaExtensionsQueueItem): Promise<void> {
@ -374,47 +600,32 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
}
private async _updateExtensionsOnExtHosts(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise<void> {
const groupedToRemove: ExtensionIdentifier[][] = [];
const groupRemove = (extensionHostKind: ExtensionHostKind, extensionRunningLocation: ExtensionRunningLocation) => {
groupedToRemove[extensionHostKind] = filterByRunningLocation(toRemove, extId => extId, this._runningLocation, extensionRunningLocation);
};
groupRemove(ExtensionHostKind.LocalProcess, ExtensionRunningLocation.LocalProcess);
groupRemove(ExtensionHostKind.LocalWebWorker, ExtensionRunningLocation.LocalWebWorker);
groupRemove(ExtensionHostKind.Remote, ExtensionRunningLocation.Remote);
// Remove old running location
const removedRunningLocation = new Map<string, ExtensionRunningLocation | null>();
for (const extensionId of toRemove) {
this._runningLocation.delete(ExtensionIdentifier.toKey(extensionId));
const extensionKey = ExtensionIdentifier.toKey(extensionId);
removedRunningLocation.set(extensionKey, this._runningLocation.get(extensionKey) || null);
this._runningLocation.delete(extensionKey);
}
const groupedToAdd: IExtensionDescription[][] = [];
const groupAdd = (extensionHostKind: ExtensionHostKind, extensionRunningLocation: ExtensionRunningLocation) => {
groupedToAdd[extensionHostKind] = filterByRunningLocation(toAdd, ext => ext.identifier, this._runningLocation, extensionRunningLocation);
};
for (const extension of toAdd) {
const extensionKind = this._getExtensionKind(extension);
const isRemote = extension.extensionLocation.scheme === Schemas.vscodeRemote;
const runningLocation = this._pickRunningLocation(extension.identifier, extensionKind, !isRemote, isRemote, ExtensionRunningPreference.None);
this._runningLocation.set(ExtensionIdentifier.toKey(extension.identifier), runningLocation);
}
groupAdd(ExtensionHostKind.LocalProcess, ExtensionRunningLocation.LocalProcess);
groupAdd(ExtensionHostKind.LocalWebWorker, ExtensionRunningLocation.LocalWebWorker);
groupAdd(ExtensionHostKind.Remote, ExtensionRunningLocation.Remote);
const promises: Promise<void>[] = [];
for (const extensionHostKind of [ExtensionHostKind.LocalProcess, ExtensionHostKind.LocalWebWorker, ExtensionHostKind.Remote]) {
const toAdd = groupedToAdd[extensionHostKind];
const toRemove = groupedToRemove[extensionHostKind];
if (toAdd.length > 0 || toRemove.length > 0) {
const extensionHostManager = this._getExtensionHostManager(extensionHostKind);
if (extensionHostManager) {
promises.push(extensionHostManager.deltaExtensions(toAdd, toRemove));
}
}
}
// Determine new running location
this._updateRunningLocationForAddedExtensions(toAdd);
const promises = this._extensionHostManagers.map(
extHostManager => this._updateExtensionsOnExtHost(extHostManager, toAdd, toRemove, removedRunningLocation)
);
await Promise.all(promises);
}
private async _updateExtensionsOnExtHost(extensionHostManager: IExtensionHostManager, _toAdd: IExtensionDescription[], _toRemove: ExtensionIdentifier[], removedRunningLocation: Map<string, ExtensionRunningLocation | null>): Promise<void> {
const toAdd = filterByExtensionHostManager(_toAdd, this._runningLocation, extensionHostManager);
const toRemove = _filterByExtensionHostManager(_toRemove, extId => extId, removedRunningLocation, extensionHostManager);
if (toRemove.length > 0 || toAdd.length > 0) {
await extensionHostManager.deltaExtensions(toAdd, toRemove);
}
}
public canAddExtension(extension: IExtensionDescription): boolean {
const existing = this._registry.getExtensionDescription(extension.identifier);
if (existing) {
@ -429,8 +640,8 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
const extensionKind = this._getExtensionKind(extension);
const isRemote = extension.extensionLocation.scheme === Schemas.vscodeRemote;
const runningLocation = this._pickRunningLocation(extension.identifier, extensionKind, !isRemote, isRemote, ExtensionRunningPreference.None);
if (runningLocation === ExtensionRunningLocation.None) {
const extensionHostKind = this._pickExtensionHostKind(extension.identifier, extensionKind, !isRemote, isRemote, ExtensionRunningPreference.None);
if (extensionHostKind === null) {
return false;
}
@ -518,7 +729,7 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
protected async _initialize(): Promise<void> {
perf.mark('code/willLoadExtensions');
this._startExtensionHosts(true, []);
this._startExtensionHostsIfNecessary(true, []);
const lock = await this._registryLock.acquire('_initialize');
try {
@ -558,38 +769,31 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
this._onExtensionHostExit(exitCode);
}
private findTestExtensionHost(testLocation: URI): IExtensionHostManager | undefined | null {
let extensionHostKind: ExtensionHostKind | undefined;
private findTestExtensionHost(testLocation: URI): IExtensionHostManager | null {
let runningLocation: ExtensionRunningLocation | null = null;
for (const extension of this._registry.getAllExtensionDescriptions()) {
if (isEqualOrParent(testLocation, extension.extensionLocation)) {
const runningLocation = this._runningLocation.get(ExtensionIdentifier.toKey(extension.identifier));
if (runningLocation === ExtensionRunningLocation.LocalProcess) {
extensionHostKind = ExtensionHostKind.LocalProcess;
} else if (runningLocation === ExtensionRunningLocation.LocalWebWorker) {
extensionHostKind = ExtensionHostKind.LocalWebWorker;
} else if (runningLocation === ExtensionRunningLocation.Remote) {
extensionHostKind = ExtensionHostKind.Remote;
}
runningLocation = this._runningLocation.get(ExtensionIdentifier.toKey(extension.identifier)) || null;
break;
}
}
if (extensionHostKind === undefined) {
if (runningLocation === null) {
// not sure if we should support that, but it was possible to have an test outside an extension
if (testLocation.scheme === Schemas.vscodeRemote) {
extensionHostKind = ExtensionHostKind.Remote;
runningLocation = new RemoteRunningLocation();
} else {
// When a debugger attaches to the extension host, it will surface all console.log messages from the extension host,
// but not necessarily from the window. So it would be best if any errors get printed to the console of the extension host.
// That is why here we use the local process extension host even for non-file URIs
extensionHostKind = ExtensionHostKind.LocalProcess;
runningLocation = new LocalProcessRunningLocation(0);
}
}
if (extensionHostKind !== undefined) {
return this._getExtensionHostManager(extensionHostKind);
if (runningLocation !== null) {
return this._getExtensionHostManagerByRunningLocation(runningLocation);
}
return undefined;
return null;
}
private _releaseBarrier(): void {
@ -619,14 +823,42 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
}
}
private _startExtensionHosts(isInitialStart: boolean, initialActivationEvents: string[]): void {
const extensionHosts = this._createExtensionHosts(isInitialStart);
extensionHosts.forEach((extensionHost) => {
const processManager: IExtensionHostManager = createExtensionHostManager(this._instantiationService, extensionHost, isInitialStart, initialActivationEvents, this._acquireInternalAPI());
processManager.onDidExit(([code, signal]) => this._onExtensionHostCrashOrExit(processManager, code, signal));
processManager.onDidChangeResponsiveState((responsiveState) => { this._onDidChangeResponsiveChange.fire({ isResponsive: responsiveState === ResponsiveState.Responsive }); });
this._extensionHostManagers.push(processManager);
private _startExtensionHostsIfNecessary(isInitialStart: boolean, initialActivationEvents: string[]): void {
const locations: ExtensionRunningLocation[] = [];
for (let affinity = 0; affinity <= this._maxLocalProcessAffinity; affinity++) {
locations.push(new LocalProcessRunningLocation(affinity));
}
locations.push(new LocalWebWorkerRunningLocation());
locations.push(new RemoteRunningLocation());
for (const location of locations) {
if (this._getExtensionHostManagerByRunningLocation(location)) {
// already running
continue;
}
const extHostManager = this._createExtensionHostManager(location, isInitialStart, initialActivationEvents);
if (extHostManager) {
this._extensionHostManagers.push(extHostManager);
}
}
}
private _createExtensionHostManager(runningLocation: ExtensionRunningLocation, isInitialStart: boolean, initialActivationEvents: string[]): IExtensionHostManager | null {
const extensionHost = this._createExtensionHost(runningLocation, isInitialStart);
if (!extensionHost) {
return null;
}
const extensionHostId = String(++this._lastExtensionHostId);
const processManager: IExtensionHostManager = createExtensionHostManager(this._instantiationService, extensionHostId, extensionHost, isInitialStart, initialActivationEvents, this._acquireInternalAPI());
processManager.onDidExit(([code, signal]) => this._onExtensionHostCrashOrExit(processManager, code, signal));
processManager.onDidChangeResponsiveState((responsiveState) => {
this._onDidChangeResponsiveChange.fire({
extensionHostId: extensionHostId,
extensionHostKind: processManager.kind,
isResponsive: responsiveState === ResponsiveState.Responsive
});
});
return processManager;
}
private _onExtensionHostCrashOrExit(extensionHost: IExtensionHostManager, code: number, signal: string | null): void {
@ -660,12 +892,10 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
const lock = await this._registryLock.acquire('startExtensionHosts');
try {
this._startExtensionHosts(false, Array.from(this._allRequestedActivateEvents.keys()));
this._startExtensionHostsIfNecessary(false, Array.from(this._allRequestedActivateEvents.keys()));
const localProcessExtensionHost = this._getExtensionHostManager(ExtensionHostKind.LocalProcess);
if (localProcessExtensionHost) {
await localProcessExtensionHost.ready();
}
const localProcessExtensionHosts = this._getExtensionHostManagers(ExtensionHostKind.LocalProcess);
await Promise.all(localProcessExtensionHosts.map(extHost => extHost.ready()));
} finally {
lock.dispose();
}
@ -771,15 +1001,28 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
messages: this._extensionsMessages.get(extensionKey) || [],
activationTimes: this._extensionHostActivationTimes.get(extensionKey),
runtimeErrors: this._extensionHostExtensionRuntimeErrors.get(extensionKey) || [],
runningLocation: this._runningLocation.get(extensionKey) || ExtensionRunningLocation.None,
runningLocation: this._runningLocation.get(extensionKey) || null,
};
}
}
return result;
}
public getInspectPort(_tryEnableInspector: boolean): Promise<number> {
return Promise.resolve(0);
public async getInspectPort(extensionHostId: string, tryEnableInspector: boolean): Promise<number> {
for (const extHostManager of this._extensionHostManagers) {
if (extHostManager.extensionHostId === extensionHostId) {
return extHostManager.getInspectPort(tryEnableInspector);
}
}
return 0;
}
public async getInspectPorts(extensionHostKind: ExtensionHostKind, tryEnableInspector: boolean): Promise<number[]> {
const result = await Promise.all(
this._getExtensionHostManagers(extensionHostKind).map(extHost => extHost.getInspectPort(tryEnableInspector))
);
// remove 0s:
return result.filter(element => Boolean(element));
}
public async setRemoteEnvironment(env: { [key: string]: string | null }): Promise<void> {
@ -1040,7 +1283,7 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
//#endregion
protected abstract _createExtensionHosts(isInitialStart: boolean): IExtensionHost[];
protected abstract _createExtensionHost(runningLocation: ExtensionRunningLocation, isInitialStart: boolean): IExtensionHost | null;
protected abstract _scanAndHandleExtensions(): Promise<void>;
protected abstract _scanSingleExtension(extension: IExtension): Promise<IExtensionDescription | null>;
public abstract _onExtensionHostExit(code: number): void;
@ -1094,25 +1337,28 @@ class ExtensionInfo {
}
}
class ExtensionRunningLocationClassifier {
constructor(
private readonly getExtensionKind: (extensionDescription: IExtensionDescription) => ExtensionKind[],
private readonly pickRunningLocation: (extensionId: ExtensionIdentifier, extensionKinds: ExtensionKind[], isInstalledLocally: boolean, isInstalledRemotely: boolean, preference: ExtensionRunningPreference) => ExtensionRunningLocation,
) {
}
class ExtensionHostKindClassifier {
private _toExtensionWithKind(extensions: IExtensionDescription[]): Map<string, ExtensionWithKind> {
private static _toExtensionWithKind(
extensions: IExtensionDescription[],
getExtensionKind: (extensionDescription: IExtensionDescription) => ExtensionKind[]
): Map<string, ExtensionWithKind> {
const result = new Map<string, ExtensionWithKind>();
extensions.forEach((desc) => {
const ext = new ExtensionWithKind(desc, this.getExtensionKind(desc));
const ext = new ExtensionWithKind(desc, getExtensionKind(desc));
result.set(ext.key, ext);
});
return result;
}
public determineRunningLocation(_localExtensions: IExtensionDescription[], _remoteExtensions: IExtensionDescription[]): Map<string, ExtensionRunningLocation> {
const localExtensions = this._toExtensionWithKind(_localExtensions);
const remoteExtensions = this._toExtensionWithKind(_remoteExtensions);
public static determineExtensionHostKinds(
_localExtensions: IExtensionDescription[],
_remoteExtensions: IExtensionDescription[],
getExtensionKind: (extensionDescription: IExtensionDescription) => ExtensionKind[],
pickExtensionHostKind: (extensionId: ExtensionIdentifier, extensionKinds: ExtensionKind[], isInstalledLocally: boolean, isInstalledRemotely: boolean, preference: ExtensionRunningPreference) => ExtensionHostKind | null
): Map<string, ExtensionHostKind | null> {
const localExtensions = this._toExtensionWithKind(_localExtensions, getExtensionKind);
const remoteExtensions = this._toExtensionWithKind(_remoteExtensions, getExtensionKind);
const allExtensions = new Map<string, ExtensionInfo>();
const collectExtension = (ext: ExtensionWithKind) => {
@ -1127,7 +1373,7 @@ class ExtensionRunningLocationClassifier {
localExtensions.forEach((ext) => collectExtension(ext));
remoteExtensions.forEach((ext) => collectExtension(ext));
const runningLocation = new Map<string, ExtensionRunningLocation>();
const extensionHostKinds = new Map<string, ExtensionHostKind | null>();
allExtensions.forEach((ext) => {
const isInstalledLocally = Boolean(ext.local);
const isInstalledRemotely = Boolean(ext.remote);
@ -1142,10 +1388,10 @@ class ExtensionRunningLocationClassifier {
preference = ExtensionRunningPreference.Remote;
}
runningLocation.set(ext.key, this.pickRunningLocation(ext.identifier, ext.kind, isInstalledLocally, isInstalledRemotely, preference));
extensionHostKinds.set(ext.key, pickExtensionHostKind(ext.identifier, ext.kind, isInstalledLocally, isInstalledRemotely, preference));
});
return runningLocation;
return extensionHostKinds;
}
}
@ -1244,6 +1490,29 @@ class ProposedApiController {
}
}
function filterByRunningLocation<T>(extensions: T[], extId: (item: T) => ExtensionIdentifier, runningLocation: Map<string, ExtensionRunningLocation>, desiredRunningLocation: ExtensionRunningLocation): T[] {
return extensions.filter(ext => runningLocation.get(ExtensionIdentifier.toKey(extId(ext))) === desiredRunningLocation);
export function filterByRunningLocation(extensions: IExtensionDescription[], runningLocation: Map<string, ExtensionRunningLocation | null>, desiredRunningLocation: ExtensionRunningLocation): IExtensionDescription[] {
return _filterByRunningLocation(extensions, ext => ext.identifier, runningLocation, desiredRunningLocation);
}
function _filterByRunningLocation<T>(extensions: T[], extId: (item: T) => ExtensionIdentifier, runningLocation: Map<string, ExtensionRunningLocation | null>, desiredRunningLocation: ExtensionRunningLocation): T[] {
return _filterExtensions(extensions, extId, runningLocation, extRunningLocation => desiredRunningLocation.equals(extRunningLocation));
}
function filterByExtensionHostKind(extensions: IExtensionDescription[], runningLocation: Map<string, ExtensionRunningLocation | null>, desiredExtensionHostKind: ExtensionHostKind): IExtensionDescription[] {
return _filterExtensions(extensions, ext => ext.identifier, runningLocation, extRunningLocation => extRunningLocation.kind === desiredExtensionHostKind);
}
function filterByExtensionHostManager(extensions: IExtensionDescription[], runningLocation: Map<string, ExtensionRunningLocation | null>, extensionHostManager: IExtensionHostManager): IExtensionDescription[] {
return _filterByExtensionHostManager(extensions, ext => ext.identifier, runningLocation, extensionHostManager);
}
function _filterByExtensionHostManager<T>(extensions: T[], extId: (item: T) => ExtensionIdentifier, runningLocation: Map<string, ExtensionRunningLocation | null>, extensionHostManager: IExtensionHostManager): T[] {
return _filterExtensions(extensions, extId, runningLocation, extRunningLocation => extensionHostManager.representsRunningLocation(extRunningLocation));
}
function _filterExtensions<T>(extensions: T[], extId: (item: T) => ExtensionIdentifier, runningLocation: Map<string, ExtensionRunningLocation | null>, predicate: (extRunningLocation: ExtensionRunningLocation) => boolean): T[] {
return extensions.filter((ext) => {
const extRunningLocation = runningLocation.get(ExtensionIdentifier.toKey(extId(ext)));
return extRunningLocation && predicate(extRunningLocation);
});
}

View file

@ -12,49 +12,54 @@ import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiati
import { ExtHostCustomersRegistry, IInternalExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers';
import { Proxied, ProxyIdentifier } from 'vs/workbench/services/extensions/common/proxyIdentifier';
import { IRPCProtocolLogger, RPCProtocol, RequestInitiator, ResponsiveState } from 'vs/workbench/services/extensions/common/rpcProtocol';
import { RemoteAuthorityResolverError, ResolverResult } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { RemoteAuthorityResolverErrorCode } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import * as nls from 'vs/nls';
import { registerAction2, Action2 } from 'vs/platform/actions/common/actions';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { StopWatch } from 'vs/base/common/stopwatch';
import { VSBuffer } from 'vs/base/common/buffer';
import { IExtensionHost, ExtensionHostKind, ActivationKind, extensionHostKindToString, ExtensionActivationReason, IInternalExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IExtensionHost, ExtensionHostKind, ActivationKind, extensionHostKindToString, ExtensionActivationReason, IInternalExtensionService, ExtensionRunningLocation } from 'vs/workbench/services/extensions/common/extensions';
import { CATEGORIES } from 'vs/workbench/common/actions';
import { Barrier, timeout } from 'vs/base/common/async';
import { URI } from 'vs/base/common/uri';
import { ILogService } from 'vs/platform/log/common/log';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IExtensionHostProxy } from 'vs/workbench/services/extensions/common/extensionHostProxy';
import { IExtensionHostProxy, IResolveAuthorityResult } from 'vs/workbench/services/extensions/common/extensionHostProxy';
// Enable to see detailed message communication between window and extension host
const LOG_EXTENSION_HOST_COMMUNICATION = false;
const LOG_USE_COLORS = true;
export interface IExtensionHostManager {
readonly extensionHostId: string;
readonly kind: ExtensionHostKind;
readonly onDidExit: Event<[number, string | null]>;
readonly onDidChangeResponsiveState: Event<ResponsiveState>;
dispose(): void;
ready(): Promise<void>;
representsRunningLocation(runningLocation: ExtensionRunningLocation): boolean;
deltaExtensions(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise<void>;
activate(extension: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<boolean>;
activateByEvent(activationEvent: string, activationKind: ActivationKind): Promise<void>;
activationEventIsDone(activationEvent: string): boolean;
getInspectPort(tryEnableInspector: boolean): Promise<number>;
resolveAuthority(remoteAuthority: string): Promise<ResolverResult>;
getCanonicalURI(remoteAuthority: string, uri: URI): Promise<URI>;
resolveAuthority(remoteAuthority: string, resolveAttempt: number): Promise<IResolveAuthorityResult>;
/**
* Returns `null` if no resolver for `remoteAuthority` is found.
*/
getCanonicalURI(remoteAuthority: string, uri: URI): Promise<URI | null>;
start(enabledExtensionIds: ExtensionIdentifier[]): Promise<void>;
extensionTestsExecute(): Promise<number>;
extensionTestsSendExit(exitCode: number): Promise<void>;
setRemoteEnvironment(env: { [key: string]: string | null }): Promise<void>;
}
export function createExtensionHostManager(instantiationService: IInstantiationService, extensionHost: IExtensionHost, isInitialStart: boolean, initialActivationEvents: string[], internalExtensionService: IInternalExtensionService): IExtensionHostManager {
export function createExtensionHostManager(instantiationService: IInstantiationService, extensionHostId: string, extensionHost: IExtensionHost, isInitialStart: boolean, initialActivationEvents: string[], internalExtensionService: IInternalExtensionService): IExtensionHostManager {
if (extensionHost.lazyStart && isInitialStart && initialActivationEvents.length === 0) {
return instantiationService.createInstance(LazyStartExtensionHostManager, extensionHost, internalExtensionService);
return instantiationService.createInstance(LazyStartExtensionHostManager, extensionHostId, extensionHost, internalExtensionService);
}
return instantiationService.createInstance(ExtensionHostManager, extensionHost, initialActivationEvents, internalExtensionService);
return instantiationService.createInstance(ExtensionHostManager, extensionHostId, extensionHost, initialActivationEvents, internalExtensionService);
}
export type ExtensionHostStartupClassification = {
@ -77,7 +82,6 @@ export type ExtensionHostStartupEvent = {
class ExtensionHostManager extends Disposable implements IExtensionHostManager {
public readonly kind: ExtensionHostKind;
public readonly onDidExit: Event<[number, string | null]>;
private readonly _onDidChangeResponsiveState: Emitter<ResponsiveState> = this._register(new Emitter<ResponsiveState>());
@ -92,10 +96,14 @@ class ExtensionHostManager extends Disposable implements IExtensionHostManager {
private readonly _customers: IDisposable[];
private readonly _extensionHost: IExtensionHost;
private _proxy: Promise<IExtensionHostProxy | null> | null;
private _resolveAuthorityAttempt: number;
private _hasStarted = false;
public get kind(): ExtensionHostKind {
return this._extensionHost.runningLocation.kind;
}
constructor(
public readonly extensionHostId: string,
extensionHost: IExtensionHost,
initialActivationEvents: string[],
private readonly _internalExtensionService: IInternalExtensionService,
@ -111,7 +119,6 @@ class ExtensionHostManager extends Disposable implements IExtensionHostManager {
this._customers = [];
this._extensionHost = extensionHost;
this.kind = this._extensionHost.kind;
this.onDidExit = this._extensionHost.onExit;
const startingTelemetryEvent: ExtensionHostStartupEvent = {
@ -166,7 +173,6 @@ class ExtensionHostManager extends Disposable implements IExtensionHostManager {
measure: () => this.measure()
}));
});
this._resolveAuthorityAttempt = 0;
}
public override dispose(): void {
@ -190,7 +196,7 @@ class ExtensionHostManager extends Disposable implements IExtensionHostManager {
}
private async measure(): Promise<ExtHostLatencyResult | null> {
const proxy = await this._getProxy();
const proxy = await this._proxy;
if (!proxy) {
return null;
}
@ -205,12 +211,8 @@ class ExtensionHostManager extends Disposable implements IExtensionHostManager {
};
}
private async _getProxy(): Promise<IExtensionHostProxy | null> {
return this._proxy;
}
public async ready(): Promise<void> {
await this._getProxy();
await this._proxy;
}
private async _measureLatency(proxy: IExtensionHostProxy): Promise<number> {
@ -310,7 +312,7 @@ class ExtensionHostManager extends Disposable implements IExtensionHostManager {
}
public async activate(extension: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<boolean> {
const proxy = await this._getProxy();
const proxy = await this._proxy;
if (!proxy) {
return false;
}
@ -359,57 +361,52 @@ class ExtensionHostManager extends Disposable implements IExtensionHostManager {
return 0;
}
public async resolveAuthority(remoteAuthority: string): Promise<ResolverResult> {
const authorityPlusIndex = remoteAuthority.indexOf('+');
if (authorityPlusIndex === -1) {
// This authority does not need to be resolved, simply parse the port number
const lastColon = remoteAuthority.lastIndexOf(':');
return Promise.resolve({
authority: {
authority: remoteAuthority,
host: remoteAuthority.substring(0, lastColon),
port: parseInt(remoteAuthority.substring(lastColon + 1), 10),
connectionToken: undefined
}
});
}
const proxy = await this._getProxy();
public async resolveAuthority(remoteAuthority: string, resolveAttempt: number): Promise<IResolveAuthorityResult> {
const proxy = await this._proxy;
if (!proxy) {
throw new Error(`Cannot resolve authority`);
return {
type: 'error',
error: {
message: `Cannot resolve authority`,
code: RemoteAuthorityResolverErrorCode.Unknown,
detail: undefined
}
};
}
this._resolveAuthorityAttempt++;
const result = await proxy.resolveAuthority(remoteAuthority, this._resolveAuthorityAttempt);
if (result.type === 'ok') {
return result.value;
} else {
throw new RemoteAuthorityResolverError(result.error.message, result.error.code, result.error.detail);
try {
return proxy.resolveAuthority(remoteAuthority, resolveAttempt);
} catch (err) {
return {
type: 'error',
error: {
message: err.message,
code: RemoteAuthorityResolverErrorCode.Unknown,
detail: err
}
};
}
}
public async getCanonicalURI(remoteAuthority: string, uri: URI): Promise<URI> {
const authorityPlusIndex = remoteAuthority.indexOf('+');
if (authorityPlusIndex === -1) {
// This authority does not use a resolver
return uri;
}
const proxy = await this._getProxy();
public async getCanonicalURI(remoteAuthority: string, uri: URI): Promise<URI | null> {
const proxy = await this._proxy;
if (!proxy) {
throw new Error(`Cannot resolve canonical URI`);
}
const result = await proxy.getCanonicalURI(remoteAuthority, uri);
return URI.revive(result);
return proxy.getCanonicalURI(remoteAuthority, uri);
}
public async start(enabledExtensionIds: ExtensionIdentifier[]): Promise<void> {
const proxy = await this._getProxy();
const proxy = await this._proxy;
if (!proxy) {
return;
}
this._extensionHost.extensions.keepOnly(enabledExtensionIds);
return proxy.startExtensionHost(enabledExtensionIds);
}
public async extensionTestsExecute(): Promise<number> {
const proxy = await this._getProxy();
const proxy = await this._proxy;
if (!proxy) {
throw new Error('Could not obtain Extension Host Proxy');
}
@ -417,7 +414,7 @@ class ExtensionHostManager extends Disposable implements IExtensionHostManager {
}
public async extensionTestsSendExit(exitCode: number): Promise<void> {
const proxy = await this._getProxy();
const proxy = await this._proxy;
if (!proxy) {
return;
}
@ -430,16 +427,21 @@ class ExtensionHostManager extends Disposable implements IExtensionHostManager {
}
}
public representsRunningLocation(runningLocation: ExtensionRunningLocation): boolean {
return this._extensionHost.runningLocation.equals(runningLocation);
}
public async deltaExtensions(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise<void> {
const proxy = await this._getProxy();
const proxy = await this._proxy;
if (!proxy) {
return;
}
this._extensionHost.extensions.deltaExtensions(toAdd, toRemove);
return proxy.deltaExtensions(toAdd, toRemove);
}
public async setRemoteEnvironment(env: { [key: string]: string | null }): Promise<void> {
const proxy = await this._getProxy();
const proxy = await this._proxy;
if (!proxy) {
return;
}
@ -452,7 +454,7 @@ class ExtensionHostManager extends Disposable implements IExtensionHostManager {
* Waits until `start()` and only if it has extensions proceeds to really start.
*/
class LazyStartExtensionHostManager extends Disposable implements IExtensionHostManager {
public readonly kind: ExtensionHostKind;
public readonly onDidExit: Event<[number, string | null]>;
private readonly _onDidChangeResponsiveState: Emitter<ResponsiveState> = this._register(new Emitter<ResponsiveState>());
public readonly onDidChangeResponsiveState: Event<ResponsiveState> = this._onDidChangeResponsiveState.event;
@ -461,7 +463,12 @@ class LazyStartExtensionHostManager extends Disposable implements IExtensionHost
private _startCalled: Barrier;
private _actual: ExtensionHostManager | null;
public get kind(): ExtensionHostKind {
return this._extensionHost.runningLocation.kind;
}
constructor(
public readonly extensionHostId: string,
extensionHost: IExtensionHost,
private readonly _internalExtensionService: IInternalExtensionService,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@ -469,7 +476,6 @@ class LazyStartExtensionHostManager extends Disposable implements IExtensionHost
) {
super();
this._extensionHost = extensionHost;
this.kind = extensionHost.kind;
this.onDidExit = extensionHost.onExit;
this._startCalled = new Barrier();
this._actual = null;
@ -477,7 +483,7 @@ class LazyStartExtensionHostManager extends Disposable implements IExtensionHost
private _createActual(reason: string): ExtensionHostManager {
this._logService.info(`Creating lazy extension host: ${reason}`);
this._actual = this._register(this._instantiationService.createInstance(ExtensionHostManager, this._extensionHost, [], this._internalExtensionService));
this._actual = this._register(this._instantiationService.createInstance(ExtensionHostManager, this.extensionHostId, this._extensionHost, [], this._internalExtensionService));
this._register(this._actual.onDidChangeResponsiveState((e) => this._onDidChangeResponsiveState.fire(e)));
return this._actual;
}
@ -498,6 +504,9 @@ class LazyStartExtensionHostManager extends Disposable implements IExtensionHost
await this._actual.ready();
}
}
public representsRunningLocation(runningLocation: ExtensionRunningLocation): boolean {
return this._extensionHost.runningLocation.equals(runningLocation);
}
public async deltaExtensions(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise<void> {
await this._startCalled.wait();
const extensionHostAlreadyStarted = Boolean(this._actual);
@ -543,14 +552,21 @@ class LazyStartExtensionHostManager extends Disposable implements IExtensionHost
}
return 0;
}
public async resolveAuthority(remoteAuthority: string): Promise<ResolverResult> {
public async resolveAuthority(remoteAuthority: string, resolveAttempt: number): Promise<IResolveAuthorityResult> {
await this._startCalled.wait();
if (this._actual) {
return this._actual.resolveAuthority(remoteAuthority);
return this._actual.resolveAuthority(remoteAuthority, resolveAttempt);
}
throw new Error(`Cannot resolve authority`);
return {
type: 'error',
error: {
message: `Cannot resolve authority`,
code: RemoteAuthorityResolverErrorCode.Unknown,
detail: undefined
}
};
}
public async getCanonicalURI(remoteAuthority: string, uri: URI): Promise<URI> {
public async getCanonicalURI(remoteAuthority: string, uri: URI): Promise<URI | null> {
await this._startCalled.wait();
if (this._actual) {
return this._actual.getCanonicalURI(remoteAuthority, uri);

View file

@ -27,7 +27,10 @@ export type IResolveAuthorityResult = IResolveAuthorityErrorResult | IResolveAut
export interface IExtensionHostProxy {
resolveAuthority(remoteAuthority: string, resolveAttempt: number): Promise<IResolveAuthorityResult>;
getCanonicalURI(remoteAuthority: string, uri: URI): Promise<URI>;
/**
* Returns `null` if no resolver for `remoteAuthority` is found.
*/
getCanonicalURI(remoteAuthority: string, uri: URI): Promise<URI | null>;
startExtensionHost(enabledExtensionIds: ExtensionIdentifier[]): Promise<void>;
extensionTestsExecute(): Promise<number>;
extensionTestsExit(code: number): Promise<void>;

View file

@ -14,7 +14,7 @@ import * as types from 'vs/base/common/types';
import { URI } from 'vs/base/common/uri';
import { getGalleryExtensionId, groupByExtension, getExtensionId, ExtensionKey } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { isValidExtensionVersion } from 'vs/platform/extensions/common/extensionValidator';
import { ExtensionIdentifier, IExtensionDescription, UNDEFINED_PUBLISHER } from 'vs/platform/extensions/common/extensions';
import { ExtensionIdentifier, IExtensionDescription, TargetPlatform, UNDEFINED_PUBLISHER } from 'vs/platform/extensions/common/extensions';
const MANIFEST_FILE = 'package.json';
@ -133,10 +133,8 @@ class ExtensionManifestParser extends ExtensionManifestHandler {
if (json.getNodeType(manifest) !== 'object') {
this._error(this._absoluteFolderPath, nls.localize('jsonParseInvalidType', "Invalid manifest file {0}: Not an JSON object.", this._absoluteManifestPath));
} else if (errors.length === 0) {
if (manifest.__metadata) {
manifest.uuid = manifest.__metadata.id;
manifest.targetPlatform = manifest.__metadata.targetPlatform;
}
manifest.uuid = manifest.__metadata?.id;
manifest.targetPlatform = manifest.__metadata?.targetPlatform ?? TargetPlatform.UNDEFINED;
manifest.isUserBuiltin = !!manifest.__metadata?.isBuiltin;
delete manifest.__metadata;
return manifest;
@ -366,6 +364,7 @@ class ExtensionManifestNLSReplacer extends ExtensionManifestHandler {
export interface IRelaxedExtensionDescription {
id: string;
uuid?: string;
targetPlatform: TargetPlatform;
identifier: ExtensionIdentifier;
name: string;
version: string;

View file

@ -13,6 +13,7 @@ import { getExtensionId, getGalleryExtensionId } from 'vs/platform/extensionMana
import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc';
import { ApiProposalName } from 'vs/workbench/services/extensions/common/extensionsApiProposals';
import { IV8Profile } from 'vs/platform/profiling/common/profiling';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
export const nullExtensionDescription = Object.freeze(<IExtensionDescription>{
identifier: new ExtensionIdentifier('nullExtensionDescription'),
@ -36,31 +37,48 @@ export interface IMessage {
extensionPointId: string;
}
export const enum ExtensionRunningLocation {
None,
LocalProcess,
LocalWebWorker,
Remote
}
export function extensionRunningLocationToString(location: ExtensionRunningLocation) {
switch (location) {
case ExtensionRunningLocation.None:
return 'None';
case ExtensionRunningLocation.LocalProcess:
export class LocalProcessRunningLocation {
public readonly kind = ExtensionHostKind.LocalProcess;
constructor(
public readonly affinity: number
) { }
public equals(other: ExtensionRunningLocation) {
return (this.kind === other.kind && this.affinity === other.affinity);
}
public asString(): string {
if (this.affinity === 0) {
return 'LocalProcess';
case ExtensionRunningLocation.LocalWebWorker:
return 'LocalWebWorker';
case ExtensionRunningLocation.Remote:
return 'Remote';
}
return `LocalProcess${this.affinity}`;
}
}
export class LocalWebWorkerRunningLocation {
public readonly kind = ExtensionHostKind.LocalWebWorker;
public readonly affinity = 0;
public equals(other: ExtensionRunningLocation) {
return (this.kind === other.kind);
}
public asString(): string {
return 'LocalWebWorker';
}
}
export class RemoteRunningLocation {
public readonly kind = ExtensionHostKind.Remote;
public readonly affinity = 0;
public equals(other: ExtensionRunningLocation) {
return (this.kind === other.kind);
}
public asString(): string {
return 'Remote';
}
}
export type ExtensionRunningLocation = LocalProcessRunningLocation | LocalWebWorkerRunningLocation | RemoteRunningLocation;
export interface IExtensionsStatus {
messages: IMessage[];
activationTimes: ActivationTimes | undefined;
runtimeErrors: Error[];
runningLocation: ExtensionRunningLocation;
runningLocation: ExtensionRunningLocation | null;
}
export class MissingExtensionDependency {
@ -108,12 +126,15 @@ export interface IExtensionHostProfile {
}
export const enum ExtensionHostKind {
LocalProcess,
LocalWebWorker,
Remote
LocalProcess = 1,
LocalWebWorker = 2,
Remote = 3
}
export function extensionHostKindToString(kind: ExtensionHostKind): string {
export function extensionHostKindToString(kind: ExtensionHostKind | null): string {
if (kind === null) {
return 'None';
}
switch (kind) {
case ExtensionHostKind.LocalProcess: return 'LocalProcess';
case ExtensionHostKind.LocalWebWorker: return 'LocalWebWorker';
@ -122,9 +143,14 @@ export function extensionHostKindToString(kind: ExtensionHostKind): string {
}
export interface IExtensionHost {
readonly kind: ExtensionHostKind;
readonly runningLocation: ExtensionRunningLocation;
readonly remoteAuthority: string | null;
readonly lazyStart: boolean;
/**
* A collection of extensions that will execute or are executing on this extension host.
* **NOTE**: this will reflect extensions correctly only after `start()` resolves.
*/
readonly extensions: ExtensionDescriptionRegistry;
readonly onExit: Event<[number, string | null]>;
start(): Promise<IMessagePassingProtocol> | null;
@ -186,6 +212,8 @@ export interface IWillActivateEvent {
}
export interface IResponsiveStateChangeEvent {
extensionHostId: string;
extensionHostKind: ExtensionHostKind;
isResponsive: boolean;
}
@ -288,10 +316,15 @@ export interface IExtensionService {
getExtensionsStatus(): { [id: string]: IExtensionsStatus };
/**
* Return the inspect port or `0`, the latter means inspection
* is not possible.
* Return the inspect port or `0` for a certain extension host.
* `0` means inspection is not possible.
*/
getInspectPort(tryEnableInspector: boolean): Promise<number>;
getInspectPort(extensionHostId: string, tryEnableInspector: boolean): Promise<number>;
/**
* Return the inspect ports (if inspection is possible) for extension hosts of kind `extensionHostKind`.
*/
getInspectPorts(extensionHostKind: ExtensionHostKind, tryEnableInspector: boolean): Promise<number[]>;
/**
* Stops the extension hosts.
@ -340,6 +373,7 @@ export function toExtension(extensionDescription: IExtensionDescription): IExten
identifier: { id: getGalleryExtensionId(extensionDescription.publisher, extensionDescription.name), uuid: extensionDescription.uuid },
manifest: extensionDescription,
location: extensionDescription.extensionLocation,
targetPlatform: extensionDescription.targetPlatform,
};
}
@ -351,7 +385,8 @@ export function toExtensionDescription(extension: IExtension, isUnderDevelopment
isUnderDevelopment: !!isUnderDevelopment,
extensionLocation: extension.location,
...extension.manifest,
uuid: extension.identifier.uuid
uuid: extension.identifier.uuid,
targetPlatform: extension.targetPlatform
};
}
@ -370,7 +405,8 @@ export class NullExtensionService implements IExtensionService {
getExtension() { return Promise.resolve(undefined); }
readExtensionPointContributions<T>(_extPoint: IExtensionPoint<T>): Promise<ExtensionPointContribution<T>[]> { return Promise.resolve(Object.create(null)); }
getExtensionsStatus(): { [id: string]: IExtensionsStatus } { return Object.create(null); }
getInspectPort(_tryEnableInspector: boolean): Promise<number> { return Promise.resolve(0); }
getInspectPort(_extensionHostId: string, _tryEnableInspector: boolean): Promise<number> { return Promise.resolve(0); }
getInspectPorts(_extensionHostKind: ExtensionHostKind, _tryEnableInspector: boolean): Promise<number[]> { return Promise.resolve([]); }
stopExtensionHosts(): void { }
async restartExtensionHost(): Promise<void> { }
async startExtensionHosts(): Promise<void> { }

View file

@ -25,9 +25,10 @@ import { ISignService } from 'vs/platform/sign/common/sign';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
import { parseExtensionDevOptions } from 'vs/workbench/services/extensions/common/extensionDevOptions';
import { createMessageOfType, isMessageOfType, MessageType, IExtensionHostInitData, UIKind } from 'vs/workbench/services/extensions/common/extensionHostProtocol';
import { ExtensionHostKind, ExtensionHostLogFileName, IExtensionHost } from 'vs/workbench/services/extensions/common/extensions';
import { ExtensionHostLogFileName, IExtensionHost, RemoteRunningLocation } from 'vs/workbench/services/extensions/common/extensions';
import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle';
import { Extensions, IOutputChannelRegistry } from 'vs/workbench/services/output/common/output';
@ -49,9 +50,9 @@ export interface IRemoteExtensionHostDataProvider {
export class RemoteExtensionHost extends Disposable implements IExtensionHost {
public readonly kind = ExtensionHostKind.Remote;
public readonly remoteAuthority: string;
public readonly lazyStart = false;
public readonly extensions = new ExtensionDescriptionRegistry([]);
private _onExit: Emitter<[number, string | null]> = this._register(new Emitter<[number, string | null]>());
public readonly onExit: Event<[number, string | null]> = this._onExit.event;
@ -62,6 +63,7 @@ export class RemoteExtensionHost extends Disposable implements IExtensionHost {
private readonly _isExtensionDevHost: boolean;
constructor(
public readonly runningLocation: RemoteRunningLocation,
private readonly _initDataProvider: IRemoteExtensionHostDataProvider,
private readonly _socketFactory: ISocketFactory,
@IWorkspaceContextService private readonly _contextService: IWorkspaceContextService,
@ -222,6 +224,7 @@ export class RemoteExtensionHost extends Disposable implements IExtensionHost {
.filter(extension => (extension.main || extension.browser) && extension.api === 'none').map(extension => extension.identifier)
);
const workspace = this._contextService.getWorkspace();
this.extensions.deltaExtensions(remoteInitData.extensions, []);
return {
commit: this._productService.commit,
version: this._productService.version,
@ -251,7 +254,7 @@ export class RemoteExtensionHost extends Disposable implements IExtensionHost {
},
resolvedExtensions: resolvedExtensions,
hostExtensions: hostExtensions,
extensions: remoteInitData.extensions,
extensions: this.extensions.getAllExtensionDescriptions(),
telemetryInfo,
logLevel: this._logService.getLevel(),
logsLocation: remoteInitData.extensionHostLogsPath,

View file

@ -3,11 +3,11 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { LocalProcessExtensionHost } from 'vs/workbench/services/extensions/electron-browser/localProcessExtensionHost';
import { ILocalProcessExtensionHostDataProvider, LocalProcessExtensionHost } from 'vs/workbench/services/extensions/electron-browser/localProcessExtensionHost';
import { CachedExtensionScanner } from 'vs/workbench/services/extensions/electron-sandbox/cachedExtensionScanner';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { AbstractExtensionService, ExtensionRunningPreference, extensionRunningPreferenceToString } from 'vs/workbench/services/extensions/common/abstractExtensionService';
import { AbstractExtensionService, ExtensionRunningPreference, extensionRunningPreferenceToString, filterByRunningLocation } from 'vs/workbench/services/extensions/common/abstractExtensionService';
import * as nls from 'vs/nls';
import { runWhenIdle } from 'vs/base/common/async';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
@ -16,13 +16,13 @@ import { IWorkbenchExtensionEnablementService, EnablementState, IWebExtensionsSc
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IRemoteExtensionHostDataProvider, RemoteExtensionHost, IRemoteExtensionHostInitData } from 'vs/workbench/services/extensions/common/remoteExtensionHost';
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
import { IRemoteAuthorityResolverService, RemoteAuthorityResolverError, ResolverResult } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { IRemoteAuthorityResolverService, RemoteAuthorityResolverError, RemoteAuthorityResolverErrorCode, ResolverResult } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IHostService } from 'vs/workbench/services/host/browser/host';
import { IExtensionService, toExtension, ExtensionHostKind, IExtensionHost, webWorkerExtHostConfig, ExtensionRunningLocation, WebWorkerExtHostConfigValue, extensionRunningLocationToString, extensionHostKindToString } from 'vs/workbench/services/extensions/common/extensions';
import { IExtensionService, toExtension, ExtensionHostKind, IExtensionHost, webWorkerExtHostConfig, ExtensionRunningLocation, WebWorkerExtHostConfigValue, extensionHostKindToString } from 'vs/workbench/services/extensions/common/extensions';
import { IExtensionHostManager } from 'vs/workbench/services/extensions/common/extensionHostManager';
import { ExtensionIdentifier, IExtension, ExtensionType, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { ExtensionKind } from 'vs/platform/environment/common/environment';
@ -35,7 +35,7 @@ import { IRemoteExplorerService } from 'vs/workbench/services/remote/common/remo
import { Action2, registerAction2 } from 'vs/platform/actions/common/actions';
import { getRemoteName } from 'vs/platform/remote/common/remoteHosts';
import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment';
import { WebWorkerExtensionHost } from 'vs/workbench/services/extensions/browser/webWorkerExtensionHost';
import { IWebWorkerExtensionHostDataProvider, WebWorkerExtensionHost } from 'vs/workbench/services/extensions/browser/webWorkerExtensionHost';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { ILogService } from 'vs/platform/log/common/log';
import { CATEGORIES } from 'vs/workbench/common/actions';
@ -48,6 +48,8 @@ import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/w
import { CancellationToken } from 'vs/base/common/cancellation';
import { StopWatch } from 'vs/base/common/stopwatch';
import { isCI } from 'vs/base/common/platform';
import { IResolveAuthorityErrorResult } from 'vs/workbench/services/extensions/common/extensionHostProxy';
import { URI } from 'vs/base/common/uri';
export class ExtensionService extends AbstractExtensionService implements IExtensionService {
@ -56,6 +58,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten
private readonly _remoteInitData: Map<string, IRemoteExtensionHostInitData>;
private readonly _extensionScanner: CachedExtensionScanner;
private readonly _crashTracker = new ExtensionHostCrashTracker();
private _resolveAuthorityAttempt: number = 0;
constructor(
@IInstantiationService instantiationService: IInstantiationService,
@ -151,13 +154,13 @@ export class ExtensionService extends AbstractExtensionService implements IExten
]));
}
private _createLocalExtensionHostDataProvider(isInitialStart: boolean, desiredRunningLocation: ExtensionRunningLocation) {
private _createLocalExtensionHostDataProvider(isInitialStart: boolean, desiredRunningLocation: ExtensionRunningLocation): ILocalProcessExtensionHostDataProvider & IWebWorkerExtensionHostDataProvider {
return {
getInitData: async () => {
if (isInitialStart) {
// Here we load even extensions that would be disabled by workspace trust
const localExtensions = this._checkEnabledAndProposedAPI(await this._scanAllLocalExtensions(), /* ignore workspace trust */true);
const runningLocation = this._runningLocationClassifier.determineRunningLocation(localExtensions, []);
const runningLocation = this._determineRunningLocation(localExtensions);
const localProcessExtensions = filterByRunningLocation(localExtensions, runningLocation, desiredRunningLocation);
return {
autoStart: false,
@ -166,7 +169,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten
} else {
// restart case
const allExtensions = await this.getExtensions();
const localProcessExtensions = filterByRunningLocation(allExtensions, this._runningLocation, desiredRunningLocation);
const localProcessExtensions = this._filterByRunningLocation(allExtensions, desiredRunningLocation);
return {
autoStart: true,
extensions: localProcessExtensions
@ -186,69 +189,70 @@ export class ExtensionService extends AbstractExtensionService implements IExten
};
}
protected _pickRunningLocation(extensionId: ExtensionIdentifier, extensionKinds: ExtensionKind[], isInstalledLocally: boolean, isInstalledRemotely: boolean, preference: ExtensionRunningPreference): ExtensionRunningLocation {
const result = ExtensionService.pickRunningLocation(extensionKinds, isInstalledLocally, isInstalledRemotely, preference, Boolean(this._environmentService.remoteAuthority), this._enableLocalWebWorker);
this._logService.trace(`pickRunningLocation for ${extensionId.value}, extension kinds: [${extensionKinds.join(', ')}], isInstalledLocally: ${isInstalledLocally}, isInstalledRemotely: ${isInstalledRemotely}, preference: ${extensionRunningPreferenceToString(preference)} => ${extensionRunningLocationToString(result)}`);
protected _pickExtensionHostKind(extensionId: ExtensionIdentifier, extensionKinds: ExtensionKind[], isInstalledLocally: boolean, isInstalledRemotely: boolean, preference: ExtensionRunningPreference): ExtensionHostKind | null {
const result = ExtensionService.pickExtensionHostKind(extensionKinds, isInstalledLocally, isInstalledRemotely, preference, Boolean(this._environmentService.remoteAuthority), this._enableLocalWebWorker);
this._logService.trace(`pickRunningLocation for ${extensionId.value}, extension kinds: [${extensionKinds.join(', ')}], isInstalledLocally: ${isInstalledLocally}, isInstalledRemotely: ${isInstalledRemotely}, preference: ${extensionRunningPreferenceToString(preference)} => ${extensionHostKindToString(result)}`);
return result;
}
public static pickRunningLocation(extensionKinds: ExtensionKind[], isInstalledLocally: boolean, isInstalledRemotely: boolean, preference: ExtensionRunningPreference, hasRemoteExtHost: boolean, hasWebWorkerExtHost: boolean): ExtensionRunningLocation {
const result: ExtensionRunningLocation[] = [];
public static pickExtensionHostKind(extensionKinds: ExtensionKind[], isInstalledLocally: boolean, isInstalledRemotely: boolean, preference: ExtensionRunningPreference, hasRemoteExtHost: boolean, hasWebWorkerExtHost: boolean): ExtensionHostKind | null {
const result: ExtensionHostKind[] = [];
for (const extensionKind of extensionKinds) {
if (extensionKind === 'ui' && isInstalledLocally) {
// ui extensions run locally if possible
if (preference === ExtensionRunningPreference.None || preference === ExtensionRunningPreference.Local) {
return ExtensionRunningLocation.LocalProcess;
return ExtensionHostKind.LocalProcess;
} else {
result.push(ExtensionRunningLocation.LocalProcess);
result.push(ExtensionHostKind.LocalProcess);
}
}
if (extensionKind === 'workspace' && isInstalledRemotely) {
// workspace extensions run remotely if possible
if (preference === ExtensionRunningPreference.None || preference === ExtensionRunningPreference.Remote) {
return ExtensionRunningLocation.Remote;
return ExtensionHostKind.Remote;
} else {
result.push(ExtensionRunningLocation.Remote);
result.push(ExtensionHostKind.Remote);
}
}
if (extensionKind === 'workspace' && !hasRemoteExtHost) {
// workspace extensions also run locally if there is no remote
if (preference === ExtensionRunningPreference.None || preference === ExtensionRunningPreference.Local) {
return ExtensionRunningLocation.LocalProcess;
return ExtensionHostKind.LocalProcess;
} else {
result.push(ExtensionRunningLocation.LocalProcess);
result.push(ExtensionHostKind.LocalProcess);
}
}
if (extensionKind === 'web' && isInstalledLocally && hasWebWorkerExtHost) {
// web worker extensions run in the local web worker if possible
if (preference === ExtensionRunningPreference.None || preference === ExtensionRunningPreference.Local) {
return ExtensionRunningLocation.LocalWebWorker;
return ExtensionHostKind.LocalWebWorker;
} else {
result.push(ExtensionRunningLocation.LocalWebWorker);
result.push(ExtensionHostKind.LocalWebWorker);
}
}
}
return (result.length > 0 ? result[0] : ExtensionRunningLocation.None);
return (result.length > 0 ? result[0] : null);
}
protected _createExtensionHosts(isInitialStart: boolean): IExtensionHost[] {
const result: IExtensionHost[] = [];
const localProcessExtHost = this._instantiationService.createInstance(LocalProcessExtensionHost, this._createLocalExtensionHostDataProvider(isInitialStart, ExtensionRunningLocation.LocalProcess));
result.push(localProcessExtHost);
if (this._enableLocalWebWorker) {
const webWorkerExtHost = this._instantiationService.createInstance(WebWorkerExtensionHost, this._lazyLocalWebWorker, this._createLocalExtensionHostDataProvider(isInitialStart, ExtensionRunningLocation.LocalWebWorker));
result.push(webWorkerExtHost);
protected _createExtensionHost(runningLocation: ExtensionRunningLocation, isInitialStart: boolean): IExtensionHost | null {
switch (runningLocation.kind) {
case ExtensionHostKind.LocalProcess: {
return this._instantiationService.createInstance(LocalProcessExtensionHost, runningLocation, this._createLocalExtensionHostDataProvider(isInitialStart, runningLocation));
}
case ExtensionHostKind.LocalWebWorker: {
if (this._enableLocalWebWorker) {
return this._instantiationService.createInstance(WebWorkerExtensionHost, runningLocation, this._lazyLocalWebWorker, this._createLocalExtensionHostDataProvider(isInitialStart, runningLocation));
}
return null;
}
case ExtensionHostKind.Remote: {
const remoteAgentConnection = this._remoteAgentService.getConnection();
if (remoteAgentConnection) {
return this._instantiationService.createInstance(RemoteExtensionHost, runningLocation, this._createRemoteExtensionHostDataProvider(remoteAgentConnection.remoteAuthority), this._remoteAgentService.socketFactory);
}
return null;
}
}
const remoteAgentConnection = this._remoteAgentService.getConnection();
if (remoteAgentConnection) {
const remoteExtHost = this._instantiationService.createInstance(RemoteExtensionHost, this._createRemoteExtensionHostDataProvider(remoteAgentConnection.remoteAuthority), this._remoteAgentService.socketFactory);
result.push(remoteExtHost);
}
return result;
}
protected override _onExtensionHostCrashed(extensionHost: IExtensionHostManager, code: number, signal: string | null): void {
@ -340,18 +344,88 @@ export class ExtensionService extends AbstractExtensionService implements IExten
// --- impl
private async _resolveAuthority(remoteAuthority: string): Promise<ResolverResult> {
const authorityPlusIndex = remoteAuthority.indexOf('+');
if (authorityPlusIndex === -1) {
// This authority does not need to be resolved, simply parse the port number
const lastColon = remoteAuthority.lastIndexOf(':');
return {
authority: {
authority: remoteAuthority,
host: remoteAuthority.substring(0, lastColon),
port: parseInt(remoteAuthority.substring(lastColon + 1), 10),
connectionToken: undefined
}
};
}
const localProcessExtensionHosts = this._getExtensionHostManagers(ExtensionHostKind.LocalProcess);
if (localProcessExtensionHosts.length === 0) {
// no local process extension hosts
throw new Error(`Cannot resolve authority`);
}
this._resolveAuthorityAttempt++;
const results = await Promise.all(localProcessExtensionHosts.map(extHost => extHost.resolveAuthority(remoteAuthority, this._resolveAuthorityAttempt)));
let bestErrorResult: IResolveAuthorityErrorResult | null = null;
for (const result of results) {
if (result.type === 'ok') {
return result.value;
}
if (!bestErrorResult) {
bestErrorResult = result;
continue;
}
const bestErrorIsUnknown = (bestErrorResult.error.code === RemoteAuthorityResolverErrorCode.Unknown);
const errorIsUnknown = (result.error.code === RemoteAuthorityResolverErrorCode.Unknown);
if (bestErrorIsUnknown && !errorIsUnknown) {
bestErrorResult = result;
}
}
// we can only reach this if there is an error
throw new RemoteAuthorityResolverError(bestErrorResult!.error.message, bestErrorResult!.error.code, bestErrorResult!.error.detail);
}
private async _getCanonicalURI(remoteAuthority: string, uri: URI): Promise<URI> {
const authorityPlusIndex = remoteAuthority.indexOf('+');
if (authorityPlusIndex === -1) {
// This authority does not use a resolver
return uri;
}
const localProcessExtensionHosts = this._getExtensionHostManagers(ExtensionHostKind.LocalProcess);
if (localProcessExtensionHosts.length === 0) {
// no local process extension hosts
throw new Error(`Cannot resolve canonical URI`);
}
const results = await Promise.all(localProcessExtensionHosts.map(extHost => extHost.getCanonicalURI(remoteAuthority, uri)));
for (const result of results) {
if (result) {
return result;
}
}
// we can only reach this if there was no resolver extension that can return the cannonical uri
throw new Error(`Cannot get canonical URI because no extension is installed to resolve ${getRemoteAuthorityPrefix(remoteAuthority)}`);
}
private async _resolveAuthorityAgain(): Promise<void> {
const remoteAuthority = this._environmentService.remoteAuthority;
if (!remoteAuthority) {
return;
}
const localProcessExtensionHost = this._getExtensionHostManager(ExtensionHostKind.LocalProcess)!;
this._remoteAuthorityResolverService._clearResolvedAuthority(remoteAuthority);
const sw = StopWatch.create(false);
this._logService.info(`Invoking resolveAuthority(${getRemoteAuthorityPrefix(remoteAuthority)})`);
try {
const result = await localProcessExtensionHost.resolveAuthority(remoteAuthority);
const result = await this._resolveAuthority(remoteAuthority);
this._logService.info(`resolveAuthority(${getRemoteAuthorityPrefix(remoteAuthority)}) returned '${result.authority.host}:${result.authority.port}' after ${sw.elapsed()} ms`);
this._remoteAuthorityResolverService._setResolvedAuthority(result.authority, result.options);
} catch (err) {
@ -364,7 +438,6 @@ export class ExtensionService extends AbstractExtensionService implements IExten
this._extensionScanner.startScanningExtensions(this.createLogger());
const remoteAuthority = this._environmentService.remoteAuthority;
const localProcessExtensionHost = this._getExtensionHostManager(ExtensionHostKind.LocalProcess)!;
let remoteEnv: IRemoteAgentEnvironment | null = null;
let remoteExtensions: IExtensionDescription[] = [];
@ -376,12 +449,11 @@ export class ExtensionService extends AbstractExtensionService implements IExten
// The current remote authority resolver cannot give the canonical URI for this URI
return uri;
}
const localProcessExtensionHost = this._getExtensionHostManager(ExtensionHostKind.LocalProcess)!;
if (isCI) {
this._logService.info(`Invoking getCanonicalURI for authority ${getRemoteAuthorityPrefix(remoteAuthority)}...`);
}
try {
return localProcessExtensionHost.getCanonicalURI(remoteAuthority, uri);
return this._getCanonicalURI(remoteAuthority, uri);
} finally {
if (isCI) {
this._logService.info(`getCanonicalURI returned for authority ${getRemoteAuthorityPrefix(remoteAuthority)}.`);
@ -407,7 +479,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten
const sw = StopWatch.create(false);
this._logService.info(`Invoking resolveAuthority(${getRemoteAuthorityPrefix(remoteAuthority)})`);
try {
resolverResult = await localProcessExtensionHost.resolveAuthority(remoteAuthority);
resolverResult = await this._resolveAuthority(remoteAuthority);
this._logService.info(`resolveAuthority(${getRemoteAuthorityPrefix(remoteAuthority)}) returned '${resolverResult.authority.host}:${resolverResult.authority.port}' after ${sw.elapsed()} ms`);
} catch (err) {
this._logService.error(`resolveAuthority(${getRemoteAuthorityPrefix(remoteAuthority)}) returned an error after ${sw.elapsed()} ms`, err);
@ -471,12 +543,12 @@ export class ExtensionService extends AbstractExtensionService implements IExten
remoteExtensions = this._checkEnabledAndProposedAPI(remoteExtensions, false);
const localExtensions = this._checkEnabledAndProposedAPI(await this._scanAllLocalExtensions(), false);
this._runningLocation = this._runningLocationClassifier.determineRunningLocation(localExtensions, remoteExtensions);
this._initializeRunningLocation(localExtensions, remoteExtensions);
// remove non-UI extensions from the local extensions
const localProcessExtensions = filterByRunningLocation(localExtensions, this._runningLocation, ExtensionRunningLocation.LocalProcess);
const localWebWorkerExtensions = filterByRunningLocation(localExtensions, this._runningLocation, ExtensionRunningLocation.LocalWebWorker);
remoteExtensions = filterByRunningLocation(remoteExtensions, this._runningLocation, ExtensionRunningLocation.Remote);
const localProcessExtensions = this._filterByExtensionHostKind(localExtensions, ExtensionHostKind.LocalProcess);
const localWebWorkerExtensions = this._filterByExtensionHostKind(localExtensions, ExtensionHostKind.LocalWebWorker);
remoteExtensions = this._filterByExtensionHostKind(remoteExtensions, ExtensionHostKind.Remote);
const result = this._registry.deltaExtensions(remoteExtensions.concat(localProcessExtensions).concat(localWebWorkerExtensions), []);
if (result.removedDueToLooping.length > 0) {
@ -498,23 +570,22 @@ export class ExtensionService extends AbstractExtensionService implements IExten
this._doHandleExtensionPoints(this._registry.getAllExtensionDescriptions());
const localProcessExtensionHost = this._getExtensionHostManager(ExtensionHostKind.LocalProcess);
if (localProcessExtensionHost) {
localProcessExtensionHost.start(localProcessExtensions.map(extension => extension.identifier).filter(id => this._registry.containsExtension(id)));
const localProcessExtensionHosts = this._getExtensionHostManagers(ExtensionHostKind.LocalProcess);
const filteredLocalProcessExtensions = localProcessExtensions.filter(extension => this._registry.containsExtension(extension.identifier));
for (const extHost of localProcessExtensionHosts) {
this._startExtensionHost(extHost, filteredLocalProcessExtensions);
}
const localWebWorkerExtensionHost = this._getExtensionHostManager(ExtensionHostKind.LocalWebWorker);
if (localWebWorkerExtensionHost) {
localWebWorkerExtensionHost.start(localWebWorkerExtensions.map(extension => extension.identifier).filter(id => this._registry.containsExtension(id)));
const localWebWorkerExtensionHosts = this._getExtensionHostManagers(ExtensionHostKind.LocalWebWorker);
const filteredLocalWebWorkerExtensions = localWebWorkerExtensions.filter(extension => this._registry.containsExtension(extension.identifier));
for (const extHost of localWebWorkerExtensionHosts) {
this._startExtensionHost(extHost, filteredLocalWebWorkerExtensions);
}
}
public override async getInspectPort(tryEnableInspector: boolean): Promise<number> {
const localProcessExtensionHost = this._getExtensionHostManager(ExtensionHostKind.LocalProcess);
if (localProcessExtensionHost) {
return localProcessExtensionHost.getInspectPort(tryEnableInspector);
}
return 0;
private _startExtensionHost(extensionHostManager: IExtensionHostManager, _extensions: IExtensionDescription[]): void {
const extensions = this._filterByExtensionHostManager(_extensions, extensionHostManager);
extensionHostManager.start(extensions.map(extension => extension.identifier));
}
public _onExtensionHostExit(code: number): void {
@ -630,10 +701,6 @@ function getRemoteAuthorityPrefix(remoteAuthority: string): string {
return remoteAuthority.substring(0, plusIndex);
}
function filterByRunningLocation(extensions: IExtensionDescription[], runningLocation: Map<string, ExtensionRunningLocation>, desiredRunningLocation: ExtensionRunningLocation): IExtensionDescription[] {
return extensions.filter(ext => runningLocation.get(ExtensionIdentifier.toKey(ext.identifier)) === desiredRunningLocation);
}
registerSingleton(IExtensionService, ExtensionService);
class RestartExtensionHostAction extends Action2 {

View file

@ -34,7 +34,7 @@ import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'
import { parseExtensionDevOptions } from '../common/extensionDevOptions';
import { VSBuffer } from 'vs/base/common/buffer';
import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug';
import { IExtensionHost, ExtensionHostLogFileName, ExtensionHostKind } from 'vs/workbench/services/extensions/common/extensions';
import { IExtensionHost, ExtensionHostLogFileName, LocalProcessRunningLocation } from 'vs/workbench/services/extensions/common/extensions';
import { IHostService } from 'vs/workbench/services/host/browser/host';
import { joinPath } from 'vs/base/common/resources';
import { Registry } from 'vs/platform/registry/common/platform';
@ -44,6 +44,7 @@ import { IExtensionHostProcessOptions, IExtensionHostStarter } from 'vs/platform
import { SerializedError } from 'vs/base/common/errors';
import { removeDangerousEnvVariables } from 'vs/base/node/processes';
import { StopWatch } from 'vs/base/common/stopwatch';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
export interface ILocalProcessExtensionHostInitData {
readonly autoStart: boolean;
@ -105,9 +106,9 @@ class ExtensionHostProcess {
export class LocalProcessExtensionHost implements IExtensionHost {
public readonly kind = ExtensionHostKind.LocalProcess;
public readonly remoteAuthority = null;
public readonly lazyStart = false;
public readonly extensions = new ExtensionDescriptionRegistry([]);
private readonly _onExit: Emitter<[number, string]> = new Emitter<[number, string]>();
public readonly onExit: Event<[number, string]> = this._onExit.event;
@ -135,6 +136,7 @@ export class LocalProcessExtensionHost implements IExtensionHost {
private readonly _extensionHostLogFile: URI;
constructor(
public readonly runningLocation: LocalProcessRunningLocation,
private readonly _initDataProvider: ILocalProcessExtensionHostDataProvider,
@IWorkspaceContextService private readonly _contextService: IWorkspaceContextService,
@INotificationService private readonly _notificationService: INotificationService,
@ -502,6 +504,7 @@ export class LocalProcessExtensionHost implements IExtensionHost {
private async _createExtHostInitData(): Promise<IExtensionHostInitData> {
const [telemetryInfo, initData] = await Promise.all([this._telemetryService.getTelemetryInfo(), this._initDataProvider.getInitData()]);
const workspace = this._contextService.getWorkspace();
this.extensions.deltaExtensions(initData.extensions, []);
return {
commit: this._productService.commit,
version: this._productService.version,
@ -532,7 +535,7 @@ export class LocalProcessExtensionHost implements IExtensionHost {
},
resolvedExtensions: [],
hostExtensions: [],
extensions: initData.extensions,
extensions: this.extensions.getAllExtensionDescriptions(),
telemetryInfo,
logLevel: this._logService.getLevel(),
logsLocation: this._environmentService.extHostLogsPath,

View file

@ -6,84 +6,84 @@
import * as assert from 'assert';
import { ExtensionService as BrowserExtensionService } from 'vs/workbench/services/extensions/browser/extensionService';
import { ExtensionRunningPreference } from 'vs/workbench/services/extensions/common/abstractExtensionService';
import { ExtensionRunningLocation } from 'vs/workbench/services/extensions/common/extensions';
import { ExtensionHostKind } from 'vs/workbench/services/extensions/common/extensions';
suite('BrowserExtensionService', () => {
test('pickRunningLocation', () => {
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation([], false, false, ExtensionRunningPreference.None), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation([], false, true, ExtensionRunningPreference.None), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation([], true, false, ExtensionRunningPreference.None), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation([], true, true, ExtensionRunningPreference.None), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation([], false, false, ExtensionRunningPreference.None), null);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation([], false, true, ExtensionRunningPreference.None), null);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation([], true, false, ExtensionRunningPreference.None), null);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation([], true, true, ExtensionRunningPreference.None), null);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui'], false, false, ExtensionRunningPreference.None), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui'], false, true, ExtensionRunningPreference.None), ExtensionRunningLocation.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui'], true, false, ExtensionRunningPreference.None), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui'], true, true, ExtensionRunningPreference.None), ExtensionRunningLocation.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui'], false, false, ExtensionRunningPreference.None), null);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui'], false, true, ExtensionRunningPreference.None), ExtensionHostKind.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui'], true, false, ExtensionRunningPreference.None), null);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui'], true, true, ExtensionRunningPreference.None), ExtensionHostKind.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace'], false, false, ExtensionRunningPreference.None), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace'], false, true, ExtensionRunningPreference.None), ExtensionRunningLocation.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace'], true, false, ExtensionRunningPreference.None), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace'], true, true, ExtensionRunningPreference.None), ExtensionRunningLocation.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace'], false, false, ExtensionRunningPreference.None), null);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace'], false, true, ExtensionRunningPreference.None), ExtensionHostKind.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace'], true, false, ExtensionRunningPreference.None), null);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace'], true, true, ExtensionRunningPreference.None), ExtensionHostKind.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web'], false, false, ExtensionRunningPreference.None), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web'], false, true, ExtensionRunningPreference.None), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web'], true, false, ExtensionRunningPreference.None), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web'], true, true, ExtensionRunningPreference.None), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web'], false, false, ExtensionRunningPreference.None), null);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web'], false, true, ExtensionRunningPreference.None), ExtensionHostKind.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web'], true, false, ExtensionRunningPreference.None), ExtensionHostKind.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web'], true, true, ExtensionRunningPreference.None), ExtensionHostKind.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace'], false, false, ExtensionRunningPreference.None), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace'], false, true, ExtensionRunningPreference.None), ExtensionRunningLocation.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace'], true, false, ExtensionRunningPreference.None), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace'], true, true, ExtensionRunningPreference.None), ExtensionRunningLocation.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui'], false, false, ExtensionRunningPreference.None), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui'], false, true, ExtensionRunningPreference.None), ExtensionRunningLocation.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui'], true, false, ExtensionRunningPreference.None), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui'], true, true, ExtensionRunningPreference.None), ExtensionRunningLocation.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace'], false, false, ExtensionRunningPreference.None), null);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace'], false, true, ExtensionRunningPreference.None), ExtensionHostKind.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace'], true, false, ExtensionRunningPreference.None), null);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace'], true, true, ExtensionRunningPreference.None), ExtensionHostKind.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui'], false, false, ExtensionRunningPreference.None), null);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui'], false, true, ExtensionRunningPreference.None), ExtensionHostKind.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui'], true, false, ExtensionRunningPreference.None), null);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui'], true, true, ExtensionRunningPreference.None), ExtensionHostKind.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace'], false, false, ExtensionRunningPreference.None), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace'], false, true, ExtensionRunningPreference.None), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace'], true, false, ExtensionRunningPreference.None), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace'], true, true, ExtensionRunningPreference.None), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web'], false, false, ExtensionRunningPreference.None), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web'], false, true, ExtensionRunningPreference.None), ExtensionRunningLocation.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web'], true, false, ExtensionRunningPreference.None), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web'], true, true, ExtensionRunningPreference.None), ExtensionRunningLocation.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace'], false, false, ExtensionRunningPreference.None), null);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace'], false, true, ExtensionRunningPreference.None), ExtensionHostKind.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace'], true, false, ExtensionRunningPreference.None), ExtensionHostKind.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace'], true, true, ExtensionRunningPreference.None), ExtensionHostKind.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web'], false, false, ExtensionRunningPreference.None), null);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web'], false, true, ExtensionRunningPreference.None), ExtensionHostKind.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web'], true, false, ExtensionRunningPreference.None), ExtensionHostKind.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web'], true, true, ExtensionRunningPreference.None), ExtensionHostKind.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web'], false, false, ExtensionRunningPreference.None), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web'], false, true, ExtensionRunningPreference.None), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web'], true, false, ExtensionRunningPreference.None), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web'], true, true, ExtensionRunningPreference.None), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui'], false, false, ExtensionRunningPreference.None), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui'], false, true, ExtensionRunningPreference.None), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui'], true, false, ExtensionRunningPreference.None), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui'], true, true, ExtensionRunningPreference.None), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web'], false, false, ExtensionRunningPreference.None), null);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web'], false, true, ExtensionRunningPreference.None), ExtensionHostKind.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web'], true, false, ExtensionRunningPreference.None), ExtensionHostKind.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web'], true, true, ExtensionRunningPreference.None), ExtensionHostKind.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui'], false, false, ExtensionRunningPreference.None), null);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui'], false, true, ExtensionRunningPreference.None), ExtensionHostKind.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui'], true, false, ExtensionRunningPreference.None), ExtensionHostKind.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui'], true, true, ExtensionRunningPreference.None), ExtensionHostKind.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web', 'workspace'], false, false, ExtensionRunningPreference.None), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web', 'workspace'], false, true, ExtensionRunningPreference.None), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web', 'workspace'], true, false, ExtensionRunningPreference.None), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web', 'workspace'], true, true, ExtensionRunningPreference.None), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace', 'web'], false, false, ExtensionRunningPreference.None), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace', 'web'], false, true, ExtensionRunningPreference.None), ExtensionRunningLocation.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace', 'web'], true, false, ExtensionRunningPreference.None), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace', 'web'], true, true, ExtensionRunningPreference.None), ExtensionRunningLocation.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web', 'workspace'], false, false, ExtensionRunningPreference.None), null);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web', 'workspace'], false, true, ExtensionRunningPreference.None), ExtensionHostKind.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web', 'workspace'], true, false, ExtensionRunningPreference.None), ExtensionHostKind.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'web', 'workspace'], true, true, ExtensionRunningPreference.None), ExtensionHostKind.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace', 'web'], false, false, ExtensionRunningPreference.None), null);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace', 'web'], false, true, ExtensionRunningPreference.None), ExtensionHostKind.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace', 'web'], true, false, ExtensionRunningPreference.None), ExtensionHostKind.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['ui', 'workspace', 'web'], true, true, ExtensionRunningPreference.None), ExtensionHostKind.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui', 'workspace'], false, false, ExtensionRunningPreference.None), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui', 'workspace'], false, true, ExtensionRunningPreference.None), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui', 'workspace'], true, false, ExtensionRunningPreference.None), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui', 'workspace'], true, true, ExtensionRunningPreference.None), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace', 'ui'], false, false, ExtensionRunningPreference.None), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace', 'ui'], false, true, ExtensionRunningPreference.None), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace', 'ui'], true, false, ExtensionRunningPreference.None), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace', 'ui'], true, true, ExtensionRunningPreference.None), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui', 'workspace'], false, false, ExtensionRunningPreference.None), null);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui', 'workspace'], false, true, ExtensionRunningPreference.None), ExtensionHostKind.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui', 'workspace'], true, false, ExtensionRunningPreference.None), ExtensionHostKind.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'ui', 'workspace'], true, true, ExtensionRunningPreference.None), ExtensionHostKind.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace', 'ui'], false, false, ExtensionRunningPreference.None), null);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace', 'ui'], false, true, ExtensionRunningPreference.None), ExtensionHostKind.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace', 'ui'], true, false, ExtensionRunningPreference.None), ExtensionHostKind.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['web', 'workspace', 'ui'], true, true, ExtensionRunningPreference.None), ExtensionHostKind.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui', 'web'], false, false, ExtensionRunningPreference.None), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui', 'web'], false, true, ExtensionRunningPreference.None), ExtensionRunningLocation.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui', 'web'], true, false, ExtensionRunningPreference.None), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui', 'web'], true, true, ExtensionRunningPreference.None), ExtensionRunningLocation.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web', 'ui'], false, false, ExtensionRunningPreference.None), ExtensionRunningLocation.None);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web', 'ui'], false, true, ExtensionRunningPreference.None), ExtensionRunningLocation.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web', 'ui'], true, false, ExtensionRunningPreference.None), ExtensionRunningLocation.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web', 'ui'], true, true, ExtensionRunningPreference.None), ExtensionRunningLocation.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui', 'web'], false, false, ExtensionRunningPreference.None), null);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui', 'web'], false, true, ExtensionRunningPreference.None), ExtensionHostKind.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui', 'web'], true, false, ExtensionRunningPreference.None), ExtensionHostKind.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'ui', 'web'], true, true, ExtensionRunningPreference.None), ExtensionHostKind.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web', 'ui'], false, false, ExtensionRunningPreference.None), null);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web', 'ui'], false, true, ExtensionRunningPreference.None), ExtensionHostKind.Remote);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web', 'ui'], true, false, ExtensionRunningPreference.None), ExtensionHostKind.LocalWebWorker);
assert.deepStrictEqual(BrowserExtensionService.pickRunningLocation(['workspace', 'web', 'ui'], true, true, ExtensionRunningPreference.None), ExtensionHostKind.Remote);
});
});

View file

@ -11,11 +11,11 @@ import { Disposable } from 'vs/base/common/lifecycle';
import { Event, Emitter } from 'vs/base/common/event';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { URI } from 'vs/base/common/uri';
import { move } from 'vs/base/common/arrays';
import { coalesce, move } from 'vs/base/common/arrays';
import { isUndefined, isUndefinedOrNull } from 'vs/base/common/types';
import { isEqual } from 'vs/base/common/resources';
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
import { groupBy } from 'vs/base/common/collections';
import { IStringDictionary } from 'vs/base/common/collections';
export function getViewsStateStorageId(viewContainerStorageId: string): string { return `${viewContainerStorageId}.hidden`; }
@ -109,9 +109,9 @@ class ViewDescriptorsState extends Disposable {
}
private updateWorkspaceState(viewDescriptors: ReadonlyArray<IViewDescriptor>): void {
const storedViewsStates: { [id: string]: IStoredWorkspaceViewState } = JSON.parse(this.storageService.get(this.workspaceViewsStateStorageId, StorageScope.WORKSPACE, '{}'));
const storedViewsStates = this.getStoredWorkspaceState();
for (const viewDescriptor of viewDescriptors) {
const viewState = this.state.get(viewDescriptor.id);
const viewState = this.get(viewDescriptor.id);
if (viewState) {
storedViewsStates[viewDescriptor.id] = {
collapsed: !!viewState.collapsed,
@ -132,7 +132,7 @@ class ViewDescriptorsState extends Disposable {
private updateGlobalState(viewDescriptors: ReadonlyArray<IViewDescriptor>): void {
const storedGlobalState = this.getStoredGlobalState();
for (const viewDescriptor of viewDescriptors) {
const state = this.state.get(viewDescriptor.id);
const state = this.get(viewDescriptor.id);
storedGlobalState.set(viewDescriptor.id, {
id: viewDescriptor.id,
isHidden: state && viewDescriptor.canToggleVisibility ? !state.visibleGlobal : false,
@ -147,13 +147,24 @@ class ViewDescriptorsState extends Disposable {
&& this.globalViewsStatesValue !== this.getStoredGlobalViewsStatesValue() /* This checks if current window changed the value or not */) {
this._globalViewsStatesValue = undefined;
const storedViewsVisibilityStates = this.getStoredGlobalState();
const storedWorkspaceViewsStates = this.getStoredWorkspaceState();
const changedStates: { id: string; visible: boolean }[] = [];
for (const [id, storedState] of storedViewsVisibilityStates) {
const state = this.state.get(id);
const state = this.get(id);
if (state) {
if (state.visibleGlobal !== !storedState.isHidden) {
changedStates.push({ id, visible: !storedState.isHidden });
}
} else {
const workspaceViewState = <IStoredWorkspaceViewState | undefined>storedWorkspaceViewsStates[id];
this.set(id, {
active: false,
visibleGlobal: !storedState.isHidden,
visibleWorkspace: isUndefined(workspaceViewState?.isHidden) ? undefined : !workspaceViewState?.isHidden,
collapsed: workspaceViewState?.collapsed,
order: workspaceViewState?.order,
size: workspaceViewState?.size,
});
}
}
if (changedStates.length) {
@ -164,7 +175,7 @@ class ViewDescriptorsState extends Disposable {
private initialize(): Map<string, IViewDescriptorState> {
const viewStates = new Map<string, IViewDescriptorState>();
const workspaceViewsStates = <{ [id: string]: IStoredWorkspaceViewState }>JSON.parse(this.storageService.get(this.workspaceViewsStateStorageId, StorageScope.WORKSPACE, '{}'));
const workspaceViewsStates = this.getStoredWorkspaceState();
for (const id of Object.keys(workspaceViewsStates)) {
const workspaceViewState = workspaceViewsStates[id];
viewStates.set(id, {
@ -224,6 +235,10 @@ class ViewDescriptorsState extends Disposable {
return viewStates;
}
private getStoredWorkspaceState(): IStringDictionary<IStoredWorkspaceViewState> {
return JSON.parse(this.storageService.get(this.workspaceViewsStateStorageId, StorageScope.WORKSPACE, '{}'));
}
private getStoredGlobalState(): Map<string, IStoredGlobalViewState> {
return this.parseStoredGlobalState(this.globalViewsStatesValue).state;
}
@ -389,61 +404,61 @@ export class ViewContainerModel extends Disposable implements IViewContainerMode
return this.isViewDescriptorVisible(viewDescriptorItem);
}
setVisible(id: string, visible: boolean, size?: number): void {
this.updateVisibility([{ id, visible, size }]);
setVisible(id: string, visible: boolean): void {
this.updateVisibility([{ id, visible }]);
}
private updateVisibility(viewDescriptors: { id: string; visible: boolean; size?: number }[]): void {
const { toBeAdded, toBeRemoved } = groupBy(viewDescriptors, viewDescriptor => viewDescriptor.visible ? 'toBeAdded' : 'toBeRemoved');
const updateVisibility = (viewDescriptors: { id: string; visible: boolean; size?: number }[]): { viewDescriptorItem: IViewDescriptorItem; visibleIndex: number }[] => {
const result: { viewDescriptorItem: IViewDescriptorItem; visibleIndex: number }[] = [];
for (const { id, visible, size } of viewDescriptors) {
const foundViewDescriptor = this.findAndIgnoreIfNotFound(id);
if (!foundViewDescriptor) {
continue;
}
const { viewDescriptorItem, visibleIndex } = foundViewDescriptor;
const viewDescriptor = viewDescriptorItem.viewDescriptor;
if (!viewDescriptor.canToggleVisibility) {
continue;
}
if (this.isViewDescriptorVisibleWhenActive(viewDescriptorItem) === visible) {
continue;
}
if (viewDescriptor.workspace) {
viewDescriptorItem.state.visibleWorkspace = visible;
} else {
viewDescriptorItem.state.visibleGlobal = visible;
}
if (typeof viewDescriptorItem.state.size === 'number') {
viewDescriptorItem.state.size = size;
}
if (this.isViewDescriptorVisible(viewDescriptorItem) !== visible) {
// do not add events if visibility is not changed
continue;
}
result.push({ viewDescriptorItem, visibleIndex });
private updateVisibility(viewDescriptors: { id: string; visible: boolean }[]): void {
// First: Update and remove the view descriptors which are asked to be hidden
const viewDescriptorItemsToHide = coalesce(viewDescriptors.filter(({ visible }) => !visible)
.map(({ id }) => this.findAndIgnoreIfNotFound(id)));
const removed: IViewDescriptorRef[] = [];
for (const { viewDescriptorItem, visibleIndex } of viewDescriptorItemsToHide) {
if (this.updateViewDescriptorItemVisibility(viewDescriptorItem, false)) {
removed.push({ viewDescriptor: viewDescriptorItem.viewDescriptor, index: visibleIndex });
}
return result;
};
if (toBeRemoved?.length) {
const removedVisibleDescriptors = updateVisibility(toBeRemoved).map(({ viewDescriptorItem, visibleIndex }) => ({ viewDescriptor: viewDescriptorItem.viewDescriptor, index: visibleIndex }));
this.broadCastRemovedVisibleViewDescriptors(removedVisibleDescriptors);
}
if (removed.length) {
this.broadCastRemovedVisibleViewDescriptors(removed);
}
if (toBeAdded?.length) {
const addedVisibleDescriptors = updateVisibility(toBeAdded).map(({ viewDescriptorItem, visibleIndex }) => ({ index: visibleIndex, viewDescriptor: viewDescriptorItem.viewDescriptor, size: viewDescriptorItem.state.size, collapsed: !!viewDescriptorItem.state.collapsed }));
this.broadCastAddedVisibleViewDescriptors(addedVisibleDescriptors);
// Second: Update and add the view descriptors which are asked to be shown
const added: IAddedViewDescriptorRef[] = [];
for (const { id, visible } of viewDescriptors) {
if (!visible) {
continue;
}
const foundViewDescriptor = this.findAndIgnoreIfNotFound(id);
if (!foundViewDescriptor) {
continue;
}
const { viewDescriptorItem, visibleIndex } = foundViewDescriptor;
if (this.updateViewDescriptorItemVisibility(viewDescriptorItem, true)) {
added.push({ index: visibleIndex, viewDescriptor: viewDescriptorItem.viewDescriptor, size: viewDescriptorItem.state.size, collapsed: !!viewDescriptorItem.state.collapsed });
}
}
if (added.length) {
this.broadCastAddedVisibleViewDescriptors(added);
}
}
private updateViewDescriptorItemVisibility(viewDescriptorItem: IViewDescriptorItem, visible: boolean): boolean {
if (!viewDescriptorItem.viewDescriptor.canToggleVisibility) {
return false;
}
if (this.isViewDescriptorVisibleWhenActive(viewDescriptorItem) === visible) {
return false;
}
// update visibility
if (viewDescriptorItem.viewDescriptor.workspace) {
viewDescriptorItem.state.visibleWorkspace = visible;
} else {
viewDescriptorItem.state.visibleGlobal = visible;
}
// return `true` only if visibility is changed
return this.isViewDescriptorVisible(viewDescriptorItem) === visible;
}
isCollapsed(id: string): boolean {

View file

@ -17,6 +17,7 @@ import { Registry } from 'vs/platform/registry/common/platform';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { Event } from 'vs/base/common/event';
import { getViewsStateStorageId } from 'vs/workbench/services/views/common/viewContainerModel';
const ViewContainerRegistry = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry);
const ViewsRegistry = Registry.as<IViewsRegistry>(ViewContainerExtensions.ViewsRegistry);
@ -580,4 +581,230 @@ suite('ViewContainerModel', () => {
assert.strictEqual(target.elements.length, 0);
});
test('#142087: view descriptor visibility is not reset', async function () {
container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: 'test', ctorDescriptor: new SyncDescriptor(<any>{}) }, ViewContainerLocation.Sidebar);
const testObject = viewDescriptorService.getViewContainerModel(container);
const viewDescriptor: IViewDescriptor = {
id: 'view1',
ctorDescriptor: null!,
name: 'Test View 1',
canToggleVisibility: true
};
storageService.store(getViewsStateStorageId('test.state'), JSON.stringify([{
id: viewDescriptor.id,
isHidden: true,
order: undefined
}]), StorageScope.GLOBAL, StorageTarget.USER);
ViewsRegistry.registerViews([viewDescriptor], container);
assert.strictEqual(testObject.isVisible(viewDescriptor.id), false);
assert.strictEqual(testObject.activeViewDescriptors[0].id, viewDescriptor.id);
assert.strictEqual(testObject.visibleViewDescriptors.length, 0);
});
test('remove event is triggered properly if mutliple views are hidden at the same time', async function () {
container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: 'test', ctorDescriptor: new SyncDescriptor(<any>{}) }, ViewContainerLocation.Sidebar);
const testObject = viewDescriptorService.getViewContainerModel(container);
const target = disposableStore.add(new ViewDescriptorSequence(testObject));
const viewDescriptor1: IViewDescriptor = {
id: 'view1',
ctorDescriptor: null!,
name: 'Test View 1',
canToggleVisibility: true
};
const viewDescriptor2: IViewDescriptor = {
id: 'view2',
ctorDescriptor: null!,
name: 'Test View 2',
canToggleVisibility: true
};
const viewDescriptor3: IViewDescriptor = {
id: 'view3',
ctorDescriptor: null!,
name: 'Test View 3',
canToggleVisibility: true
};
ViewsRegistry.registerViews([viewDescriptor1, viewDescriptor2, viewDescriptor3], container);
const remomveEvent = sinon.spy();
testObject.onDidRemoveVisibleViewDescriptors(remomveEvent);
const addEvent = sinon.spy();
testObject.onDidAddVisibleViewDescriptors(addEvent);
storageService.store(getViewsStateStorageId('test.state'), JSON.stringify([{
id: viewDescriptor1.id,
isHidden: false,
order: undefined
}, {
id: viewDescriptor2.id,
isHidden: true,
order: undefined
}, {
id: viewDescriptor3.id,
isHidden: true,
order: undefined
}]), StorageScope.GLOBAL, StorageTarget.USER);
assert.ok(!addEvent.called, 'add event should not be called');
assert.ok(remomveEvent.calledOnce, 'remove event should be called');
assert.deepStrictEqual(remomveEvent.args[0][0], [{
viewDescriptor: viewDescriptor3,
index: 2
}, {
viewDescriptor: viewDescriptor2,
index: 1
}]);
assert.strictEqual(target.elements.length, 1);
assert.strictEqual(target.elements[0].id, viewDescriptor1.id);
});
test('add event is triggered properly if mutliple views are hidden at the same time', async function () {
container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: 'test', ctorDescriptor: new SyncDescriptor(<any>{}) }, ViewContainerLocation.Sidebar);
const testObject = viewDescriptorService.getViewContainerModel(container);
const target = disposableStore.add(new ViewDescriptorSequence(testObject));
const viewDescriptor1: IViewDescriptor = {
id: 'view1',
ctorDescriptor: null!,
name: 'Test View 1',
canToggleVisibility: true
};
const viewDescriptor2: IViewDescriptor = {
id: 'view2',
ctorDescriptor: null!,
name: 'Test View 2',
canToggleVisibility: true
};
const viewDescriptor3: IViewDescriptor = {
id: 'view3',
ctorDescriptor: null!,
name: 'Test View 3',
canToggleVisibility: true
};
ViewsRegistry.registerViews([viewDescriptor1, viewDescriptor2, viewDescriptor3], container);
testObject.setVisible(viewDescriptor1.id, false);
testObject.setVisible(viewDescriptor3.id, false);
const removeEvent = sinon.spy();
testObject.onDidRemoveVisibleViewDescriptors(removeEvent);
const addEvent = sinon.spy();
testObject.onDidAddVisibleViewDescriptors(addEvent);
storageService.store(getViewsStateStorageId('test.state'), JSON.stringify([{
id: viewDescriptor1.id,
isHidden: false,
order: undefined
}, {
id: viewDescriptor2.id,
isHidden: false,
order: undefined
}, {
id: viewDescriptor3.id,
isHidden: false,
order: undefined
}]), StorageScope.GLOBAL, StorageTarget.USER);
assert.ok(!removeEvent.called, 'remove event should not be called');
assert.ok(addEvent.calledOnce, 'add event should be called once');
assert.deepStrictEqual(addEvent.args[0][0], [{
viewDescriptor: viewDescriptor1,
index: 0,
collapsed: false,
size: undefined
}, {
viewDescriptor: viewDescriptor3,
index: 2,
collapsed: false,
size: undefined
}]);
assert.strictEqual(target.elements.length, 3);
assert.strictEqual(target.elements[0].id, viewDescriptor1.id);
assert.strictEqual(target.elements[1].id, viewDescriptor2.id);
assert.strictEqual(target.elements[2].id, viewDescriptor3.id);
});
test('add and remove events are triggered properly if mutliple views are hidden and added at the same time', async function () {
container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: 'test', ctorDescriptor: new SyncDescriptor(<any>{}) }, ViewContainerLocation.Sidebar);
const testObject = viewDescriptorService.getViewContainerModel(container);
const target = disposableStore.add(new ViewDescriptorSequence(testObject));
const viewDescriptor1: IViewDescriptor = {
id: 'view1',
ctorDescriptor: null!,
name: 'Test View 1',
canToggleVisibility: true
};
const viewDescriptor2: IViewDescriptor = {
id: 'view2',
ctorDescriptor: null!,
name: 'Test View 2',
canToggleVisibility: true
};
const viewDescriptor3: IViewDescriptor = {
id: 'view3',
ctorDescriptor: null!,
name: 'Test View 3',
canToggleVisibility: true
};
const viewDescriptor4: IViewDescriptor = {
id: 'view4',
ctorDescriptor: null!,
name: 'Test View 4',
canToggleVisibility: true
};
ViewsRegistry.registerViews([viewDescriptor1, viewDescriptor2, viewDescriptor3, viewDescriptor4], container);
testObject.setVisible(viewDescriptor1.id, false);
const removeEvent = sinon.spy();
testObject.onDidRemoveVisibleViewDescriptors(removeEvent);
const addEvent = sinon.spy();
testObject.onDidAddVisibleViewDescriptors(addEvent);
storageService.store(getViewsStateStorageId('test.state'), JSON.stringify([{
id: viewDescriptor1.id,
isHidden: false,
order: undefined
}, {
id: viewDescriptor2.id,
isHidden: true,
order: undefined
}, {
id: viewDescriptor3.id,
isHidden: false,
order: undefined
}, {
id: viewDescriptor4.id,
isHidden: true,
order: undefined
}]), StorageScope.GLOBAL, StorageTarget.USER);
assert.ok(removeEvent.calledOnce, 'remove event should be called once');
assert.deepStrictEqual(removeEvent.args[0][0], [{
viewDescriptor: viewDescriptor4,
index: 2
}, {
viewDescriptor: viewDescriptor2,
index: 0
}]);
assert.ok(addEvent.calledOnce, 'add event should be called once');
assert.deepStrictEqual(addEvent.args[0][0], [{
viewDescriptor: viewDescriptor1,
index: 0,
collapsed: false,
size: undefined
}]);
assert.strictEqual(target.elements.length, 2);
assert.strictEqual(target.elements[0].id, viewDescriptor1.id);
assert.strictEqual(target.elements[1].id, viewDescriptor3.id);
});
});

View file

@ -99,8 +99,7 @@ declare module 'vscode' {
/**
* All the groups within the group container
*/
// TODO@API rename to groups
readonly all: TabGroup[];
readonly groups: TabGroup[];
/**
* The currently active group

View file

@ -224,7 +224,7 @@ async function launchServer(options: LaunchOptions) {
...process.env
};
const args = ['--disable-telemetry', '--port', `${port++}`, '--driver', 'web', '--extensions-dir', extensionsPath, '--server-data-dir', agentFolder, '--accept-server-license-terms'];
const args = ['--disable-telemetry', '--disable-workspace-trust', '--port', `${port++}`, '--driver', 'web', '--extensions-dir', extensionsPath, '--server-data-dir', agentFolder, '--accept-server-license-terms'];
let serverLocation: string | undefined;
if (codeServerPath) {

View file

@ -125,7 +125,7 @@ async function launchServer(browserType: BrowserType): Promise<{ endpoint: url.U
const root = path.join(__dirname, '..', '..', '..', '..');
const logsPath = path.join(root, '.build', 'logs', 'integration-tests-browser');
const serverArgs = ['--driver', 'web', '--enable-proposed-api', '--disable-telemetry', '--server-data-dir', userDataDir, '--accept-server-license-terms'];
const serverArgs = ['--driver', 'web', '--enable-proposed-api', '--disable-telemetry', '--server-data-dir', userDataDir, '--accept-server-license-terms', '--disable-workspace-trust'];
let serverLocation: string;
if (process.env.VSCODE_REMOTE_SERVER_PATH) {