mirror of
https://github.com/Microsoft/vscode
synced 2024-08-27 04:49:35 +00:00
Merge remote-tracking branch 'origin/main' into tyriar/142669
This commit is contained in:
commit
2ce4442722
|
@ -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 &&
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
"git": {
|
||||
"name": "sumneko/lua.tmbundle",
|
||||
"repositoryUrl": "https://github.com/sumneko/lua.tmbundle",
|
||||
"commitHash": "9b79cbc54c3267065fca3d7b5b3845d1efbd091d"
|
||||
"commitHash": "43da68bb99b948782e57985f633fa0c395190674"
|
||||
}
|
||||
},
|
||||
"licenseDetail": [
|
||||
|
|
|
@ -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*\\??:)",
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
"git": {
|
||||
"name": "seti-ui",
|
||||
"repositoryUrl": "https://github.com/jesseweed/seti-ui",
|
||||
"commitHash": "921e46b4c378059032c2a9247a6cc96d0f9e57b4"
|
||||
"commitHash": "8dba1bc311dad1b9bc23c4779149f3bf9baa8cb0"
|
||||
}
|
||||
},
|
||||
"version": "0.1.0"
|
||||
|
|
Binary file not shown.
|
@ -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"
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Binary file not shown.
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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 {
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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', () => {
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 ? {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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)
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
|
|
@ -77,7 +77,7 @@ MenuRegistry.appendMenuItems([
|
|||
title: localize('miShowAuxiliaryBarNoMnemonic', "Show Secondary Side Bar"),
|
||||
toggled: AuxiliaryBarVisibleContext
|
||||
},
|
||||
order: 4
|
||||
order: 2
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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."),
|
||||
},
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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': [
|
||||
{
|
||||
|
|
|
@ -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)));
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 });
|
||||
}
|
||||
}
|
||||
}),
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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'));
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 |
|
@ -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
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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> { }
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
||||
});
|
||||
|
|
3
src/vscode-dts/vscode.proposed.tabs.d.ts
vendored
3
src/vscode-dts/vscode.proposed.tabs.d.ts
vendored
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in a new issue