diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
index 2dc1460b16f..012854c7309 100644
--- a/.github/ISSUE_TEMPLATE/feature_request.md
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -8,3 +8,4 @@ about: Suggest an idea for this project
+
diff --git a/.github/commands.json b/.github/commands.json
new file mode 100644
index 00000000000..10d2daa7da4
--- /dev/null
+++ b/.github/commands.json
@@ -0,0 +1,167 @@
+[
+ {
+ "type": "comment",
+ "name": "question",
+ "allowUsers": [
+ "cleidigh",
+ "usernamehw",
+ "gjsjohnmurray",
+ "IllusionMH"
+ ],
+ "action": "updateLabels",
+ "addLabel": "*question"
+ },
+ {
+ "type": "label",
+ "name": "*question",
+ "action": "close",
+ "comment": "Please ask your question on [StackOverflow](https://aka.ms/vscodestackoverflow). We have a great community over [there](https://aka.ms/vscodestackoverflow). They have already answered thousands of questions and are happy to answer yours as well. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines.\n\nHappy Coding!"
+ },
+ {
+ "type": "label",
+ "name": "*dev-question",
+ "action": "close",
+ "comment": "We have a great developer community [over on slack](https://aka.ms/vscode-dev-community) where extension authors help each other. This is a great place for you to ask questions and find support.\n\nHappy Coding!"
+ },
+ {
+ "type": "label",
+ "name": "*extension-candidate",
+ "action": "close",
+ "comment": "We try to keep VS Code lean and we think the functionality you're asking for is great for a VS Code extension. Maybe you can already find one that suits you in the [VS Code Marketplace](https://aka.ms/vscodemarketplace). Just in case, in a few simple steps you can get started [writing your own extension](https://aka.ms/vscodewritingextensions). See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines.\n\nHappy Coding!"
+ },
+ {
+ "type": "label",
+ "name": "*not-reproducible",
+ "action": "close",
+ "comment": "We closed this issue because we are unable to reproduce the problem with the steps you describe. Chances are we've already fixed your problem in a recent version of VS Code. If not, please ask us to reopen the issue and provide us with more detail. Our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines might help you with that.\n\nHappy Coding!"
+ },
+ {
+ "type": "label",
+ "name": "*out-of-scope",
+ "action": "close",
+ "comment": "We closed this issue because we don't plan to address it in the foreseeable future. You can find more detailed information about our decision-making process [here](https://aka.ms/vscode-out-of-scope). If you disagree and feel that this issue is crucial: We are happy to listen and to reconsider.\n\nIf you wonder what we are up to, please see our [roadmap](https://aka.ms/vscoderoadmap) and [issue reporting](https://aka.ms/vscodeissuereporting) guidelines.\n\nThanks for your understanding and happy coding!"
+ },
+ {
+ "type": "comment",
+ "name": "causedByExtension",
+ "allowUsers": [
+ "cleidigh",
+ "usernamehw",
+ "gjsjohnmurray",
+ "IllusionMH"
+ ],
+ "action": "updateLabels",
+ "addLabel": "*caused-by-extension"
+ },
+ {
+ "type": "label",
+ "name": "*caused-by-extension",
+ "action": "close",
+ "comment": "This issue is caused by an extension, please file it with the repository (or contact) the extension has linked in its overview in VS Code or the [marketplace](https://aka.ms/vscodemarketplace) for VS Code. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines.\n\nHappy Coding!"
+ },
+ {
+ "type": "label",
+ "name": "*as-designed",
+ "action": "close",
+ "comment": "The described behavior is how it is expected to work. If you disagree, please explain what is expected and what is not in more detail. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines.\n\nHappy Coding!"
+ },
+ {
+ "type": "label",
+ "name": "*english-please",
+ "action": "close",
+ "comment": "This issue is being closed because its description is not in English, that makes it hard for us to work on it. Please open a new issue with an English description. You might find [Bing Translator](https://www.bing.com/translator) useful."
+ },
+ {
+ "type": "comment",
+ "name": "duplicate",
+ "allowUsers": [
+ "cleidigh",
+ "usernamehw",
+ "gjsjohnmurray",
+ "IllusionMH"
+ ],
+ "action": "updateLabels",
+ "addLabel": "*duplicate"
+ },
+ {
+ "type": "label",
+ "name": "*duplicate",
+ "action": "close",
+ "comment": "Thanks for creating this issue! We figured it's covering the same as another one we already have. Thus, we closed this one as a duplicate. You can search for existing issues [here](https://aka.ms/vscodeissuesearch). See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines.\n\nHappy Coding!"
+ },
+ {
+ "type": "comment",
+ "name": "confirm",
+ "allowUsers": [
+ "cleidigh",
+ "usernamehw",
+ "gjsjohnmurray",
+ "IllusionMH"
+ ],
+ "action": "updateLabels",
+ "addLabel": "confirmed",
+ "removeLabel": "confirmation-pending"
+ },
+ {
+ "type": "comment",
+ "name": "confirmationPending",
+ "allowUsers": [
+ "cleidigh",
+ "usernamehw",
+ "gjsjohnmurray",
+ "IllusionMH"
+ ],
+ "action": "updateLabels",
+ "addLabel": "confirmation-pending",
+ "removeLabel": "confirmed"
+ },
+ {
+ "type": "comment",
+ "name": "needsMoreInfo",
+ "allowUsers": [
+ "cleidigh",
+ "usernamehw",
+ "gjsjohnmurray",
+ "IllusionMH"
+ ],
+ "action": "updateLabels",
+ "addLabel": "~needs more info"
+ },
+ {
+ "type": "label",
+ "name": "~needs version info",
+ "action": "updateLabels",
+ "addLabel": "needs more info",
+ "removeLabel": "~needs version info",
+ "comment": "Thanks for creating this issue! We figured it's missing some basic information, such as a version number, or in some other way doesn't follow our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines. Please take the time to review these and update the issue.\n\nHappy Coding!"
+ },
+ {
+ "type": "label",
+ "name": "~needs more info",
+ "action": "updateLabels",
+ "addLabel": "needs more info",
+ "removeLabel": "~needs more info",
+ "comment": "Thanks for creating this issue! We figured it's missing some basic information or in some other way doesn't follow our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines. Please take the time to review these and update the issue.\n\nHappy Coding!"
+ },
+ {
+ "type": "comment",
+ "name": "a11ymas",
+ "allowUsers": [
+ "AccessibilityTestingTeam-TCS",
+ "dixitsonali95",
+ "Mohini78",
+ "ChitrarupaSharma",
+ "mspatil110",
+ "umasarath52",
+ "v-umnaik"
+ ],
+ "action": "updateLabels",
+ "addLabel": "a11ymas"
+ },
+ {
+ "type": "label",
+ "name": "*off-topic",
+ "action": "close",
+ "comment": "Thanks for creating this issue. We think this issue is unactionable or unrelated to the goals of this project. Please follow our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines.\n\nHappy Coding!"
+ }
+]
diff --git a/.github/commands.yml b/.github/commands.yml
index 24ac951d6f0..d2bac8edb1f 100644
--- a/.github/commands.yml
+++ b/.github/commands.yml
@@ -1,3 +1,16 @@
+# {
+# perform: true,
+# commands: [
+# {
+# type: 'comment',
+# name: 'findDuplicates',
+# allowUsers: ['cleidigh', 'usernamehw', 'gjsjohnmurray', 'IllusionMH'],
+# action: 'comment',
+# comment: "Potential duplicates:\n${potentialDuplicates}"
+# }
+# ]
+# }
+
{
perform: true,
commands: [
diff --git a/.github/copycat.yml b/.github/copycat.yml
deleted file mode 100644
index 690c803bd0a..00000000000
--- a/.github/copycat.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- perform: true,
- target_owner: 'chrmarti',
- target_repo: 'testissues'
-}
diff --git a/.github/workflows/commands.yml b/.github/workflows/commands.yml
new file mode 100644
index 00000000000..35974e43535
--- /dev/null
+++ b/.github/workflows/commands.yml
@@ -0,0 +1,21 @@
+name: Commands
+on:
+ issue_comment:
+ types: [created]
+ issues:
+ types: [labeled]
+
+jobs:
+ main:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout Actions
+ uses: actions/checkout@v2
+ with:
+ repository: 'JacksonKearl/vscode-triage-github-actions'
+ ref: v2
+ # - name: Run Commands
+ # uses: ./commands
+ # with:
+ # token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}}
+ # config-path: commands
diff --git a/.github/workflows/copycat.yml b/.github/workflows/copycat.yml
new file mode 100644
index 00000000000..34fb291329a
--- /dev/null
+++ b/.github/workflows/copycat.yml
@@ -0,0 +1,26 @@
+name: CopyCat
+on:
+ issues:
+ types: [opened]
+
+jobs:
+ main:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout Actions
+ uses: actions/checkout@v2
+ with:
+ repository: 'JacksonKearl/vscode-triage-github-actions'
+ ref: v2
+ # - name: Run CopyCat (JacksonKearl/testissues)
+ # uses: ./copycat
+ # with:
+ # token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}}
+ # owner: JacksonKearl
+ # repo: testissues
+ # - name: Run CopyCat (chrmarti/testissues)
+ # uses: ./copycat
+ # with:
+ # token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}}
+ # owner: chrmarti
+ # repo: testissues
diff --git a/.github/workflows/needs-version-info.yml b/.github/workflows/needs-version-info.yml
new file mode 100644
index 00000000000..3b62b3c6947
--- /dev/null
+++ b/.github/workflows/needs-version-info.yml
@@ -0,0 +1,20 @@
+name: Needs Version Info
+on:
+ issues:
+ types: [opened]
+
+jobs:
+ main:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout Actions
+ uses: actions/checkout@v2
+ with:
+ repository: 'JacksonKearl/vscode-triage-github-actions'
+ ref: v2
+ # - name: Run Needs Version Info
+ # uses: ./needs-more-info
+ # with:
+ # token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}}
+ # matcher: '\b(\d\.\d{2,3}\.\d|insiders?|1\.\d\d\d?)\b'
+ # label: ~needs version info
diff --git a/build/builtInExtensions.json b/build/builtInExtensions.json
index 63c41b403da..c8607da9d4c 100644
--- a/build/builtInExtensions.json
+++ b/build/builtInExtensions.json
@@ -46,7 +46,7 @@
},
{
"name": "ms-vscode.js-debug-nightly",
- "version": "2020.3.317",
+ "version": "2020.3.1117",
"forQualities": [
"insider"
],
diff --git a/build/lib/i18n.js b/build/lib/i18n.js
index ea1758ee57e..2e7415cd721 100644
--- a/build/lib/i18n.js
+++ b/build/lib/i18n.js
@@ -114,7 +114,7 @@ let XLF = /** @class */ (() => {
for (let file in this.files) {
this.appendNewLine(``, 2);
for (let item of this.files[file]) {
- this.addStringItem(item);
+ this.addStringItem(file, item);
}
this.appendNewLine('', 2);
}
@@ -154,9 +154,12 @@ let XLF = /** @class */ (() => {
this.files[original].push({ id: realKey, message: message, comment: comment });
}
}
- addStringItem(item) {
- if (!item.id || !item.message) {
- throw new Error(`No item ID or value specified: ${JSON.stringify(item)}`);
+ addStringItem(file, item) {
+ if (!item.id || item.message === undefined || item.message === null) {
+ throw new Error(`No item ID or value specified: ${JSON.stringify(item)}. File: ${file}`);
+ }
+ if (item.message.length === 0) {
+ log(`Item with id ${item.id} in file ${file} has an empty message.`);
}
this.appendNewLine(``, 4);
this.appendNewLine(`${item.message}`, 6);
diff --git a/build/lib/i18n.ts b/build/lib/i18n.ts
index d69109a9d05..b9fb3879872 100644
--- a/build/lib/i18n.ts
+++ b/build/lib/i18n.ts
@@ -201,7 +201,7 @@ export class XLF {
for (let file in this.files) {
this.appendNewLine(``, 2);
for (let item of this.files[file]) {
- this.addStringItem(item);
+ this.addStringItem(file, item);
}
this.appendNewLine('', 2);
}
@@ -243,9 +243,12 @@ export class XLF {
}
}
- private addStringItem(item: Item): void {
- if (!item.id || !item.message) {
- throw new Error(`No item ID or value specified: ${JSON.stringify(item)}`);
+ private addStringItem(file: string, item: Item): void {
+ if (!item.id || item.message === undefined || item.message === null) {
+ throw new Error(`No item ID or value specified: ${JSON.stringify(item)}. File: ${file}`);
+ }
+ if (item.message.length === 0) {
+ log(`Item with id ${item.id} in file ${file} has an empty message.`);
}
this.appendNewLine(``, 4);
diff --git a/build/lib/treeshaking.js b/build/lib/treeshaking.js
index bc739120444..bf16c0fa839 100644
--- a/build/lib/treeshaking.js
+++ b/build/lib/treeshaking.js
@@ -333,7 +333,7 @@ function markNodes(languageService, options) {
}
setColor(node, 2 /* Black */);
black_queue.push(node);
- if (options.shakeLevel === 2 /* ClassMembers */ && (ts.isMethodDeclaration(node) || ts.isMethodSignature(node) || ts.isPropertySignature(node) || ts.isGetAccessor(node) || ts.isSetAccessor(node))) {
+ if (options.shakeLevel === 2 /* ClassMembers */ && (ts.isMethodDeclaration(node) || ts.isMethodSignature(node) || ts.isPropertySignature(node) || ts.isPropertyDeclaration(node) || ts.isGetAccessor(node) || ts.isSetAccessor(node))) {
const references = languageService.getReferencesAtPosition(node.getSourceFile().fileName, node.name.pos + node.name.getLeadingTriviaWidth());
if (references) {
for (let i = 0, len = references.length; i < len; i++) {
diff --git a/build/lib/treeshaking.ts b/build/lib/treeshaking.ts
index c1551e8143d..16d9ab6e3e9 100644
--- a/build/lib/treeshaking.ts
+++ b/build/lib/treeshaking.ts
@@ -436,7 +436,7 @@ function markNodes(languageService: ts.LanguageService, options: ITreeShakingOpt
setColor(node, NodeColor.Black);
black_queue.push(node);
- if (options.shakeLevel === ShakeLevel.ClassMembers && (ts.isMethodDeclaration(node) || ts.isMethodSignature(node) || ts.isPropertySignature(node) || ts.isGetAccessor(node) || ts.isSetAccessor(node))) {
+ if (options.shakeLevel === ShakeLevel.ClassMembers && (ts.isMethodDeclaration(node) || ts.isMethodSignature(node) || ts.isPropertySignature(node) || ts.isPropertyDeclaration(node) || ts.isGetAccessor(node) || ts.isSetAccessor(node))) {
const references = languageService.getReferencesAtPosition(node.getSourceFile().fileName, node.name.pos + node.name.getLeadingTriviaWidth());
if (references) {
for (let i = 0, len = references.length; i < len; i++) {
diff --git a/build/package.json b/build/package.json
index 2abecaa1279..dbaed69b320 100644
--- a/build/package.json
+++ b/build/package.json
@@ -40,10 +40,10 @@
"iconv-lite": "0.4.23",
"mime": "^1.3.4",
"minimatch": "3.0.4",
- "minimist": "^1.2.0",
+ "minimist": "^1.2.2",
"request": "^2.85.0",
"terser": "4.3.8",
- "typescript": "3.9.0-dev.20200304",
+ "typescript": "^3.9.0-dev.20200313",
"vsce": "1.48.0",
"vscode-telemetry-extractor": "^1.5.4",
"xml2js": "^0.4.17"
diff --git a/build/yarn.lock b/build/yarn.lock
index ef17978a534..9b0fe8e7861 100644
--- a/build/yarn.lock
+++ b/build/yarn.lock
@@ -1780,10 +1780,10 @@ minimatch@3.0.4, minimatch@^3.0.3, minimatch@^3.0.4:
dependencies:
brace-expansion "^1.1.7"
-minimist@^1.1.0, minimist@^1.2.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
- integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=
+minimist@^1.1.0, minimist@^1.2.0, minimist@^1.2.2:
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.2.tgz#b00a00230a1108c48c169e69a291aafda3aacd63"
+ integrity sha512-rIqbOrKb8GJmx/5bc2M0QchhUouMXSpd1RTclXsB41JdL+VtnojfaJR+h7F9k18/4kHUsBFgk80Uk+q569vjPA==
minimist@~0.0.1:
version "0.0.10"
@@ -2453,16 +2453,16 @@ typed-rest-client@^0.9.0:
tunnel "0.0.4"
underscore "1.8.3"
-typescript@3.9.0-dev.20200304:
- version "3.9.0-dev.20200304"
- resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.0-dev.20200304.tgz#3cc35357eff29dc5604b4fa56d6597e13daf86ed"
- integrity sha512-eUip/GgJmjp4qtHiJDxVhE5SDDiPzBUg7KBAFUgb7HgL/tv10JAHej7fnS1i+7xrq1eDtbkJyPaYOVnhL9db7Q==
-
typescript@^3.0.1:
version "3.5.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.3.tgz#c830f657f93f1ea846819e929092f5fe5983e977"
integrity sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==
+typescript@^3.9.0-dev.20200313:
+ version "3.9.0-dev.20200313"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.0-dev.20200313.tgz#f66aeb2c08268f2b1fc6d1d96e15554c6e7ed29b"
+ integrity sha512-85/IJPm1nEUbQDxK3aN+svIy4X3kPcAipihB3704NY1HXncJ1daNLJW1OktOacb8tD/URpIGs9nMgbUrKvglGg==
+
typical@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/typical/-/typical-4.0.0.tgz#cbeaff3b9d7ae1e2bbfaf5a4e6f11eccfde94fc4"
diff --git a/extensions/css-language-features/server/package.json b/extensions/css-language-features/server/package.json
index 7f439dd686a..593ecdff80a 100644
--- a/extensions/css-language-features/server/package.json
+++ b/extensions/css-language-features/server/package.json
@@ -9,7 +9,7 @@
},
"main": "./out/cssServerMain",
"dependencies": {
- "vscode-css-languageservice": "^4.1.0",
+ "vscode-css-languageservice": "^4.1.1",
"vscode-languageserver": "^6.1.1"
},
"devDependencies": {
diff --git a/extensions/css-language-features/server/yarn.lock b/extensions/css-language-features/server/yarn.lock
index e57ff40f62e..902123900d0 100644
--- a/extensions/css-language-features/server/yarn.lock
+++ b/extensions/css-language-features/server/yarn.lock
@@ -689,10 +689,10 @@ to-regex-range@^5.0.1:
dependencies:
is-number "^7.0.0"
-vscode-css-languageservice@^4.1.0:
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-4.1.0.tgz#144c8274e0bf1719fa6f773ca684bd1c7ffd634f"
- integrity sha512-iTX3dTp0Y0RFWhIux5jasI8r9swdiWVB1Z3OrZ10iDHxzkETjVPxAQ5BEQU4ag0Awc8TTg1C7sJriHQY2LO14g==
+vscode-css-languageservice@^4.1.1:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-4.1.1.tgz#9131dd465e4b20f3ba78ab9734b2c7cdb9237443"
+ integrity sha512-2r2bYbhscivRu1zqh5kNe8aYpFnfksMYC7wTpKX2HqFsSzSJYXk0sCqPaWsP5ptqz0OFBTXnzx2JlE+Nb5Edgw==
dependencies:
vscode-languageserver-textdocument "^1.0.1"
vscode-languageserver-types "^3.15.1"
diff --git a/extensions/image-preview/src/preview.ts b/extensions/image-preview/src/preview.ts
index fc43a136a01..38f6f5231b5 100644
--- a/extensions/image-preview/src/preview.ts
+++ b/extensions/image-preview/src/preview.ts
@@ -27,8 +27,8 @@ export class PreviewManager implements vscode.CustomEditorProvider {
private readonly zoomStatusBarEntry: ZoomStatusBarEntry,
) { }
- public async resolveCustomDocument(_document: vscode.CustomDocument): Promise {
- return {};
+ public async resolveCustomDocument(_document: vscode.CustomDocument): Promise {
+ // noop
}
public async resolveCustomEditor(
diff --git a/extensions/json-language-features/client/src/jsonMain.ts b/extensions/json-language-features/client/src/jsonMain.ts
index 5d41458931c..0f934004cdd 100644
--- a/extensions/json-language-features/client/src/jsonMain.ts
+++ b/extensions/json-language-features/client/src/jsonMain.ts
@@ -188,6 +188,9 @@ export function activate(context: ExtensionContext) {
// handle content request
client.onRequest(VSCodeContentRequest.type, (uriPath: string) => {
const uri = Uri.parse(uriPath);
+ if (uri.scheme === 'untitled') {
+ return Promise.reject(new Error(localize('untitled.schema', 'Unable to load {0}', uri.toString())));
+ }
if (uri.scheme !== 'http' && uri.scheme !== 'https') {
return workspace.openTextDocument(uri).then(doc => {
schemaDocuments[uri.toString()] = true;
@@ -342,6 +345,9 @@ function getSchemaAssociations(_context: ExtensionContext): ISchemaAssociation[]
fileMatch = [fileMatch];
}
if (Array.isArray(fileMatch) && url) {
+ if (url[0] === '.' && url[1] === '/') {
+ url = Uri.file(path.join(extension.extensionPath, url)).toString();
+ }
fileMatch = fileMatch.map(fm => {
if (fm[0] === '%') {
fm = fm.replace(/%APP_SETTINGS_HOME%/, '/User');
diff --git a/extensions/markdown-language-features/src/features/previewManager.ts b/extensions/markdown-language-features/src/features/previewManager.ts
index a6fc8ec528c..a9403313931 100644
--- a/extensions/markdown-language-features/src/features/previewManager.ts
+++ b/extensions/markdown-language-features/src/features/previewManager.ts
@@ -148,8 +148,8 @@ export class MarkdownPreviewManager extends Disposable implements vscode.Webview
this.registerDynamicPreview(preview);
}
- public async resolveCustomDocument(_document: vscode.CustomDocument): Promise {
- return {};
+ public async resolveCustomDocument(_document: vscode.CustomDocument): Promise {
+ // noop
}
public async resolveCustomTextEditor(
diff --git a/extensions/markdown-language-features/yarn.lock b/extensions/markdown-language-features/yarn.lock
index 5c7a22c74f0..23cd1744005 100644
--- a/extensions/markdown-language-features/yarn.lock
+++ b/extensions/markdown-language-features/yarn.lock
@@ -191,9 +191,9 @@ abbrev@1:
integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
acorn@^6.2.1:
- version "6.3.0"
- resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.3.0.tgz#0087509119ffa4fc0a0041d1e93a417e68cb856e"
- integrity sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA==
+ version "6.4.1"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.1.tgz#531e58ba3f51b9dacb9a6646ca4debf5b14ca474"
+ integrity sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==
ajv-errors@^1.0.0:
version "1.0.1"
diff --git a/extensions/theme-abyss/themes/abyss-color-theme.json b/extensions/theme-abyss/themes/abyss-color-theme.json
index 1df39d31c90..39f93305b8d 100644
--- a/extensions/theme-abyss/themes/abyss-color-theme.json
+++ b/extensions/theme-abyss/themes/abyss-color-theme.json
@@ -434,5 +434,6 @@
"terminal.ansiBrightMagenta": "#d778ff",
"terminal.ansiBrightCyan": "#78ffff",
"terminal.ansiBrightWhite": "#ffffff"
- }
+ },
+ "semanticHighlighting": true
}
diff --git a/extensions/theme-defaults/themes/dark_defaults.json b/extensions/theme-defaults/themes/dark_defaults.json
index 00c2ac8c36b..89f0a5beec8 100644
--- a/extensions/theme-defaults/themes/dark_defaults.json
+++ b/extensions/theme-defaults/themes/dark_defaults.json
@@ -18,5 +18,6 @@
"menu.foreground": "#CCCCCC",
"statusBarItem.remoteForeground": "#FFF",
"statusBarItem.remoteBackground": "#16825D"
- }
-}
\ No newline at end of file
+ },
+ "semanticHighlighting": true
+}
diff --git a/extensions/theme-defaults/themes/hc_black_defaults.json b/extensions/theme-defaults/themes/hc_black_defaults.json
index 9d11138a99b..1a03010abff 100644
--- a/extensions/theme-defaults/themes/hc_black_defaults.json
+++ b/extensions/theme-defaults/themes/hc_black_defaults.json
@@ -337,5 +337,6 @@
"foreground": "#569cd6"
}
}
- ]
-}
\ No newline at end of file
+ ],
+ "semanticHighlighting": true
+}
diff --git a/extensions/theme-defaults/themes/light_defaults.json b/extensions/theme-defaults/themes/light_defaults.json
index 018da38e939..9ea03a9e317 100644
--- a/extensions/theme-defaults/themes/light_defaults.json
+++ b/extensions/theme-defaults/themes/light_defaults.json
@@ -18,5 +18,6 @@
"settings.numberInputBorder": "#CECECE",
"statusBarItem.remoteForeground": "#FFF",
"statusBarItem.remoteBackground": "#16825D"
- }
+ },
+ "semanticHighlighting": true
}
diff --git a/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json b/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json
index 111a4a23d9b..cdd22307117 100644
--- a/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json
+++ b/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json
@@ -394,5 +394,6 @@
"foreground": "#dc3958"
}
}
- ]
+ ],
+ "semanticHighlighting": true
}
diff --git a/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json b/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json
index f0b6126d5fd..8b1fe2dd80e 100644
--- a/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json
+++ b/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json
@@ -572,5 +572,6 @@
"foreground": "#c7444a"
}
}
- ]
+ ],
+ "semanticHighlighting": true
}
diff --git a/extensions/theme-monokai/themes/monokai-color-theme.json b/extensions/theme-monokai/themes/monokai-color-theme.json
index c695640f299..a3050894657 100644
--- a/extensions/theme-monokai/themes/monokai-color-theme.json
+++ b/extensions/theme-monokai/themes/monokai-color-theme.json
@@ -476,5 +476,6 @@
"foreground": "#FD971F"
}
}
- ]
+ ],
+ "semanticHighlighting": true
}
diff --git a/extensions/theme-quietlight/themes/quietlight-color-theme.json b/extensions/theme-quietlight/themes/quietlight-color-theme.json
index ae19ba7889b..ffcb30cff03 100644
--- a/extensions/theme-quietlight/themes/quietlight-color-theme.json
+++ b/extensions/theme-quietlight/themes/quietlight-color-theme.json
@@ -494,5 +494,6 @@
"walkThrough.embeddedEditorBackground": "#00000014",
"editorIndentGuide.background": "#aaaaaa60",
"editorIndentGuide.activeBackground": "#777777b0"
- }
+ },
+ "semanticHighlighting": true
}
diff --git a/extensions/theme-red/themes/Red-color-theme.json b/extensions/theme-red/themes/Red-color-theme.json
index 277e7a8db3f..8ebbf48c22b 100644
--- a/extensions/theme-red/themes/Red-color-theme.json
+++ b/extensions/theme-red/themes/Red-color-theme.json
@@ -385,5 +385,6 @@
"foreground": "#ec0d1e"
}
}
- ]
+ ],
+ "semanticHighlighting": true
}
diff --git a/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json b/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json
index 682444485d5..b23ff8bb85c 100644
--- a/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json
+++ b/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json
@@ -477,5 +477,6 @@
"terminal.ansiBrightMagenta": "#6c71c4",
"terminal.ansiBrightCyan": "#93a1a1",
"terminal.ansiBrightWhite": "#fdf6e3"
- }
+ },
+ "semanticHighlighting": true
}
diff --git a/extensions/theme-solarized-light/themes/solarized-light-color-theme.json b/extensions/theme-solarized-light/themes/solarized-light-color-theme.json
index a29c8fb32f0..2c1f501d850 100644
--- a/extensions/theme-solarized-light/themes/solarized-light-color-theme.json
+++ b/extensions/theme-solarized-light/themes/solarized-light-color-theme.json
@@ -484,5 +484,6 @@
// Interactive Playground
"walkThrough.embeddedEditorBackground": "#00000014"
- }
+ },
+ "semanticHighlighting": true
}
diff --git a/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-theme.json b/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-theme.json
index f8c47a29e7b..0baee6822ef 100644
--- a/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-theme.json
+++ b/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-theme.json
@@ -255,5 +255,6 @@
"foreground": "#b267e6"
}
}
- ]
+ ],
+ "semanticHighlighting": true
}
diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json
index b3f12439bc8..e803011bfb4 100644
--- a/extensions/typescript-language-features/package.json
+++ b/extensions/typescript-language-features/package.json
@@ -19,7 +19,7 @@
"jsonc-parser": "^2.2.1",
"rimraf": "^2.6.3",
"semver": "5.5.1",
- "typescript-vscode-sh-plugin": "^0.6.8",
+ "typescript-vscode-sh-plugin": "^0.6.10",
"vscode-extension-telemetry": "0.1.1",
"vscode-nls": "^4.1.1"
},
diff --git a/extensions/typescript-language-features/src/features/completions.ts b/extensions/typescript-language-features/src/features/completions.ts
index e2f8ba9fd37..6758b85b0f6 100644
--- a/extensions/typescript-language-features/src/features/completions.ts
+++ b/extensions/typescript-language-features/src/features/completions.ts
@@ -216,7 +216,7 @@ class MyCompletionItem extends vscode.CompletionItem {
case PConst.Kind.function:
case PConst.Kind.localFunction:
return vscode.CompletionItemKind.Function;
- case PConst.Kind.memberFunction:
+ case PConst.Kind.method:
case PConst.Kind.constructSignature:
case PConst.Kind.callSignature:
case PConst.Kind.indexSignature:
@@ -272,7 +272,7 @@ class MyCompletionItem extends vscode.CompletionItem {
case PConst.Kind.memberVariable:
case PConst.Kind.class:
case PConst.Kind.function:
- case PConst.Kind.memberFunction:
+ case PConst.Kind.method:
case PConst.Kind.keyword:
case PConst.Kind.parameter:
commitCharacters.push('.', ',', ';');
diff --git a/extensions/typescript-language-features/src/features/documentSymbol.ts b/extensions/typescript-language-features/src/features/documentSymbol.ts
index 02c1be917d3..e119b005bab 100644
--- a/extensions/typescript-language-features/src/features/documentSymbol.ts
+++ b/extensions/typescript-language-features/src/features/documentSymbol.ts
@@ -16,7 +16,7 @@ const getSymbolKind = (kind: string): vscode.SymbolKind => {
case PConst.Kind.class: return vscode.SymbolKind.Class;
case PConst.Kind.enum: return vscode.SymbolKind.Enum;
case PConst.Kind.interface: return vscode.SymbolKind.Interface;
- case PConst.Kind.memberFunction: return vscode.SymbolKind.Method;
+ case PConst.Kind.method: return vscode.SymbolKind.Method;
case PConst.Kind.memberVariable: return vscode.SymbolKind.Property;
case PConst.Kind.memberGetAccessor: return vscode.SymbolKind.Property;
case PConst.Kind.memberSetAccessor: return vscode.SymbolKind.Property;
diff --git a/extensions/typescript-language-features/src/features/implementationsCodeLens.ts b/extensions/typescript-language-features/src/features/implementationsCodeLens.ts
index f7e325c819d..c6ea7ca6dee 100644
--- a/extensions/typescript-language-features/src/features/implementationsCodeLens.ts
+++ b/extensions/typescript-language-features/src/features/implementationsCodeLens.ts
@@ -75,7 +75,7 @@ export default class TypeScriptImplementationsCodeLensProvider extends TypeScrip
return getSymbolRange(document, item);
case PConst.Kind.class:
- case PConst.Kind.memberFunction:
+ case PConst.Kind.method:
case PConst.Kind.memberVariable:
case PConst.Kind.memberGetAccessor:
case PConst.Kind.memberSetAccessor:
diff --git a/extensions/typescript-language-features/src/features/referencesCodeLens.ts b/extensions/typescript-language-features/src/features/referencesCodeLens.ts
index 7aa228f8f8e..0cf8d3f0a51 100644
--- a/extensions/typescript-language-features/src/features/referencesCodeLens.ts
+++ b/extensions/typescript-language-features/src/features/referencesCodeLens.ts
@@ -94,7 +94,7 @@ export class TypeScriptReferencesCodeLensProvider extends TypeScriptBaseCodeLens
case PConst.Kind.enum:
return getSymbolRange(document, item);
- case PConst.Kind.memberFunction:
+ case PConst.Kind.method:
case PConst.Kind.memberGetAccessor:
case PConst.Kind.memberSetAccessor:
case PConst.Kind.constructorImplementation:
diff --git a/extensions/typescript-language-features/src/features/workspaceSymbols.ts b/extensions/typescript-language-features/src/features/workspaceSymbols.ts
index 7b23d385c1f..e23c21eeb37 100644
--- a/extensions/typescript-language-features/src/features/workspaceSymbols.ts
+++ b/extensions/typescript-language-features/src/features/workspaceSymbols.ts
@@ -9,15 +9,21 @@ import { ITypeScriptServiceClient } from '../typescriptService';
import * as fileSchemes from '../utils/fileSchemes';
import { doesResourceLookLikeAJavaScriptFile, doesResourceLookLikeATypeScriptFile } from '../utils/languageDescription';
import * as typeConverters from '../utils/typeConverters';
+import * as PConst from '../protocol.const';
function getSymbolKind(item: Proto.NavtoItem): vscode.SymbolKind {
switch (item.kind) {
- case 'method': return vscode.SymbolKind.Method;
- case 'enum': return vscode.SymbolKind.Enum;
- case 'function': return vscode.SymbolKind.Function;
- case 'class': return vscode.SymbolKind.Class;
- case 'interface': return vscode.SymbolKind.Interface;
- case 'var': return vscode.SymbolKind.Variable;
+ case PConst.Kind.method: return vscode.SymbolKind.Method;
+ case PConst.Kind.enum: return vscode.SymbolKind.Enum;
+ case PConst.Kind.enumMember: return vscode.SymbolKind.EnumMember;
+ case PConst.Kind.function: return vscode.SymbolKind.Function;
+ case PConst.Kind.class: return vscode.SymbolKind.Class;
+ case PConst.Kind.interface: return vscode.SymbolKind.Interface;
+ case PConst.Kind.type: return vscode.SymbolKind.Class;
+ case PConst.Kind.memberVariable: return vscode.SymbolKind.Field;
+ case PConst.Kind.memberGetAccessor: return vscode.SymbolKind.Field;
+ case PConst.Kind.memberSetAccessor: return vscode.SymbolKind.Field;
+ case PConst.Kind.variable: return vscode.SymbolKind.Variable;
default: return vscode.SymbolKind.Variable;
}
}
@@ -25,7 +31,7 @@ function getSymbolKind(item: Proto.NavtoItem): vscode.SymbolKind {
class TypeScriptWorkspaceSymbolProvider implements vscode.WorkspaceSymbolProvider {
public constructor(
private readonly client: ITypeScriptServiceClient,
- private readonly modeIds: string[]
+ private readonly modeIds: readonly string[]
) { }
public async provideWorkspaceSymbols(
@@ -54,7 +60,7 @@ class TypeScriptWorkspaceSymbolProvider implements vscode.WorkspaceSymbolProvide
}
return response.body
- .filter(item => item.containerName && item.kind !== 'alias')
+ .filter(item => item.containerName || item.kind !== 'alias')
.map(item => this.toSymbolInformation(item));
}
@@ -115,7 +121,8 @@ class TypeScriptWorkspaceSymbolProvider implements vscode.WorkspaceSymbolProvide
export function register(
client: ITypeScriptServiceClient,
- modeIds: string[],
+ modeIds: readonly string[],
) {
- return vscode.languages.registerWorkspaceSymbolProvider(new TypeScriptWorkspaceSymbolProvider(client, modeIds));
+ return vscode.languages.registerWorkspaceSymbolProvider(
+ new TypeScriptWorkspaceSymbolProvider(client, modeIds));
}
diff --git a/extensions/typescript-language-features/src/protocol.const.ts b/extensions/typescript-language-features/src/protocol.const.ts
index a44c175f295..37fe42fd830 100644
--- a/extensions/typescript-language-features/src/protocol.const.ts
+++ b/extensions/typescript-language-features/src/protocol.const.ts
@@ -21,7 +21,7 @@ export class Kind {
public static readonly let = 'let';
public static readonly localFunction = 'local function';
public static readonly localVariable = 'local var';
- public static readonly memberFunction = 'method';
+ public static readonly method = 'method';
public static readonly memberGetAccessor = 'getter';
public static readonly memberSetAccessor = 'setter';
public static readonly memberVariable = 'property';
diff --git a/extensions/typescript-language-features/src/utils/typeConverters.ts b/extensions/typescript-language-features/src/utils/typeConverters.ts
index 0026eaa1232..78333b2da0b 100644
--- a/extensions/typescript-language-features/src/utils/typeConverters.ts
+++ b/extensions/typescript-language-features/src/utils/typeConverters.ts
@@ -107,7 +107,7 @@ export namespace SymbolKind {
case PConst.Kind.interface: return vscode.SymbolKind.Interface;
case PConst.Kind.indexSignature: return vscode.SymbolKind.Method;
case PConst.Kind.callSignature: return vscode.SymbolKind.Method;
- case PConst.Kind.memberFunction: return vscode.SymbolKind.Method;
+ case PConst.Kind.method: return vscode.SymbolKind.Method;
case PConst.Kind.memberVariable: return vscode.SymbolKind.Property;
case PConst.Kind.memberGetAccessor: return vscode.SymbolKind.Property;
case PConst.Kind.memberSetAccessor: return vscode.SymbolKind.Property;
diff --git a/extensions/typescript-language-features/yarn.lock b/extensions/typescript-language-features/yarn.lock
index 82daf1829dd..1934b8a6810 100644
--- a/extensions/typescript-language-features/yarn.lock
+++ b/extensions/typescript-language-features/yarn.lock
@@ -626,10 +626,10 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0:
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=
-typescript-vscode-sh-plugin@^0.6.8:
- version "0.6.8"
- resolved "https://registry.yarnpkg.com/typescript-vscode-sh-plugin/-/typescript-vscode-sh-plugin-0.6.8.tgz#60d5025f2ab814496824ee997b5e9fc12c5b7f1a"
- integrity sha512-XEh/GwBRsZKWQjPTODqWWiW8o8DyF7Yzfp/xvq1vyK5Z9JykFKAkx95BEmALv9x9dpc2RcLZHgVsKFXrtDABCw==
+typescript-vscode-sh-plugin@^0.6.10:
+ version "0.6.10"
+ resolved "https://registry.yarnpkg.com/typescript-vscode-sh-plugin/-/typescript-vscode-sh-plugin-0.6.10.tgz#f9fdac506a3adb698d52fd01723ec78e8a5fc09e"
+ integrity sha512-cYycpwLnYT2oS48tac+UvVRtIFHHTcHAz/g3N2HpYftuMEBvBcsGfe2SrlnrGCa1gMheTbo+twIHhsQu9ygdvg==
uri-js@^4.2.2:
version "4.2.2"
diff --git a/extensions/vscode-api-tests/package.json b/extensions/vscode-api-tests/package.json
index 8ac6b2806ca..c6fec9f7fb2 100644
--- a/extensions/vscode-api-tests/package.json
+++ b/extensions/vscode-api-tests/package.json
@@ -10,7 +10,7 @@
"onFileSystem:memfs",
"onDebug"
],
- "main": "./out/extension",
+ "main": "./out/web-playground/extension",
"engines": {
"vscode": "^1.25.0"
},
diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/debug.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/debug.test.ts
index be3509a2bfa..7a60146353b 100644
--- a/extensions/vscode-api-tests/src/singlefolder-tests/debug.test.ts
+++ b/extensions/vscode-api-tests/src/singlefolder-tests/debug.test.ts
@@ -37,8 +37,7 @@ suite('Debug', function () {
disposeAll(toDispose);
});
- this.retries(2);
- test('start debugging', async function () {
+ test.skip('start debugging', async function () {
let stoppedEvents = 0;
let variablesReceived: () => void;
let initializedReceived: () => void;
diff --git a/extensions/vscode-api-tests/src/extension.ts b/extensions/vscode-api-tests/src/web-playground/extension.ts
similarity index 100%
rename from extensions/vscode-api-tests/src/extension.ts
rename to extensions/vscode-api-tests/src/web-playground/extension.ts
diff --git a/package.json b/package.json
index 6fc79f7aba1..308165d4aa4 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "code-oss-dev",
"version": "1.44.0",
- "distro": "07db0bb3dc2da82ce33f2871e0093df1b9085d06",
+ "distro": "de617fbc2d2b5e151b9e4f1713fcd5d29dda04ea",
"author": {
"name": "Microsoft Corporation"
},
@@ -150,7 +150,7 @@
"source-map": "^0.4.4",
"style-loader": "^1.0.0",
"ts-loader": "^4.4.2",
- "typescript": "3.9.0-dev.20200304",
+ "typescript": "^3.9.0-dev.20200313",
"typescript-formatter": "7.1.0",
"underscore": "^1.8.2",
"vinyl": "^2.0.0",
diff --git a/src/vs/base/browser/ui/actionbar/actionbar.css b/src/vs/base/browser/ui/actionbar/actionbar.css
index a8ab18355b9..9b304e81a80 100644
--- a/src/vs/base/browser/ui/actionbar/actionbar.css
+++ b/src/vs/base/browser/ui/actionbar/actionbar.css
@@ -46,7 +46,8 @@
}
.monaco-action-bar .action-item .codicon {
- vertical-align: middle;
+ display: flex;
+ align-items: center;
}
.monaco-action-bar .action-label {
diff --git a/src/vs/base/browser/ui/actionbar/actionbar.ts b/src/vs/base/browser/ui/actionbar/actionbar.ts
index 523a242daaa..a226019523b 100644
--- a/src/vs/base/browser/ui/actionbar/actionbar.ts
+++ b/src/vs/base/browser/ui/actionbar/actionbar.ts
@@ -510,7 +510,7 @@ export class ActionBar extends Disposable implements IActionRunner {
} else if (event.equals(nextKey)) {
this.focusNext();
} else if (event.equals(KeyCode.Escape)) {
- this.cancel();
+ this._onDidCancel.fire();
} else if (this.isTriggerKeyEvent(event)) {
// Staying out of the else branch even if not triggered
if (this.options.triggerKeys && this.options.triggerKeys.keyDown) {
@@ -813,14 +813,6 @@ export class ActionBar extends Disposable implements IActionRunner {
}
}
- private cancel(): void {
- if (document.activeElement instanceof HTMLElement) {
- document.activeElement.blur(); // remove focus from focused action
- }
-
- this._onDidCancel.fire();
- }
-
run(action: IAction, context?: unknown): Promise {
return this._actionRunner.run(action, context);
}
diff --git a/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css b/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css
index 998b1e698a2..66d3d01872d 100644
--- a/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css
+++ b/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css
@@ -5,7 +5,7 @@
@font-face {
font-family: "codicon";
- src: url("./codicon.ttf?df9e07bbeddc0cf98f4d7a7c92bef3d8") format("truetype");
+ src: url("./codicon.ttf?5490083fcec741c6a0a08a366d2f9c98") format("truetype");
}
.codicon[class*='codicon-'] {
@@ -419,3 +419,4 @@
.codicon-bell-dot:before { content: "\f101" }
.codicon-debug-alt-2:before { content: "\f102" }
.codicon-debug-alt:before { content: "\f103" }
+.codicon-run-all:before { content: "\f104" }
diff --git a/src/vs/base/browser/ui/codiconLabel/codicon/codicon.ttf b/src/vs/base/browser/ui/codiconLabel/codicon/codicon.ttf
index 94b2533d5b9..5eac56a666c 100644
Binary files a/src/vs/base/browser/ui/codiconLabel/codicon/codicon.ttf and b/src/vs/base/browser/ui/codiconLabel/codicon/codicon.ttf differ
diff --git a/src/vs/base/browser/ui/iconLabel/iconLabel.ts b/src/vs/base/browser/ui/iconLabel/iconLabel.ts
index af06f86e2fa..3266a55df19 100644
--- a/src/vs/base/browser/ui/iconLabel/iconLabel.ts
+++ b/src/vs/base/browser/ui/iconLabel/iconLabel.ts
@@ -23,6 +23,7 @@ export interface IIconLabelValueOptions {
hideIcon?: boolean;
extraClasses?: string[];
italic?: boolean;
+ strikethrough?: boolean;
matches?: IMatch[];
labelEscapeNewLines?: boolean;
descriptionMatches?: IMatch[];
@@ -136,6 +137,10 @@ export class IconLabel extends Disposable {
if (options.italic) {
classes.push('italic');
}
+
+ if (options.strikethrough) {
+ classes.push('strikethrough');
+ }
}
this.domNode.className = classes.join(' ');
diff --git a/src/vs/base/browser/ui/iconLabel/iconlabel.css b/src/vs/base/browser/ui/iconLabel/iconlabel.css
index 8ee16195b5c..63a9056621c 100644
--- a/src/vs/base/browser/ui/iconLabel/iconlabel.css
+++ b/src/vs/base/browser/ui/iconLabel/iconlabel.css
@@ -60,6 +60,11 @@
font-style: italic;
}
+.monaco-icon-label.strikethrough > .monaco-icon-label-container > .monaco-icon-name-container > .label-name,
+.monaco-icon-label.strikethrough > .monaco-icon-description-container > .label-description {
+ text-decoration: line-through;
+}
+
.monaco-icon-label::after {
opacity: 0.75;
font-size: 90%;
diff --git a/src/vs/base/browser/ui/inputbox/inputBox.ts b/src/vs/base/browser/ui/inputbox/inputBox.ts
index e5ccf0f4bf2..b188a00df7e 100644
--- a/src/vs/base/browser/ui/inputbox/inputBox.ts
+++ b/src/vs/base/browser/ui/inputbox/inputBox.ts
@@ -295,6 +295,10 @@ export class InputBox extends Widget {
}
}
+ public isSelectionAtEnd(): boolean {
+ return this.input.selectionEnd === this.input.value.length && this.input.selectionStart === this.input.selectionEnd;
+ }
+
public enable(): void {
this.input.removeAttribute('disabled');
}
@@ -373,18 +377,6 @@ export class InputBox extends Widget {
const styles = this.stylesForType(this.message.type);
this.element.style.border = styles.border ? `1px solid ${styles.border}` : '';
- // ARIA Support
- let alertText: string;
- if (message.type === MessageType.ERROR) {
- alertText = nls.localize('alertErrorMessage', "Error: {0}", message.content);
- } else if (message.type === MessageType.WARNING) {
- alertText = nls.localize('alertWarningMessage', "Warning: {0}", message.content);
- } else {
- alertText = nls.localize('alertInfoMessage', "Info: {0}", message.content);
- }
-
- aria.alert(alertText);
-
if (this.hasFocus() || force) {
this._showMessage();
}
@@ -485,6 +477,18 @@ export class InputBox extends Widget {
layout: layout
});
+ // ARIA Support
+ let alertText: string;
+ if (this.message.type === MessageType.ERROR) {
+ alertText = nls.localize('alertErrorMessage', "Error: {0}", this.message.content);
+ } else if (this.message.type === MessageType.WARNING) {
+ alertText = nls.localize('alertWarningMessage', "Warning: {0}", this.message.content);
+ } else {
+ alertText = nls.localize('alertInfoMessage', "Info: {0}", this.message.content);
+ }
+
+ aria.alert(alertText);
+
this.state = 'open';
}
diff --git a/src/vs/base/browser/ui/list/list.ts b/src/vs/base/browser/ui/list/list.ts
index 5f86ea5989b..bb35d213a06 100644
--- a/src/vs/base/browser/ui/list/list.ts
+++ b/src/vs/base/browser/ui/list/list.ts
@@ -65,7 +65,7 @@ export interface IIdentityProvider {
export enum ListAriaRootRole {
/** default list structure role */
- LIST = 'list',
+ LIST = 'listbox',
/** default tree structure role */
TREE = 'tree',
diff --git a/src/vs/base/browser/ui/splitview/paneview.ts b/src/vs/base/browser/ui/splitview/paneview.ts
index dd943d0ed32..5fc898e82f0 100644
--- a/src/vs/base/browser/ui/splitview/paneview.ts
+++ b/src/vs/base/browser/ui/splitview/paneview.ts
@@ -22,6 +22,7 @@ export interface IPaneOptions {
minimumBodySize?: number;
maximumBodySize?: number;
expanded?: boolean;
+ orientation?: Orientation;
title: string;
}
@@ -50,6 +51,7 @@ export abstract class Pane extends Disposable implements IView {
private body!: HTMLElement;
protected _expanded: boolean;
+ protected _orientation: Orientation;
protected _preventCollapse?: boolean;
private expandedSize: number | undefined = undefined;
@@ -117,11 +119,12 @@ export abstract class Pane extends Disposable implements IView {
return headerSize + maximumBodySize;
}
- width: number = 0;
+ orthogonalSize: number = 0;
constructor(options: IPaneOptions) {
super();
this._expanded = typeof options.expanded === 'undefined' ? true : !!options.expanded;
+ this._orientation = typeof options.orientation === 'undefined' ? Orientation.VERTICAL : options.orientation;
this.ariaHeaderLabel = localize('viewSection', "{0} Section", options.title);
this._minimumBodySize = typeof options.minimumBodySize === 'number' ? options.minimumBodySize : 120;
this._maximumBodySize = typeof options.maximumBodySize === 'number' ? options.maximumBodySize : Number.POSITIVE_INFINITY;
@@ -206,14 +209,21 @@ export abstract class Pane extends Disposable implements IView {
this.body = append(this.element, $('.pane-body'));
this.renderBody(this.body);
+
+ if (!this.isExpanded()) {
+ this.body.remove();
+ }
}
- layout(height: number): void {
+ layout(size: number): void {
const headerSize = this.headerVisible ? Pane.HEADER_SIZE : 0;
+ const width = this._orientation === Orientation.VERTICAL ? this.orthogonalSize : size;
+ const height = this._orientation === Orientation.VERTICAL ? size - headerSize : this.orthogonalSize - headerSize;
+
if (this.isExpanded()) {
- this.layoutBody(height - headerSize, this.width);
- this.expandedSize = height;
+ this.layoutBody(height, width);
+ this.expandedSize = size;
}
}
@@ -391,7 +401,7 @@ export class PaneView extends Disposable {
private dndContext: IDndContext = { draggable: null };
private el: HTMLElement;
private paneItems: IPaneItem[] = [];
- private width: number = 0;
+ private orthogonalSize: number = 0;
private splitview: SplitView;
private orientation: Orientation;
private animationTimer: number | undefined = undefined;
@@ -417,7 +427,7 @@ export class PaneView extends Disposable {
const paneItem = { pane: pane, disposable: disposables };
this.paneItems.splice(index, 0, paneItem);
- pane.width = this.width;
+ pane.orthogonalSize = this.orthogonalSize;
this.splitview.addView(pane, size, index);
if (this.dnd) {
@@ -474,10 +484,10 @@ export class PaneView extends Disposable {
}
layout(height: number, width: number): void {
- this.width = width;
+ this.orthogonalSize = this.orientation === Orientation.VERTICAL ? width : height;
for (const paneItem of this.paneItems) {
- paneItem.pane.width = width;
+ paneItem.pane.orthogonalSize = this.orthogonalSize;
}
this.splitview.layout(this.orientation === Orientation.HORIZONTAL ? width : height);
diff --git a/src/vs/base/common/filters.ts b/src/vs/base/common/filters.ts
index 3740a128f1a..e16f6f4c708 100644
--- a/src/vs/base/common/filters.ts
+++ b/src/vs/base/common/filters.ts
@@ -392,7 +392,7 @@ export function anyScore(pattern: string, lowPattern: string, _patternPos: numbe
//#region --- fuzzyScore ---
-export function createMatches(score: undefined | FuzzyScore): IMatch[] {
+export function createMatches(score: undefined | FuzzyScore, offset = 0): IMatch[] {
if (typeof score === 'undefined') {
return [];
}
@@ -407,7 +407,7 @@ export function createMatches(score: undefined | FuzzyScore): IMatch[] {
if (last && last.end === pos) {
last.end = pos + 1;
} else {
- res.push({ start: pos, end: pos + 1 });
+ res.push({ start: pos + offset, end: pos + 1 + offset });
}
}
}
diff --git a/src/vs/base/common/hash.ts b/src/vs/base/common/hash.ts
index 1902e82c312..4b47073d8e5 100644
--- a/src/vs/base/common/hash.ts
+++ b/src/vs/base/common/hash.ts
@@ -3,6 +3,8 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
+import * as strings from 'vs/base/common/strings';
+
/**
* Return a hash value for an object.
*/
@@ -70,3 +72,235 @@ export class Hasher {
return this._value;
}
}
+
+const enum SHA1Constant {
+ BLOCK_SIZE = 64, // 512 / 8
+ UNICODE_REPLACEMENT = 0xFFFD,
+}
+
+function leftRotate(value: number, bits: number, totalBits: number = 32): number {
+ // delta + bits = totalBits
+ const delta = totalBits - bits;
+
+ // All ones, expect `delta` zeros aligned to the right
+ const mask = ~((1 << delta) - 1);
+
+ // Join (value left-shifted `bits` bits) with (masked value right-shifted `delta` bits)
+ return ((value << bits) | ((mask & value) >>> delta)) >>> 0;
+}
+
+function fill(dest: Uint8Array, index: number = 0, count: number = dest.byteLength, value: number = 0): void {
+ for (let i = 0; i < count; i++) {
+ dest[index + i] = value;
+ }
+}
+
+function leftPad(value: string, length: number, char: string = '0'): string {
+ while (value.length < length) {
+ value = char + value;
+ }
+ return value;
+}
+
+function toHexString(value: number, bitsize: number = 32): string {
+ return leftPad((value >>> 0).toString(16), bitsize / 4);
+}
+
+/**
+ * A SHA1 implementation that works with strings and does not allocate.
+ */
+export class StringSHA1 {
+ private static _bigBlock32 = new DataView(new ArrayBuffer(320)); // 80 * 4 = 320
+
+ private _h0 = 0x67452301;
+ private _h1 = 0xEFCDAB89;
+ private _h2 = 0x98BADCFE;
+ private _h3 = 0x10325476;
+ private _h4 = 0xC3D2E1F0;
+
+ private readonly _buff: Uint8Array;
+ private readonly _buffDV: DataView;
+ private _buffLen: number;
+ private _totalLen: number;
+ private _leftoverHighSurrogate: number;
+ private _finished: boolean;
+
+ constructor() {
+ this._buff = new Uint8Array(SHA1Constant.BLOCK_SIZE + 3 /* to fit any utf-8 */);
+ this._buffDV = new DataView(this._buff.buffer);
+ this._buffLen = 0;
+ this._totalLen = 0;
+ this._leftoverHighSurrogate = 0;
+ this._finished = false;
+ }
+
+ public update(str: string): void {
+ const strLen = str.length;
+ if (strLen === 0) {
+ return;
+ }
+
+ const buff = this._buff;
+ let buffLen = this._buffLen;
+ let leftoverHighSurrogate = this._leftoverHighSurrogate;
+ let charCode: number;
+ let offset: number;
+
+ if (leftoverHighSurrogate !== 0) {
+ charCode = leftoverHighSurrogate;
+ offset = -1;
+ leftoverHighSurrogate = 0;
+ } else {
+ charCode = str.charCodeAt(0);
+ offset = 0;
+ }
+
+ while (true) {
+ let codePoint = charCode;
+ if (strings.isHighSurrogate(charCode)) {
+ if (offset + 1 < strLen) {
+ const nextCharCode = str.charCodeAt(offset + 1);
+ if (strings.isLowSurrogate(nextCharCode)) {
+ offset++;
+ codePoint = strings.computeCodePoint(charCode, nextCharCode);
+ } else {
+ // illegal => unicode replacement character
+ codePoint = SHA1Constant.UNICODE_REPLACEMENT;
+ }
+ } else {
+ // last character is a surrogate pair
+ leftoverHighSurrogate = charCode;
+ break;
+ }
+ } else if (strings.isLowSurrogate(charCode)) {
+ // illegal => unicode replacement character
+ codePoint = SHA1Constant.UNICODE_REPLACEMENT;
+ }
+
+ buffLen = this._push(buff, buffLen, codePoint);
+ offset++;
+ if (offset < strLen) {
+ charCode = str.charCodeAt(offset);
+ } else {
+ break;
+ }
+ }
+
+ this._buffLen = buffLen;
+ this._leftoverHighSurrogate = leftoverHighSurrogate;
+ }
+
+ private _push(buff: Uint8Array, buffLen: number, codePoint: number): number {
+ if (codePoint < 0x0080) {
+ buff[buffLen++] = codePoint;
+ } else if (codePoint < 0x0800) {
+ buff[buffLen++] = 0b11000000 | ((codePoint & 0b00000000000000000000011111000000) >>> 6);
+ buff[buffLen++] = 0b10000000 | ((codePoint & 0b00000000000000000000000000111111) >>> 0);
+ } else if (codePoint < 0x10000) {
+ buff[buffLen++] = 0b11100000 | ((codePoint & 0b00000000000000001111000000000000) >>> 12);
+ buff[buffLen++] = 0b10000000 | ((codePoint & 0b00000000000000000000111111000000) >>> 6);
+ buff[buffLen++] = 0b10000000 | ((codePoint & 0b00000000000000000000000000111111) >>> 0);
+ } else {
+ buff[buffLen++] = 0b11110000 | ((codePoint & 0b00000000000111000000000000000000) >>> 18);
+ buff[buffLen++] = 0b10000000 | ((codePoint & 0b00000000000000111111000000000000) >>> 12);
+ buff[buffLen++] = 0b10000000 | ((codePoint & 0b00000000000000000000111111000000) >>> 6);
+ buff[buffLen++] = 0b10000000 | ((codePoint & 0b00000000000000000000000000111111) >>> 0);
+ }
+
+ if (buffLen >= SHA1Constant.BLOCK_SIZE) {
+ this._step();
+ buffLen -= SHA1Constant.BLOCK_SIZE;
+ this._totalLen += SHA1Constant.BLOCK_SIZE;
+ // take last 3 in case of UTF8 overflow
+ buff[0] = buff[SHA1Constant.BLOCK_SIZE + 0];
+ buff[1] = buff[SHA1Constant.BLOCK_SIZE + 1];
+ buff[2] = buff[SHA1Constant.BLOCK_SIZE + 2];
+ }
+
+ return buffLen;
+ }
+
+ public digest(): string {
+ if (!this._finished) {
+ this._finished = true;
+ if (this._leftoverHighSurrogate) {
+ // illegal => unicode replacement character
+ this._leftoverHighSurrogate = 0;
+ this._buffLen = this._push(this._buff, this._buffLen, SHA1Constant.UNICODE_REPLACEMENT);
+ }
+ this._totalLen += this._buffLen;
+ this._wrapUp();
+ }
+
+ return toHexString(this._h0) + toHexString(this._h1) + toHexString(this._h2) + toHexString(this._h3) + toHexString(this._h4);
+ }
+
+ private _wrapUp(): void {
+ this._buff[this._buffLen++] = 0x80;
+ fill(this._buff, this._buffLen);
+
+ if (this._buffLen > 56) {
+ this._step();
+ fill(this._buff);
+ }
+
+ // this will fit because the mantissa can cover up to 52 bits
+ const ml = 8 * this._totalLen;
+
+ this._buffDV.setUint32(56, Math.floor(ml / 4294967296), false);
+ this._buffDV.setUint32(60, ml % 4294967296, false);
+
+ this._step();
+ }
+
+ private _step(): void {
+ const bigBlock32 = StringSHA1._bigBlock32;
+ const data = this._buffDV;
+
+ for (let j = 0; j < 64 /* 16*4 */; j += 4) {
+ bigBlock32.setUint32(j, data.getUint32(j, false), false);
+ }
+
+ for (let j = 64; j < 320 /* 80*4 */; j += 4) {
+ bigBlock32.setUint32(j, leftRotate((bigBlock32.getUint32(j - 12, false) ^ bigBlock32.getUint32(j - 32, false) ^ bigBlock32.getUint32(j - 56, false) ^ bigBlock32.getUint32(j - 64, false)), 1), false);
+ }
+
+ let a = this._h0;
+ let b = this._h1;
+ let c = this._h2;
+ let d = this._h3;
+ let e = this._h4;
+
+ let f: number, k: number;
+ let temp: number;
+
+ for (let j = 0; j < 80; j++) {
+ if (j < 20) {
+ f = (b & c) | ((~b) & d);
+ k = 0x5A827999;
+ } else if (j < 40) {
+ f = b ^ c ^ d;
+ k = 0x6ED9EBA1;
+ } else if (j < 60) {
+ f = (b & c) | (b & d) | (c & d);
+ k = 0x8F1BBCDC;
+ } else {
+ f = b ^ c ^ d;
+ k = 0xCA62C1D6;
+ }
+
+ temp = (leftRotate(a, 5) + f + e + k + bigBlock32.getUint32(j * 4, false)) & 0xffffffff;
+ e = d;
+ d = c;
+ c = leftRotate(b, 30);
+ b = a;
+ a = temp;
+ }
+
+ this._h0 = (this._h0 + a) & 0xffffffff;
+ this._h1 = (this._h1 + b) & 0xffffffff;
+ this._h2 = (this._h2 + c) & 0xffffffff;
+ this._h3 = (this._h3 + d) & 0xffffffff;
+ this._h4 = (this._h4 + e) & 0xffffffff;
+ }
+}
diff --git a/src/vs/base/common/strings.ts b/src/vs/base/common/strings.ts
index 4a26a87ee2f..21277fc2e70 100644
--- a/src/vs/base/common/strings.ts
+++ b/src/vs/base/common/strings.ts
@@ -428,29 +428,27 @@ export function commonSuffixLength(a: string, b: string): number {
return len;
}
-// --- unicode
-// http://en.wikipedia.org/wiki/Surrogate_pair
-// Returns the code point starting at a specified index in a string
-// Code points U+0000 to U+D7FF and U+E000 to U+FFFF are represented on a single character
-// Code points U+10000 to U+10FFFF are represented on two consecutive characters
-//export function getUnicodePoint(str:string, index:number, len:number):number {
-// const chrCode = str.charCodeAt(index);
-// if (0xD800 <= chrCode && chrCode <= 0xDBFF && index + 1 < len) {
-// const nextChrCode = str.charCodeAt(index + 1);
-// if (0xDC00 <= nextChrCode && nextChrCode <= 0xDFFF) {
-// return (chrCode - 0xD800) << 10 + (nextChrCode - 0xDC00) + 0x10000;
-// }
-// }
-// return chrCode;
-//}
+/**
+ * See http://en.wikipedia.org/wiki/Surrogate_pair
+ */
export function isHighSurrogate(charCode: number): boolean {
return (0xD800 <= charCode && charCode <= 0xDBFF);
}
+/**
+ * See http://en.wikipedia.org/wiki/Surrogate_pair
+ */
export function isLowSurrogate(charCode: number): boolean {
return (0xDC00 <= charCode && charCode <= 0xDFFF);
}
+/**
+ * See http://en.wikipedia.org/wiki/Surrogate_pair
+ */
+export function computeCodePoint(highSurrogate: number, lowSurrogate: number): number {
+ return ((highSurrogate - 0xD800) << 10) + (lowSurrogate - 0xDC00) + 0x10000;
+}
+
/**
* get the code point that begins at offset `offset`
*/
@@ -459,7 +457,7 @@ export function getNextCodePoint(str: string, len: number, offset: number): numb
if (isHighSurrogate(charCode) && offset + 1 < len) {
const nextCharCode = str.charCodeAt(offset + 1);
if (isLowSurrogate(nextCharCode)) {
- return ((charCode - 0xD800) << 10) + (nextCharCode - 0xDC00) + 0x10000;
+ return computeCodePoint(charCode, nextCharCode);
}
}
return charCode;
@@ -473,7 +471,7 @@ function getPrevCodePoint(str: string, offset: number): number {
if (isLowSurrogate(charCode) && offset > 1) {
const prevCharCode = str.charCodeAt(offset - 2);
if (isHighSurrogate(prevCharCode)) {
- return ((prevCharCode - 0xD800) << 10) + (charCode - 0xDC00) + 0x10000;
+ return computeCodePoint(prevCharCode, charCode);
}
}
return charCode;
diff --git a/src/vs/base/common/uri.ts b/src/vs/base/common/uri.ts
index bdc79116318..0108b531460 100644
--- a/src/vs/base/common/uri.ts
+++ b/src/vs/base/common/uri.ts
@@ -85,6 +85,7 @@ const _regexp = /^(([^:/?#]+?):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/;
* (http://tools.ietf.org/html/rfc3986#section-3) with minimal validation
* and encoding.
*
+ * ```txt
* foo://example.com:8042/over/there?name=ferret#nose
* \_/ \______________/\_________/ \_________/ \__/
* | | | | |
@@ -92,6 +93,7 @@ const _regexp = /^(([^:/?#]+?):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/;
* | _____________________|__
* / \ / \
* urn:example:animal:ferret:nose
+ * ```
*/
export class URI implements UriComponents {
diff --git a/src/vs/base/parts/quickinput/browser/media/quickInput.css b/src/vs/base/parts/quickinput/browser/media/quickInput.css
index 1a5f4d35f6c..45c205f92a5 100644
--- a/src/vs/base/parts/quickinput/browser/media/quickInput.css
+++ b/src/vs/base/parts/quickinput/browser/media/quickInput.css
@@ -186,6 +186,11 @@
align-items: center;
}
+.quick-input-list .quick-input-list-rows > .quick-input-list-row .monaco-icon-label,
+.quick-input-list .quick-input-list-rows > .quick-input-list-row .monaco-icon-label .monaco-icon-label-container > .monaco-icon-name-container {
+ flex: 1; /* make sure the icon label grows within the row */
+}
+
.quick-input-list .quick-input-list-rows > .quick-input-list-row .codicon {
vertical-align: sub;
}
@@ -194,6 +199,10 @@
opacity: 1;
}
+.quick-input-list .quick-input-list-entry .quick-input-list-entry-keybinding {
+ margin-right: 8px; /* separate from the separator label or scrollbar if any */
+}
+
.quick-input-list .quick-input-list-label-meta {
opacity: 0.7;
line-height: normal;
@@ -205,17 +214,13 @@
font-weight: bold;
}
-.quick-input-list .quick-input-list-separator {
- margin-right: 18px;
-}
-
-.quick-input-list .quick-input-list-entry.has-actions:hover .quick-input-list-separator,
-.quick-input-list .monaco-list-row.focused .quick-input-list-entry.has-actions .quick-input-list-separator {
- margin-right: 0;
+.quick-input-list .quick-input-list-entry .quick-input-list-separator {
+ margin-right: 8px; /* separate from keybindings or actions */
}
.quick-input-list .quick-input-list-entry-action-bar {
- display: none;
+ display: flex;
+ visibility: hidden; /* not using display: none here to not flicker too much */
flex: 0;
overflow: visible;
}
@@ -231,15 +236,16 @@
margin-top: 1px;
}
-.quick-input-list .quick-input-list-entry-action-bar ul:first-child .action-label.codicon {
- margin-left: 2px;
+.quick-input-list .quick-input-list-entry-action-bar {
+ margin-right: 4px; /* separate from scrollbar */
}
-.quick-input-list .quick-input-list-entry-action-bar ul:last-child .action-label.codicon {
- margin-right: 8px;
+.quick-input-list .quick-input-list-entry-action-bar .action-label.codicon {
+ margin-right: 4px; /* separate actions */
}
+.quick-input-list .quick-input-list-entry.always-visible-actions .quick-input-list-entry-action-bar,
.quick-input-list .quick-input-list-entry:hover .quick-input-list-entry-action-bar,
.quick-input-list .monaco-list-row.focused .quick-input-list-entry-action-bar {
- display: flex;
+ visibility: visible;
}
diff --git a/src/vs/base/parts/quickinput/browser/quickInput.ts b/src/vs/base/parts/quickinput/browser/quickInput.ts
index 7defd5cf056..3479a6bbead 100644
--- a/src/vs/base/parts/quickinput/browser/quickInput.ts
+++ b/src/vs/base/parts/quickinput/browser/quickInput.ts
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./media/quickInput';
-import { IQuickPickItem, IPickOptions, IInputOptions, IQuickNavigateConfiguration, IQuickPick, IQuickInput, IQuickInputButton, IInputBox, IQuickPickItemButtonEvent, QuickPickInput, IQuickPickSeparator, IKeyMods } from 'vs/base/parts/quickinput/common/quickInput';
+import { IQuickPickItem, IPickOptions, IInputOptions, IQuickNavigateConfiguration, IQuickPick, IQuickInput, IQuickInputButton, IInputBox, IQuickPickItemButtonEvent, QuickPickInput, IQuickPickSeparator, IKeyMods, IQuickPickAcceptEvent } from 'vs/base/parts/quickinput/common/quickInput';
import * as dom from 'vs/base/browser/dom';
import { CancellationToken } from 'vs/base/common/cancellation';
import { QuickInputList } from './quickInputList';
@@ -379,11 +379,12 @@ class QuickPick extends QuickInput implements IQuickPi
private _ariaLabel = QuickPick.DEFAULT_ARIA_LABEL;
private _placeholder: string | undefined;
private readonly onDidChangeValueEmitter = this._register(new Emitter());
- private readonly onDidAcceptEmitter = this._register(new Emitter());
+ private readonly onDidAcceptEmitter = this._register(new Emitter());
private readonly onDidCustomEmitter = this._register(new Emitter());
private _items: Array = [];
private itemsUpdated = false;
private _canSelectMany = false;
+ private _canAcceptInBackground = false;
private _matchOnDescription = false;
private _matchOnDetail = false;
private _matchOnLabel = true;
@@ -462,6 +463,14 @@ class QuickPick extends QuickInput implements IQuickPi
this.update();
}
+ get canAcceptInBackground() {
+ return this._canAcceptInBackground;
+ }
+
+ set canAcceptInBackground(canAcceptInBackground: boolean) {
+ this._canAcceptInBackground = canAcceptInBackground;
+ }
+
get matchOnDescription() {
return this._matchOnDescription;
}
@@ -663,6 +672,22 @@ class QuickPick extends QuickInput implements IQuickPi
this.ui.list.domFocus();
}
event.preventDefault();
+ break;
+ case KeyCode.RightArrow:
+ if (!this._canAcceptInBackground) {
+ return; // needs to be enabled
+ }
+
+ if (!this.ui.inputBox.isSelectionAtEnd()) {
+ return; // ensure input box selection at end
+ }
+
+ if (this.activeItems[0]) {
+ this._selectedItems = [this.activeItems[0]];
+ this.onDidChangeSelectionEmitter.fire(this.selectedItems);
+ this.onDidAcceptEmitter.fire({ inBackground: true });
+ }
+
break;
}
}));
@@ -671,7 +696,7 @@ class QuickPick extends QuickInput implements IQuickPi
this._selectedItems = [this.activeItems[0]];
this.onDidChangeSelectionEmitter.fire(this.selectedItems);
}
- this.onDidAcceptEmitter.fire(undefined);
+ this.onDidAcceptEmitter.fire({ inBackground: false });
}));
this.visibleDisposables.add(this.ui.onDidCustom(() => {
this.onDidCustomEmitter.fire(undefined);
@@ -686,7 +711,7 @@ class QuickPick extends QuickInput implements IQuickPi
this._activeItems = focusedItems as T[];
this.onDidChangeActiveEmitter.fire(focusedItems as T[]);
}));
- this.visibleDisposables.add(this.ui.list.onDidChangeSelection(selectedItems => {
+ this.visibleDisposables.add(this.ui.list.onDidChangeSelection(({ items: selectedItems, event }) => {
if (this.canSelectMany) {
if (selectedItems.length) {
this.ui.list.setSelectedElements([]);
@@ -699,7 +724,7 @@ class QuickPick extends QuickInput implements IQuickPi
this._selectedItems = selectedItems as T[];
this.onDidChangeSelectionEmitter.fire(selectedItems as T[]);
if (selectedItems.length) {
- this.onDidAcceptEmitter.fire(undefined);
+ this.onDidAcceptEmitter.fire({ inBackground: event instanceof MouseEvent && event.button === 1 /* mouse middle click */ });
}
}));
this.visibleDisposables.add(this.ui.list.onChangedCheckedElements(checkedItems => {
@@ -762,7 +787,7 @@ class QuickPick extends QuickInput implements IQuickPi
if (wasTriggerKeyPressed && this.activeItems[0]) {
this._selectedItems = [this.activeItems[0]];
this.onDidChangeSelectionEmitter.fire(this.selectedItems);
- this.onDidAcceptEmitter.fire(undefined);
+ this.onDidAcceptEmitter.fire({ inBackground: false });
}
});
}
diff --git a/src/vs/base/parts/quickinput/browser/quickInputBox.ts b/src/vs/base/parts/quickinput/browser/quickInputBox.ts
index 6ae2b873034..2e796764a6e 100644
--- a/src/vs/base/parts/quickinput/browser/quickInputBox.ts
+++ b/src/vs/base/parts/quickinput/browser/quickInputBox.ts
@@ -54,7 +54,11 @@ export class QuickInputBox extends Disposable {
this.inputBox.select(range);
}
- setPlaceholder(placeholder: string) {
+ isSelectionAtEnd(): boolean {
+ return this.inputBox.isSelectionAtEnd();
+ }
+
+ setPlaceholder(placeholder: string): void {
this.inputBox.setPlaceHolder(placeholder);
}
@@ -90,11 +94,11 @@ export class QuickInputBox extends Disposable {
return this.inputBox.hasFocus();
}
- setAttribute(name: string, value: string) {
+ setAttribute(name: string, value: string): void {
this.inputBox.inputElement.setAttribute(name, value);
}
- removeAttribute(name: string) {
+ removeAttribute(name: string): void {
this.inputBox.inputElement.removeAttribute(name);
}
@@ -118,7 +122,7 @@ export class QuickInputBox extends Disposable {
this.inputBox.layout();
}
- style(styles: IInputBoxStyles) {
+ style(styles: IInputBoxStyles): void {
this.inputBox.style(styles);
}
}
diff --git a/src/vs/base/parts/quickinput/browser/quickInputList.ts b/src/vs/base/parts/quickinput/browser/quickInputList.ts
index c816f564689..d339c421185 100644
--- a/src/vs/base/parts/quickinput/browser/quickInputList.ts
+++ b/src/vs/base/parts/quickinput/browser/quickInputList.ts
@@ -25,7 +25,8 @@ import { Action } from 'vs/base/common/actions';
import { getIconClass } from 'vs/base/parts/quickinput/browser/quickInputUtils';
import { withNullAsUndefined } from 'vs/base/common/types';
import { IQuickInputOptions } from 'vs/base/parts/quickinput/browser/quickInput';
-import { IListOptions, List, IListStyles } from 'vs/base/browser/ui/list/listWidget';
+import { IListOptions, List, IListStyles, IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget';
+import { KeybindingLabel } from 'vs/base/browser/ui/keybindingLabel/keybindingLabel';
const $ = dom.$;
@@ -79,6 +80,7 @@ interface IListElementTemplateData {
entry: HTMLDivElement;
checkbox: HTMLInputElement;
label: IconLabel;
+ keybinding: KeybindingLabel;
detail: HighlightedLabel;
separator: HTMLDivElement;
actionBar: ActionBar;
@@ -118,6 +120,10 @@ class ListElementRenderer implements IListRenderer element.saneLabel },
openController: { shouldOpen: () => false }, // Workaround #58124
setRowLineHeight: false,
multipleSelectionSupport: false,
horizontalScrolling: false,
+ accessibilityProvider
} as IListOptions);
this.list.getHTMLElement().id = id;
this.disposables.push(this.list);
@@ -303,7 +318,7 @@ export class QuickInputList {
@memoize
get onDidChangeSelection() {
- return Event.map(this.list.onDidChangeSelection, e => e.elements.map(e => e.item));
+ return Event.map(this.list.onDidChangeSelection, e => ({ items: e.elements.map(e => e.item), event: e.browserEvent }));
}
getAllVisibleChecked() {
@@ -606,3 +621,9 @@ function compareEntries(elementA: ListElement, elementB: ListElement, lookFor: s
return compareAnything(elementA.saneLabel, elementB.saneLabel, lookFor);
}
+
+class QuickInputAccessibilityProvider implements IAccessibilityProvider {
+ getAriaLabel(element: ListElement): string | null {
+ return element.saneAriaLabel;
+ }
+}
diff --git a/src/vs/base/parts/quickinput/common/quickInput.ts b/src/vs/base/parts/quickinput/common/quickInput.ts
index 7a7d14cb0c6..c6ad0c93b76 100644
--- a/src/vs/base/parts/quickinput/common/quickInput.ts
+++ b/src/vs/base/parts/quickinput/common/quickInput.ts
@@ -24,10 +24,22 @@ export interface IQuickPickItem {
ariaLabel?: string;
description?: string;
detail?: string;
+ /**
+ * Allows to show a keybinding next to the item to indicate
+ * how the item can be triggered outside of the picker using
+ * keyboard shortcut.
+ */
+ keybinding?: ResolvedKeybinding;
iconClasses?: string[];
italic?: boolean;
+ strikethrough?: boolean;
highlights?: IQuickPickItemHighlights;
buttons?: IQuickInputButton[];
+ /**
+ * Wether to always show the buttons. By default buttons
+ * are only visible when hovering over them with the mouse
+ */
+ buttonsAlwaysVisible?: boolean;
picked?: boolean;
alwaysShow?: boolean;
}
@@ -164,6 +176,15 @@ export interface IQuickInput extends IDisposable {
hide(): void;
}
+export interface IQuickPickAcceptEvent {
+
+ /**
+ * Signals if the picker item is to be accepted
+ * in the background while keeping the picker open.
+ */
+ inBackground: boolean;
+}
+
export interface IQuickPick extends IQuickInput {
value: string;
@@ -180,7 +201,14 @@ export interface IQuickPick extends IQuickInput {
readonly onDidChangeValue: Event;
- readonly onDidAccept: Event;
+ readonly onDidAccept: Event;
+
+ /**
+ * If enabled, will fire the `onDidAccept` event when
+ * pressing the arrow-right key with the idea of accepting
+ * the selected item without closing the picker.
+ */
+ canAcceptInBackground: boolean;
ok: boolean | 'default';
diff --git a/src/vs/base/parts/tree/browser/treeViewModel.ts b/src/vs/base/parts/tree/browser/treeViewModel.ts
index 823f0bb0a9d..b78c4d8184f 100644
--- a/src/vs/base/parts/tree/browser/treeViewModel.ts
+++ b/src/vs/base/parts/tree/browser/treeViewModel.ts
@@ -45,8 +45,7 @@ export class HeightMap {
totalSize = viewItem.top + viewItem.height;
}
- let boundSplice = this.heightMap.splice.bind(this.heightMap, i, 0);
-
+ const startingIndex = i;
let itemsToInsert: IViewItem[] = [];
while (item = iterator.next()) {
@@ -58,7 +57,7 @@ export class HeightMap {
sizeDiff += viewItem.height;
}
- boundSplice.apply(this.heightMap, itemsToInsert);
+ this.heightMap.splice(startingIndex, 0, ...itemsToInsert);
for (j = i; j < this.heightMap.length; j++) {
viewItem = this.heightMap[j];
diff --git a/src/vs/base/test/common/hash.test.ts b/src/vs/base/test/common/hash.test.ts
index 58b5904b63d..3225caf7b23 100644
--- a/src/vs/base/test/common/hash.test.ts
+++ b/src/vs/base/test/common/hash.test.ts
@@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
-import { hash } from 'vs/base/common/hash';
+import { hash, StringSHA1 } from 'vs/base/common/hash';
suite('Hash', () => {
test('string', () => {
@@ -53,4 +53,28 @@ suite('Hash', () => {
assert.notEqual(a, b);
});
+ function checkSHA1(strings: string[], expected: string) {
+ const hash = new StringSHA1();
+ for (const str of strings) {
+ hash.update(str);
+ }
+ const actual = hash.digest();
+ assert.equal(actual, expected);
+ }
+
+ test('sha1-1', () => {
+ checkSHA1(['\udd56'], '9bdb77276c1852e1fb067820472812fcf6084024');
+ });
+
+ test('sha1-2', () => {
+ checkSHA1(['\udb52'], '9bdb77276c1852e1fb067820472812fcf6084024');
+ });
+
+ test('sha1-3', () => {
+ checkSHA1(['\uda02ꑍ'], '9b483a471f22fe7e09d83f221871a987244bbd3f');
+ });
+
+ test('sha1-4', () => {
+ checkSHA1(['hello'], 'aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d');
+ });
});
diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts
index 0e6c2358199..e8532f91590 100644
--- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts
+++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts
@@ -49,17 +49,16 @@ import { IFileService } from 'vs/platform/files/common/files';
import { DiskFileSystemProvider } from 'vs/platform/files/electron-browser/diskFileSystemProvider';
import { Schemas } from 'vs/base/common/network';
import { IProductService } from 'vs/platform/product/common/productService';
-import { IUserDataSyncService, IUserDataSyncStoreService, registerConfiguration, IUserDataSyncLogService, IUserDataSyncUtilService, ISettingsSyncService, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync';
+import { IUserDataSyncService, IUserDataSyncStoreService, registerConfiguration, IUserDataSyncLogService, IUserDataSyncUtilService, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync';
import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService';
import { UserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSyncStoreService';
-import { UserDataSyncChannel, UserDataSyncUtilServiceClient, SettingsSyncChannel, UserDataAutoSyncChannel, UserDataSyncStoreServiceChannel, UserDataSyncBackupStoreServiceChannel } from 'vs/platform/userDataSync/common/userDataSyncIpc';
+import { UserDataSyncChannel, UserDataSyncUtilServiceClient, UserDataAutoSyncChannel, UserDataSyncStoreServiceChannel, UserDataSyncBackupStoreServiceChannel } from 'vs/platform/userDataSync/common/userDataSyncIpc';
import { IElectronService } from 'vs/platform/electron/node/electron';
import { LoggerService } from 'vs/platform/log/node/loggerService';
import { UserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSyncLog';
import { ICredentialsService } from 'vs/platform/credentials/common/credentials';
import { KeytarCredentialsService } from 'vs/platform/credentials/node/credentialsService';
import { UserDataAutoSyncService } from 'vs/platform/userDataSync/electron-browser/userDataAutoSyncService';
-import { SettingsSynchroniser } from 'vs/platform/userDataSync/common/settingsSync';
import { NativeStorageService } from 'vs/platform/storage/node/storageService';
import { GlobalStorageDatabaseChannelClient } from 'vs/platform/storage/node/storageIpc';
import { IStorageService } from 'vs/platform/storage/common/storage';
@@ -197,7 +196,6 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat
services.set(IUserDataSyncStoreService, new SyncDescriptor(UserDataSyncStoreService));
services.set(IUserDataSyncBackupStoreService, new SyncDescriptor(UserDataSyncBackupStoreService));
services.set(IUserDataSyncEnablementService, new SyncDescriptor(UserDataSyncEnablementService));
- services.set(ISettingsSyncService, new SyncDescriptor(SettingsSynchroniser));
services.set(IUserDataSyncService, new SyncDescriptor(UserDataSyncService));
registerConfiguration();
@@ -229,10 +227,6 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat
const userDataSyncBackupStoreServiceChannel = new UserDataSyncBackupStoreServiceChannel(userDataSyncBackupStoreService);
server.registerChannel('userDataSyncBackupStoreService', userDataSyncBackupStoreServiceChannel);
- const settingsSyncService = accessor.get(ISettingsSyncService);
- const settingsSyncChannel = new SettingsSyncChannel(settingsSyncService);
- server.registerChannel('settingsSync', settingsSyncChannel);
-
const userDataSyncService = accessor.get(IUserDataSyncService);
const userDataSyncChannel = new UserDataSyncChannel(userDataSyncService);
server.registerChannel('userDataSync', userDataSyncChannel);
diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts
index de0a7dff13a..5ee3e79c644 100644
--- a/src/vs/code/electron-main/window.ts
+++ b/src/vs/code/electron-main/window.ts
@@ -28,12 +28,13 @@ import { resolveMarketplaceHeaders } from 'vs/platform/extensionManagement/commo
import { IThemeMainService } from 'vs/platform/theme/electron-main/themeMainService';
import { endsWith } from 'vs/base/common/strings';
import { RunOnceScheduler } from 'vs/base/common/async';
-import { IFileService } from 'vs/platform/files/common/files';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IDialogMainService } from 'vs/platform/dialogs/electron-main/dialogs';
import { mnemonicButtonLabel } from 'vs/base/common/labels';
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
+import { IStorageMainService } from 'vs/platform/storage/node/storageMainService';
+import { IFileService } from 'vs/platform/files/common/files';
const RUN_TEXTMATE_IN_WORKER = false;
@@ -97,6 +98,7 @@ export class CodeWindow extends Disposable implements ICodeWindow {
@ILogService private readonly logService: ILogService,
@IEnvironmentService private readonly environmentService: IEnvironmentService,
@IFileService private readonly fileService: IFileService,
+ @IStorageMainService private readonly storageService: IStorageMainService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IThemeMainService private readonly themeMainService: IThemeMainService,
@IWorkspacesMainService private readonly workspacesMainService: IWorkspacesMainService,
@@ -226,7 +228,11 @@ export class CodeWindow extends Disposable implements ICodeWindow {
this.createTouchBar();
// Request handling
- this.marketplaceHeadersPromise = resolveMarketplaceHeaders(product.version, this.environmentService, this.fileService);
+ const that = this;
+ this.marketplaceHeadersPromise = resolveMarketplaceHeaders(product.version, this.environmentService, this.fileService, {
+ get(key) { return that.storageService.get(key); },
+ store(key, value) { that.storageService.store(key, value); }
+ });
// Eventing
this.registerListeners();
diff --git a/src/vs/editor/browser/viewParts/lines/viewLine.ts b/src/vs/editor/browser/viewParts/lines/viewLine.ts
index 9a7011dc1d4..46d36912654 100644
--- a/src/vs/editor/browser/viewParts/lines/viewLine.ts
+++ b/src/vs/editor/browser/viewParts/lines/viewLine.ts
@@ -616,7 +616,15 @@ class RenderedViewLine implements IRenderedViewLine {
if (!r || r.length === 0) {
return -1;
}
- return r[0].left;
+ const result = r[0].left;
+ if (this.input.isBasicASCII) {
+ const charOffset = this._characterMapping.getAbsoluteOffsets();
+ const expectedResult = Math.round(this.input.spaceWidth * charOffset[column - 1]);
+ if (Math.abs(expectedResult - result) <= 1) {
+ return expectedResult;
+ }
+ }
+ return result;
}
private _readRawVisibleRangesForRange(domNode: FastDomNode, startColumn: number, endColumn: number, context: DomReadingContext): HorizontalRange[] | null {
diff --git a/src/vs/editor/common/model/textModel.ts b/src/vs/editor/common/model/textModel.ts
index deb082c6c3c..59e61125845 100644
--- a/src/vs/editor/common/model/textModel.ts
+++ b/src/vs/editor/common/model/textModel.ts
@@ -1759,6 +1759,7 @@ export class TextModel extends Disposable implements model.ITextModel {
if (ranges.length > 0) {
this._emitModelTokensChangedEvent({
tokenizationSupportChanged: false,
+ semanticTokensApplied: false,
ranges: ranges
});
}
@@ -1769,6 +1770,7 @@ export class TextModel extends Disposable implements model.ITextModel {
this._emitModelTokensChangedEvent({
tokenizationSupportChanged: false,
+ semanticTokensApplied: tokens !== null,
ranges: [{ fromLineNumber: 1, toLineNumber: this.getLineCount() }]
});
}
@@ -1783,6 +1785,7 @@ export class TextModel extends Disposable implements model.ITextModel {
this._tokens.flush();
this._emitModelTokensChangedEvent({
tokenizationSupportChanged: true,
+ semanticTokensApplied: false,
ranges: [{
fromLineNumber: 1,
toLineNumber: this._buffer.getLineCount()
@@ -1795,6 +1798,7 @@ export class TextModel extends Disposable implements model.ITextModel {
this._emitModelTokensChangedEvent({
tokenizationSupportChanged: false,
+ semanticTokensApplied: false,
ranges: [{ fromLineNumber: 1, toLineNumber: this.getLineCount() }]
});
}
diff --git a/src/vs/editor/common/model/textModelEvents.ts b/src/vs/editor/common/model/textModelEvents.ts
index 17f45db65ad..6511d02173f 100644
--- a/src/vs/editor/common/model/textModelEvents.ts
+++ b/src/vs/editor/common/model/textModelEvents.ts
@@ -87,6 +87,7 @@ export interface IModelDecorationsChangedEvent {
*/
export interface IModelTokensChangedEvent {
readonly tokenizationSupportChanged: boolean;
+ readonly semanticTokensApplied: boolean;
readonly ranges: {
/**
* The start of the range (inclusive)
diff --git a/src/vs/editor/common/services/modelServiceImpl.ts b/src/vs/editor/common/services/modelServiceImpl.ts
index 288ba00dedf..0db7b1795d6 100644
--- a/src/vs/editor/common/services/modelServiceImpl.ts
+++ b/src/vs/editor/common/services/modelServiceImpl.ts
@@ -466,15 +466,16 @@ class SemanticColoringFeature extends Disposable {
private _watchers: Record;
private _semanticStyling: SemanticStyling;
- private _configurationService: IConfigurationService;
constructor(modelService: IModelService, themeService: IThemeService, configurationService: IConfigurationService, logService: ILogService) {
super();
- this._configurationService = configurationService;
this._watchers = Object.create(null);
this._semanticStyling = this._register(new SemanticStyling(themeService, logService));
const isSemanticColoringEnabled = (model: ITextModel) => {
+ if (!themeService.getColorTheme().semanticHighlighting) {
+ return false;
+ }
const options = configurationService.getValue(SemanticColoringFeature.SETTING_ID, { overrideIdentifier: model.getLanguageIdentifier().language, resource: model.uri });
return options && options.enabled;
};
@@ -485,6 +486,20 @@ class SemanticColoringFeature extends Disposable {
modelSemanticColoring.dispose();
delete this._watchers[model.uri.toString()];
};
+ const handleSettingOrThemeChange = () => {
+ for (let model of modelService.getModels()) {
+ const curr = this._watchers[model.uri.toString()];
+ if (isSemanticColoringEnabled(model)) {
+ if (!curr) {
+ register(model);
+ }
+ } else {
+ if (curr) {
+ deregister(model, curr);
+ }
+ }
+ }
+ };
this._register(modelService.onModelAdded((model) => {
if (isSemanticColoringEnabled(model)) {
register(model);
@@ -496,22 +511,12 @@ class SemanticColoringFeature extends Disposable {
deregister(model, curr);
}
}));
- this._configurationService.onDidChangeConfiguration(e => {
+ this._register(configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration(SemanticColoringFeature.SETTING_ID)) {
- for (let model of modelService.getModels()) {
- const curr = this._watchers[model.uri.toString()];
- if (isSemanticColoringEnabled(model)) {
- if (!curr) {
- register(model);
- }
- } else {
- if (curr) {
- deregister(model, curr);
- }
- }
- }
+ handleSettingOrThemeChange();
}
- });
+ }));
+ this._register(themeService.onDidColorThemeChange(handleSettingOrThemeChange));
}
}
@@ -525,12 +530,9 @@ class SemanticStyling extends Disposable {
) {
super();
this._caches = new WeakMap();
- if (this._themeService) {
- // workaround for tests which use undefined... :/
- this._register(this._themeService.onDidColorThemeChange(() => {
- this._caches = new WeakMap();
- }));
- }
+ this._register(this._themeService.onDidColorThemeChange(() => {
+ this._caches = new WeakMap();
+ }));
}
public get(provider: DocumentSemanticTokensProvider): SemanticColoringProviderStyling {
@@ -768,14 +770,12 @@ class ModelSemanticColoring extends Disposable {
this._fetchSemanticTokens.schedule();
}));
- if (themeService) {
- // workaround for tests which use undefined... :/
- this._register(themeService.onDidColorThemeChange(_ => {
- // clear out existing tokens
- this._setSemanticTokens(null, null, null, []);
- this._fetchSemanticTokens.schedule();
- }));
- }
+ this._register(themeService.onDidColorThemeChange(_ => {
+ // clear out existing tokens
+ this._setSemanticTokens(null, null, null, []);
+ this._fetchSemanticTokens.schedule();
+ }));
+
this._fetchSemanticTokens.schedule(0);
}
diff --git a/src/vs/editor/common/standaloneStrings.ts b/src/vs/editor/common/standaloneStrings.ts
index 164708bd894..e915d340fb3 100644
--- a/src/vs/editor/common/standaloneStrings.ts
+++ b/src/vs/editor/common/standaloneStrings.ts
@@ -36,12 +36,6 @@ export namespace InspectTokensNLS {
}
export namespace GoToLineNLS {
- export const gotoLineLabelValidLineAndColumn = nls.localize('gotoLineLabelValidLineAndColumn', "Go to line {0} and character {1}");
- export const gotoLineLabelValidLine = nls.localize('gotoLineLabelValidLine', "Go to line {0}");
- export const gotoLineLabelEmptyWithLineLimit = nls.localize('gotoLineLabelEmptyWithLineLimit', "Type a line number between 1 and {0} to navigate to");
- export const gotoLineLabelEmptyWithLineAndColumnLimit = nls.localize('gotoLineLabelEmptyWithLineAndColumnLimit', "Type a character between 1 and {0} to navigate to");
- export const gotoLineAriaLabel = nls.localize('gotoLineAriaLabel', "Current Line: {0}. Go to line {1}.");
- export const gotoLineActionInput = nls.localize('gotoLineActionInput', "Type a line number, followed by an optional colon and a character number to navigate to");
export const gotoLineActionLabel = nls.localize('gotoLineActionLabel', "Go to Line...");
}
@@ -50,27 +44,13 @@ export namespace QuickHelpNLS {
}
export namespace QuickCommandNLS {
- export const ariaLabelEntryWithKey = nls.localize('ariaLabelEntryWithKey', "{0}, {1}, commands");
- export const ariaLabelEntry = nls.localize('ariaLabelEntry', "{0}, commands");
- export const quickCommandActionInput = nls.localize('quickCommandActionInput', "Type the name of an action you want to execute");
export const quickCommandActionLabel = nls.localize('quickCommandActionLabel', "Command Palette");
+ export const quickCommandHelp = nls.localize('quickCommandActionHelp', "Show And Run Commands");
}
export namespace QuickOutlineNLS {
- export const entryAriaLabel = nls.localize('entryAriaLabel', "{0}, symbols");
- export const quickOutlineActionInput = nls.localize('quickOutlineActionInput', "Type the name of an identifier you wish to navigate to");
export const quickOutlineActionLabel = nls.localize('quickOutlineActionLabel', "Go to Symbol...");
- export const _symbols_ = nls.localize('symbols', "symbols ({0})");
- export const _modules_ = nls.localize('modules', "modules ({0})");
- export const _class_ = nls.localize('class', "classes ({0})");
- export const _interface_ = nls.localize('interface', "interfaces ({0})");
- export const _method_ = nls.localize('method', "methods ({0})");
- export const _function_ = nls.localize('function', "functions ({0})");
- export const _property_ = nls.localize('property', "properties ({0})");
- export const _variable_ = nls.localize('variable', "variables ({0})");
- export const _variable2_ = nls.localize('variable2', "variables ({0})");
- export const _constructor_ = nls.localize('_constructor', "constructors ({0})");
- export const _call_ = nls.localize('call', "calls ({0})");
+ export const quickOutlineByCategoryActionLabel = nls.localize('quickOutlineByCategoryActionLabel', "Go to Symbol by Category...");
}
export namespace StandaloneCodeEditorNLS {
diff --git a/src/vs/editor/contrib/caretOperations/caretOperations.ts b/src/vs/editor/contrib/caretOperations/caretOperations.ts
index 3b715b871e7..abd6ab267fd 100644
--- a/src/vs/editor/contrib/caretOperations/caretOperations.ts
+++ b/src/vs/editor/contrib/caretOperations/caretOperations.ts
@@ -42,8 +42,8 @@ class MoveCaretLeftAction extends MoveCaretAction {
constructor() {
super(true, {
id: 'editor.action.moveCarretLeftAction',
- label: nls.localize('caret.moveLeft', "Move Caret Left"),
- alias: 'Move Caret Left',
+ label: nls.localize('caret.moveLeft', "Move Selected Text Left"),
+ alias: 'Move Selected Text Left',
precondition: EditorContextKeys.writable
});
}
@@ -53,8 +53,8 @@ class MoveCaretRightAction extends MoveCaretAction {
constructor() {
super(false, {
id: 'editor.action.moveCarretRightAction',
- label: nls.localize('caret.moveRight', "Move Caret Right"),
- alias: 'Move Caret Right',
+ label: nls.localize('caret.moveRight', "Move Selected Text Right"),
+ alias: 'Move Selected Text Right',
precondition: EditorContextKeys.writable
});
}
diff --git a/src/vs/editor/contrib/codelens/codelensController.ts b/src/vs/editor/contrib/codelens/codelensController.ts
index 84f1ac852c1..2f2721cf150 100644
--- a/src/vs/editor/contrib/codelens/codelensController.ts
+++ b/src/vs/editor/contrib/codelens/codelensController.ts
@@ -419,7 +419,7 @@ registerEditorAction(class ShowLensesInCurrentLine extends EditorAction {
super({
id: 'codelens.showLensesInCurrentLine',
precondition: EditorContextKeys.hasCodeLensProvider,
- label: localize('showLensOnLine', "Show Code Lens Command For Current Line"),
+ label: localize('showLensOnLine', "Show Code Lens Commands For Current Line"),
alias: 'Show Code Lens Commands For Current Line',
});
}
diff --git a/src/vs/editor/contrib/quickAccess/commandsQuickAccess.ts b/src/vs/editor/contrib/quickAccess/commandsQuickAccess.ts
new file mode 100644
index 00000000000..3354dd93e55
--- /dev/null
+++ b/src/vs/editor/contrib/quickAccess/commandsQuickAccess.ts
@@ -0,0 +1,49 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { AbstractCommandsQuickAccessProvider, ICommandQuickPick, ICommandsQuickAccessOptions } from 'vs/platform/quickinput/browser/commandsQuickAccess';
+import { IEditor } from 'vs/editor/common/editorCommon';
+import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
+import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
+import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
+import { INotificationService } from 'vs/platform/notification/common/notification';
+import { ICommandService } from 'vs/platform/commands/common/commands';
+
+export abstract class AbstractEditorCommandsQuickAccessProvider extends AbstractCommandsQuickAccessProvider {
+
+ constructor(
+ options: ICommandsQuickAccessOptions,
+ instantiationService: IInstantiationService,
+ keybindingService: IKeybindingService,
+ commandService: ICommandService,
+ telemetryService: ITelemetryService,
+ notificationService: INotificationService
+ ) {
+ super(options, instantiationService, keybindingService, commandService, telemetryService, notificationService);
+ }
+
+ /**
+ * Subclasses to provide the current active editor control.
+ */
+ protected abstract activeTextEditorControl: IEditor | undefined;
+
+ protected getCodeEditorCommandPicks(): ICommandQuickPick[] {
+ const activeTextEditorControl = this.activeTextEditorControl;
+ if (!activeTextEditorControl) {
+ return [];
+ }
+
+ const editorCommandPicks: ICommandQuickPick[] = [];
+ for (const editorAction of activeTextEditorControl.getSupportedActions()) {
+ editorCommandPicks.push({
+ commandId: editorAction.id,
+ commandAlias: editorAction.alias,
+ label: editorAction.label || editorAction.id,
+ });
+ }
+
+ return editorCommandPicks;
+ }
+}
diff --git a/src/vs/editor/contrib/quickAccess/editorNavigationQuickAccess.ts b/src/vs/editor/contrib/quickAccess/editorNavigationQuickAccess.ts
new file mode 100644
index 00000000000..5926c74dce5
--- /dev/null
+++ b/src/vs/editor/contrib/quickAccess/editorNavigationQuickAccess.ts
@@ -0,0 +1,192 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { IQuickAccessProvider } from 'vs/platform/quickinput/common/quickAccess';
+import { IEditor, ScrollType, IDiffEditor } from 'vs/editor/common/editorCommon';
+import { IModelDeltaDecoration, OverviewRulerLane, ITextModel } from 'vs/editor/common/model';
+import { IRange } from 'vs/editor/common/core/range';
+import { themeColorFromId } from 'vs/platform/theme/common/themeService';
+import { overviewRulerRangeHighlight } from 'vs/editor/common/view/editorColorRegistry';
+import { IQuickPick, IQuickPickItem, IKeyMods } from 'vs/platform/quickinput/common/quickInput';
+import { CancellationToken } from 'vs/base/common/cancellation';
+import { IDisposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle';
+import { Event } from 'vs/base/common/event';
+import { isDiffEditor } from 'vs/editor/browser/editorBrowser';
+import { withNullAsUndefined } from 'vs/base/common/types';
+import { once } from 'vs/base/common/functional';
+
+interface IEditorLineDecoration {
+ rangeHighlightId: string;
+ overviewRulerDecorationId: string;
+}
+
+/**
+ * A reusable quick access provider for the editor with support
+ * for adding decorations for navigating in the currently active file
+ * (for example "Go to line", "Go to symbol").
+ */
+export abstract class AbstractEditorNavigationQuickAccessProvider implements IQuickAccessProvider {
+
+ //#region Provider methods
+
+ provide(picker: IQuickPick, token: CancellationToken): IDisposable {
+ const disposables = new DisposableStore();
+
+ // Disable filtering & sorting, we control the results
+ picker.matchOnLabel = picker.matchOnDescription = picker.matchOnDetail = picker.sortByLabel = false;
+
+ // Provide based on current active editor
+ let pickerDisposable = this.doProvide(picker, token);
+ disposables.add(toDisposable(() => pickerDisposable.dispose()));
+
+ // Re-create whenever the active editor changes
+ disposables.add(this.onDidActiveTextEditorControlChange(() => {
+ pickerDisposable.dispose();
+ pickerDisposable = this.doProvide(picker, token);
+ }));
+
+ return disposables;
+ }
+
+ private doProvide(picker: IQuickPick, token: CancellationToken): IDisposable {
+ const disposables = new DisposableStore();
+
+ // With text control
+ const editor = this.activeTextEditorControl;
+ if (editor && this.canProvideWithTextEditor(editor)) {
+
+ // Restore any view state if this picker was closed
+ // without actually going to a line
+ const lastKnownEditorViewState = withNullAsUndefined(editor.saveViewState());
+ once(token.onCancellationRequested)(() => {
+ if (lastKnownEditorViewState) {
+ editor.restoreViewState(lastKnownEditorViewState);
+ }
+ });
+
+ // Clean up decorations on dispose
+ disposables.add(toDisposable(() => this.clearDecorations(editor)));
+
+ // Ask subclass for entries
+ disposables.add(this.provideWithTextEditor(editor, picker, token));
+ }
+
+ // Without text control
+ else {
+ disposables.add(this.provideWithoutTextEditor(picker, token));
+ }
+
+ return disposables;
+ }
+
+ /**
+ * Subclasses to implement if they can operate on the text editor.
+ */
+ protected canProvideWithTextEditor(editor: IEditor): boolean {
+ return true;
+ }
+
+ /**
+ * Subclasses to implement to provide picks for the picker when an editor is active.
+ */
+ protected abstract provideWithTextEditor(editor: IEditor, picker: IQuickPick, token: CancellationToken): IDisposable;
+
+ /**
+ * Subclasses to implement to provide picks for the picker when no editor is active.
+ */
+ protected abstract provideWithoutTextEditor(picker: IQuickPick, token: CancellationToken): IDisposable;
+
+ protected gotoLocation(editor: IEditor, range: IRange, keyMods: IKeyMods, forceSideBySide?: boolean): void {
+ editor.setSelection(range);
+ editor.revealRangeInCenter(range, ScrollType.Smooth);
+ editor.focus();
+ }
+
+ protected getModel(editor: IEditor | IDiffEditor): ITextModel | undefined {
+ return isDiffEditor(editor) ?
+ editor.getModel()?.modified :
+ editor.getModel() as ITextModel;
+ }
+
+ //#endregion
+
+
+ //#region Editor access
+
+ /**
+ * Subclasses to provide an event when the active editor control changes.
+ */
+ protected abstract readonly onDidActiveTextEditorControlChange: Event;
+
+ /**
+ * Subclasses to provide the current active editor control.
+ */
+ protected abstract activeTextEditorControl: IEditor | undefined;
+
+ //#endregion
+
+
+ //#region Decorations Utils
+
+ private rangeHighlightDecorationId: IEditorLineDecoration | undefined = undefined;
+
+ protected addDecorations(editor: IEditor, range: IRange): void {
+ editor.changeDecorations(changeAccessor => {
+
+ // Reset old decorations if any
+ const deleteDecorations: string[] = [];
+ if (this.rangeHighlightDecorationId) {
+ deleteDecorations.push(this.rangeHighlightDecorationId.overviewRulerDecorationId);
+ deleteDecorations.push(this.rangeHighlightDecorationId.rangeHighlightId);
+
+ this.rangeHighlightDecorationId = undefined;
+ }
+
+ // Add new decorations for the range
+ const newDecorations: IModelDeltaDecoration[] = [
+
+ // highlight the entire line on the range
+ {
+ range,
+ options: {
+ className: 'rangeHighlight',
+ isWholeLine: true
+ }
+ },
+
+ // also add overview ruler highlight
+ {
+ range,
+ options: {
+ overviewRuler: {
+ color: themeColorFromId(overviewRulerRangeHighlight),
+ position: OverviewRulerLane.Full
+ }
+ }
+ }
+ ];
+
+ const [rangeHighlightId, overviewRulerDecorationId] = changeAccessor.deltaDecorations(deleteDecorations, newDecorations);
+
+ this.rangeHighlightDecorationId = { rangeHighlightId, overviewRulerDecorationId };
+ });
+ }
+
+ protected clearDecorations(editor: IEditor): void {
+ const rangeHighlightDecorationId = this.rangeHighlightDecorationId;
+ if (rangeHighlightDecorationId) {
+ editor.changeDecorations(changeAccessor => {
+ changeAccessor.deltaDecorations([
+ rangeHighlightDecorationId.overviewRulerDecorationId,
+ rangeHighlightDecorationId.rangeHighlightId
+ ], []);
+ });
+
+ this.rangeHighlightDecorationId = undefined;
+ }
+ }
+
+ //#endregion
+}
diff --git a/src/vs/editor/contrib/quickAccess/gotoLine.ts b/src/vs/editor/contrib/quickAccess/gotoLineQuickAccess.ts
similarity index 60%
rename from src/vs/editor/contrib/quickAccess/gotoLine.ts
rename to src/vs/editor/contrib/quickAccess/gotoLineQuickAccess.ts
index 941c3f654f9..e24cf811a69 100644
--- a/src/vs/editor/contrib/quickAccess/gotoLine.ts
+++ b/src/vs/editor/contrib/quickAccess/gotoLineQuickAccess.ts
@@ -4,74 +4,32 @@
*--------------------------------------------------------------------------------------------*/
import { localize } from 'vs/nls';
-import { IQuickPick, IQuickPickItem, IKeyMods } from 'vs/platform/quickinput/common/quickInput';
+import { IQuickPick, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
import { CancellationToken } from 'vs/base/common/cancellation';
-import { DisposableStore, toDisposable, IDisposable, Disposable } from 'vs/base/common/lifecycle';
-import { once } from 'vs/base/common/functional';
-import { IEditor, ScrollType, IDiffEditor } from 'vs/editor/common/editorCommon';
-import { ITextModel } from 'vs/editor/common/model';
-import { isDiffEditor } from 'vs/editor/browser/editorBrowser';
+import { DisposableStore, IDisposable, Disposable } from 'vs/base/common/lifecycle';
+import { IEditor, ScrollType } from 'vs/editor/common/editorCommon';
import { IRange } from 'vs/editor/common/core/range';
-import { withNullAsUndefined } from 'vs/base/common/types';
-import { AbstractEditorQuickAccessProvider } from 'vs/editor/contrib/quickAccess/quickAccess';
+import { AbstractEditorNavigationQuickAccessProvider } from 'vs/editor/contrib/quickAccess/editorNavigationQuickAccess';
import { IPosition } from 'vs/editor/common/core/position';
-export const GOTO_LINE_PREFIX = ':';
-
interface IGotoLineQuickPickItem extends IQuickPickItem, Partial { }
-export abstract class AbstractGotoLineQuickAccessProvider extends AbstractEditorQuickAccessProvider {
+export abstract class AbstractGotoLineQuickAccessProvider extends AbstractEditorNavigationQuickAccessProvider {
- provide(picker: IQuickPick, token: CancellationToken): IDisposable {
- const disposables = new DisposableStore();
+ static PREFIX = ':';
- // Disable filtering & sorting, we control the results
- picker.matchOnLabel = picker.matchOnDescription = picker.matchOnDetail = picker.sortByLabel = false;
+ protected provideWithoutTextEditor(picker: IQuickPick): IDisposable {
+ const label = localize('cannotRunGotoLine', "Open a text editor first to go to a line.");
- // Provide based on current active editor
- let pickerDisposable = this.doProvide(picker, token);
- disposables.add(toDisposable(() => pickerDisposable.dispose()));
-
- // Re-create whenever the active editor changes
- disposables.add(this.onDidActiveTextEditorControlChange(() => {
- pickerDisposable.dispose();
- pickerDisposable = this.doProvide(picker, token);
- }));
-
- return disposables;
- }
-
- private doProvide(picker: IQuickPick, token: CancellationToken): IDisposable {
-
- // With text control
- if (this.activeTextEditorControl) {
- return this.doProvideWithTextEditor(this.activeTextEditorControl, picker, token);
- }
-
- // Without text control
- return this.doProvideWithoutTextEditor(picker);
- }
-
- private doProvideWithoutTextEditor(picker: IQuickPick): IDisposable {
- const label = localize('cannotRunGotoLine', "Open a text file first to go to a line.");
picker.items = [{ label }];
picker.ariaLabel = label;
return Disposable.None;
}
- private doProvideWithTextEditor(editor: IEditor, picker: IQuickPick, token: CancellationToken): IDisposable {
+ protected provideWithTextEditor(editor: IEditor, picker: IQuickPick, token: CancellationToken): IDisposable {
const disposables = new DisposableStore();
- // Restore any view state if this picker was closed
- // without actually going to a line
- const lastKnownEditorViewState = withNullAsUndefined(editor.saveViewState());
- once(token.onCancellationRequested)(() => {
- if (lastKnownEditorViewState) {
- editor.restoreViewState(lastKnownEditorViewState);
- }
- });
-
// Goto line once picked
disposables.add(picker.onDidAccept(() => {
const [item] = picker.selectedItems;
@@ -80,7 +38,7 @@ export abstract class AbstractGotoLineQuickAccessProvider extends AbstractEditor
return;
}
- this.gotoLine(editor, this.toRange(item.lineNumber, item.column), picker.keyMods);
+ this.gotoLocation(editor, this.toRange(item.lineNumber, item.column), picker.keyMods);
picker.hide();
}
@@ -88,7 +46,7 @@ export abstract class AbstractGotoLineQuickAccessProvider extends AbstractEditor
// React to picker changes
const updatePickerAndEditor = () => {
- const position = this.parsePosition(editor, picker.value.trim().substr(GOTO_LINE_PREFIX.length));
+ const position = this.parsePosition(editor, picker.value.trim().substr(AbstractGotoLineQuickAccessProvider.PREFIX.length));
const label = this.getPickLabel(editor, position.lineNumber, position.column);
// Picker
@@ -117,9 +75,6 @@ export abstract class AbstractGotoLineQuickAccessProvider extends AbstractEditor
updatePickerAndEditor();
disposables.add(picker.onDidChangeValue(() => updatePickerAndEditor()));
- // Clean up decorations on dispose
- disposables.add(toDisposable(() => this.clearDecorations(editor)));
-
return disposables;
}
@@ -191,16 +146,4 @@ export abstract class AbstractGotoLineQuickAccessProvider extends AbstractEditor
private lineCount(editor: IEditor): number {
return this.getModel(editor)?.getLineCount() ?? 0;
}
-
- private getModel(editor: IEditor | IDiffEditor): ITextModel | undefined {
- return isDiffEditor(editor) ?
- editor.getModel()?.modified :
- editor.getModel() as ITextModel;
- }
-
- protected gotoLine(editor: IEditor, range: IRange, keyMods: IKeyMods): void {
- editor.setSelection(range);
- editor.revealRangeInCenter(range, ScrollType.Smooth);
- editor.focus();
- }
}
diff --git a/src/vs/editor/contrib/quickAccess/gotoSymbolQuickAccess.ts b/src/vs/editor/contrib/quickAccess/gotoSymbolQuickAccess.ts
new file mode 100644
index 00000000000..c70d4e06383
--- /dev/null
+++ b/src/vs/editor/contrib/quickAccess/gotoSymbolQuickAccess.ts
@@ -0,0 +1,420 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { localize } from 'vs/nls';
+import { IQuickPick, IQuickPickItem, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput';
+import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
+import { DisposableStore, IDisposable, Disposable } from 'vs/base/common/lifecycle';
+import { IEditor, ScrollType } from 'vs/editor/common/editorCommon';
+import { ITextModel } from 'vs/editor/common/model';
+import { IRange, Range } from 'vs/editor/common/core/range';
+import { AbstractEditorNavigationQuickAccessProvider } from 'vs/editor/contrib/quickAccess/editorNavigationQuickAccess';
+import { DocumentSymbol, SymbolKinds, SymbolTag, DocumentSymbolProviderRegistry, SymbolKind } from 'vs/editor/common/modes';
+import { OutlineModel, OutlineElement } from 'vs/editor/contrib/documentSymbols/outlineModel';
+import { values } from 'vs/base/common/collections';
+import { trim, format } from 'vs/base/common/strings';
+import { fuzzyScore, FuzzyScore, createMatches } from 'vs/base/common/filters';
+
+interface IGotoSymbolQuickPickItem extends IQuickPickItem {
+ kind: SymbolKind,
+ index: number,
+ score?: FuzzyScore;
+ range?: { decoration: IRange, selection: IRange },
+}
+
+export interface IGotoSymbolQuickAccessProviderOptions {
+ openSideBySideDirection: () => undefined | 'right' | 'down'
+}
+
+export abstract class AbstractGotoSymbolQuickAccessProvider extends AbstractEditorNavigationQuickAccessProvider {
+
+ static PREFIX = '@';
+ static SCOPE_PREFIX = ':';
+ static PREFIX_BY_CATEGORY = `${AbstractGotoSymbolQuickAccessProvider.PREFIX}${AbstractGotoSymbolQuickAccessProvider.SCOPE_PREFIX}`;
+
+ constructor(private options?: IGotoSymbolQuickAccessProviderOptions) {
+ super();
+ }
+
+ protected provideWithoutTextEditor(picker: IQuickPick): IDisposable {
+ const label = localize('cannotRunGotoSymbolWithoutEditor', "Open a text editor first to go to a symbol.");
+
+ picker.items = [{ label, index: 0, kind: SymbolKind.String }];
+ picker.ariaLabel = label;
+
+ return Disposable.None;
+ }
+
+ protected provideWithTextEditor(editor: IEditor, picker: IQuickPick, token: CancellationToken): IDisposable {
+ const model = this.getModel(editor);
+ if (!model) {
+ return Disposable.None;
+ }
+
+ // Provide symbols from model if available in registry
+ if (DocumentSymbolProviderRegistry.has(model)) {
+ return this.doProvideWithEditorSymbols(editor, model, picker, token);
+ }
+
+ // Otherwise show an entry for a model without registry
+ // But give a chance to resolve the symbols at a later
+ // point if possible
+ return this.doProvideWithoutEditorSymbols(editor, model, picker, token);
+ }
+
+ private doProvideWithoutEditorSymbols(editor: IEditor, model: ITextModel, picker: IQuickPick, token: CancellationToken): IDisposable {
+ const disposables = new DisposableStore();
+
+ // Generic pick for not having any symbol information
+ const label = localize('cannotRunGotoSymbolWithoutSymbolProvider', "Open a text editor with symbol information first to go to a symbol.");
+ picker.items = [{ label, index: 0, kind: SymbolKind.String }];
+ picker.ariaLabel = label;
+
+ // Listen to changes to the registry and see if eventually
+ // we do get symbols. This can happen if the picker is opened
+ // very early after the model has loaded but before the
+ // language registry is ready.
+ // https://github.com/microsoft/vscode/issues/70607
+ const symbolProviderListener = disposables.add(DocumentSymbolProviderRegistry.onDidChange(() => {
+ if (DocumentSymbolProviderRegistry.has(model)) {
+ symbolProviderListener.dispose();
+
+ disposables.add(this.doProvideWithEditorSymbols(editor, model, picker, token));
+ }
+ }));
+
+ return disposables;
+ }
+
+ private doProvideWithEditorSymbols(editor: IEditor, model: ITextModel, picker: IQuickPick, token: CancellationToken): IDisposable {
+ const disposables = new DisposableStore();
+
+ // Goto symbol once picked
+ disposables.add(picker.onDidAccept(() => {
+ const [item] = picker.selectedItems;
+ if (item && item.range) {
+ this.gotoLocation(editor, item.range.selection, picker.keyMods);
+
+ picker.hide();
+ }
+ }));
+
+ // Goto symbol side by side if enabled
+ disposables.add(picker.onDidTriggerItemButton(({ item }) => {
+ if (item && item.range) {
+ this.gotoLocation(editor, item.range.selection, picker.keyMods, true);
+
+ picker.hide();
+ }
+ }));
+
+ // Resolve symbols from document once and reuse this
+ // request for all filtering and typing then on
+ const symbolsPromise = this.getDocumentSymbols(model, true, token);
+
+ // Set initial picks and update on type
+ let picksCts: CancellationTokenSource | undefined = undefined;
+ const updatePickerItems = async () => {
+
+ // Cancel any previous ask for picks and busy
+ picksCts?.dispose(true);
+ picker.busy = false;
+
+ // Create new cancellation source for this run
+ picksCts = new CancellationTokenSource(token);
+
+ // Collect symbol picks
+ picker.busy = true;
+ try {
+ const items = await this.getSymbolPicks(symbolsPromise, picker.value.substr(AbstractGotoSymbolQuickAccessProvider.PREFIX.length).trim(), picksCts.token);
+ if (token.isCancellationRequested) {
+ return;
+ }
+
+ picker.items = items;
+ } finally {
+ if (!token.isCancellationRequested) {
+ picker.busy = false;
+ }
+ }
+ };
+ disposables.add(picker.onDidChangeValue(() => updatePickerItems()));
+ updatePickerItems();
+
+ // Reveal and decorate when active item changes
+ // However, ignore the very first event so that
+ // opening the picker is not immediately revealing
+ // and decorating the first entry.
+ let ignoreFirstActiveEvent = true;
+ disposables.add(picker.onDidChangeActive(() => {
+ const [item] = picker.activeItems;
+ if (item && item.range) {
+ if (ignoreFirstActiveEvent) {
+ ignoreFirstActiveEvent = false;
+ return;
+ }
+
+ // Reveal
+ editor.revealRangeInCenter(item.range.selection, ScrollType.Smooth);
+
+ // Decorate
+ this.addDecorations(editor, item.range.decoration);
+ }
+ }));
+
+ return disposables;
+ }
+
+ private async getSymbolPicks(symbolsPromise: Promise, filter: string, token: CancellationToken): Promise> {
+ const symbols = await symbolsPromise;
+ if (token.isCancellationRequested) {
+ return [];
+ }
+
+ // Normalize filter
+ const filterBySymbolKind = filter.indexOf(AbstractGotoSymbolQuickAccessProvider.SCOPE_PREFIX) === 0;
+ const filterPos = filterBySymbolKind ? 1 : 0;
+ const [symbolFilter, containerFilter] = filter.split(' ') as [string, string | undefined];
+ const symbolFilterLow = symbolFilter.toLowerCase();
+ const containerFilterLow = containerFilter?.toLowerCase();
+
+ // Convert to symbol picks and apply filtering
+ const filteredSymbolPicks: IGotoSymbolQuickPickItem[] = [];
+ for (let index = 0; index < symbols.length; index++) {
+ const symbol = symbols[index];
+
+ const symbolLabel = trim(symbol.name);
+ const containerLabel = symbol.containerName;
+
+ let symbolScore: FuzzyScore | undefined = undefined;
+ let containerScore: FuzzyScore | undefined = undefined;
+
+ let includeSymbol = true;
+ if (filter.length > filterPos) {
+
+ // Score by symbol
+ symbolScore = fuzzyScore(symbolFilter, symbolFilterLow, filterPos, symbolLabel, symbolLabel.toLowerCase(), 0, true);
+ includeSymbol = !!symbolScore;
+
+ // Score by container if specified
+ if (includeSymbol && containerFilter && containerFilterLow) {
+ if (containerLabel) {
+ containerScore = fuzzyScore(containerFilter, containerFilterLow, filterPos, containerLabel, containerLabel.toLowerCase(), 0, true);
+ }
+
+ includeSymbol = !!containerScore;
+ }
+ }
+
+ if (includeSymbol) {
+ const symbolLabelWithIcon = `$(symbol-${SymbolKinds.toString(symbol.kind) || 'property'}) ${symbolLabel}`;
+ const deprecated = symbol.tags && symbol.tags.indexOf(SymbolTag.Deprecated) >= 0;
+
+ filteredSymbolPicks.push({
+ index,
+ kind: symbol.kind,
+ score: symbolScore,
+ label: symbolLabelWithIcon,
+ ariaLabel: localize('symbolsAriaLabel', "{0}, symbols picker", symbolLabel),
+ description: containerLabel,
+ highlights: deprecated ? undefined : {
+ label: createMatches(symbolScore, symbolLabelWithIcon.length - symbolLabel.length /* Readjust matches to account for codicons in label */),
+ description: createMatches(containerScore)
+ },
+ range: {
+ selection: Range.collapseToStart(symbol.selectionRange),
+ decoration: symbol.range
+ },
+ strikethrough: deprecated,
+ buttons: (() => {
+ const openSideBySideDirection = this.options?.openSideBySideDirection();
+ if (!openSideBySideDirection) {
+ return undefined;
+ }
+
+ return [
+ {
+ iconClass: openSideBySideDirection === 'right' ? 'codicon-split-horizontal' : 'codicon-split-vertical',
+ tooltip: openSideBySideDirection === 'right' ? localize('openToSide', "Open to the Side") : localize('openToBottom', "Open to the Bottom")
+ }
+ ];
+ })()
+ });
+ }
+ }
+
+ // Sort by score
+ const sortedFilteredSymbolPicks = filteredSymbolPicks.sort((symbolA, symbolB) => filterBySymbolKind ?
+ this.compareByKindAndScore(symbolA, symbolB) :
+ this.compareByScore(symbolA, symbolB)
+ );
+
+ // Add separator for types
+ // - @ only total number of symbols
+ // - @: grouped by symbol kind
+ let symbolPicks: Array = [];
+ if (filterBySymbolKind) {
+ let lastSymbolKind: SymbolKind | undefined = undefined;
+ let lastSeparator: IQuickPickSeparator | undefined = undefined;
+ let lastSymbolKindCounter = 0;
+
+ function updateLastSeparatorLabel(): void {
+ if (lastSeparator && typeof lastSymbolKind === 'number' && lastSymbolKindCounter > 0) {
+ lastSeparator.label = format(NLS_SYMBOL_KIND_CACHE[lastSymbolKind] || FALLBACK_NLS_SYMBOL_KIND, lastSymbolKindCounter);
+ }
+ }
+
+ for (const symbolPick of sortedFilteredSymbolPicks) {
+
+ // Found new kind
+ if (lastSymbolKind !== symbolPick.kind) {
+
+ // Update last separator with number of symbols we found for kind
+ updateLastSeparatorLabel();
+
+ lastSymbolKind = symbolPick.kind;
+ lastSymbolKindCounter = 1;
+
+ // Add new separator for new kind
+ lastSeparator = { type: 'separator' };
+ symbolPicks.push(lastSeparator);
+ }
+
+ // Existing kind, keep counting
+ else {
+ lastSymbolKindCounter++;
+ }
+
+ // Add to final result
+ symbolPicks.push(symbolPick);
+ }
+
+ // Update last separator with number of symbols we found for kind
+ updateLastSeparatorLabel();
+ } else {
+ symbolPicks = [
+ { label: localize('symbols', "symbols ({0})", filteredSymbolPicks.length), type: 'separator' },
+ ...sortedFilteredSymbolPicks
+ ];
+ }
+
+ return symbolPicks;
+ }
+
+ private compareByScore(symbolA: IGotoSymbolQuickPickItem, symbolB: IGotoSymbolQuickPickItem): number {
+ if (!symbolA.score && symbolB.score) {
+ return 1;
+ } else if (symbolA.score && !symbolB.score) {
+ return -1;
+ }
+
+ if (symbolA.score && symbolB.score) {
+ if (symbolA.score[0] > symbolB.score[0]) {
+ return -1;
+ } else if (symbolA.score[0] < symbolB.score[0]) {
+ return 1;
+ }
+ }
+
+ if (symbolA.index < symbolB.index) {
+ return -1;
+ } else if (symbolA.index > symbolB.index) {
+ return 1;
+ }
+
+ return 0;
+ }
+
+ private compareByKindAndScore(symbolA: IGotoSymbolQuickPickItem, symbolB: IGotoSymbolQuickPickItem): number {
+ const kindA = NLS_SYMBOL_KIND_CACHE[symbolA.kind] || FALLBACK_NLS_SYMBOL_KIND;
+ const kindB = NLS_SYMBOL_KIND_CACHE[symbolB.kind] || FALLBACK_NLS_SYMBOL_KIND;
+
+ // Sort by type first if scoped search
+ const result = kindA.localeCompare(kindB);
+ if (result === 0) {
+ return this.compareByScore(symbolA, symbolB);
+ }
+
+ return result;
+ }
+
+ private async getDocumentSymbols(document: ITextModel, flatten: boolean, token: CancellationToken): Promise {
+ const model = await OutlineModel.create(document, token);
+ if (token.isCancellationRequested) {
+ return [];
+ }
+
+ const roots: DocumentSymbol[] = [];
+ for (const child of values(model.children)) {
+ if (child instanceof OutlineElement) {
+ roots.push(child.symbol);
+ } else {
+ roots.push(...values(child.children).map(child => child.symbol));
+ }
+ }
+
+ let flatEntries: DocumentSymbol[] = [];
+ if (flatten) {
+ this.flattenDocumentSymbols(flatEntries, roots, '');
+ } else {
+ flatEntries = roots;
+ }
+
+ return flatEntries.sort((symbolA, symbolB) => Range.compareRangesUsingStarts(symbolA.range, symbolB.range));
+ }
+
+ private flattenDocumentSymbols(bucket: DocumentSymbol[], entries: DocumentSymbol[], overrideContainerLabel: string): void {
+ for (const entry of entries) {
+ bucket.push({
+ kind: entry.kind,
+ tags: entry.tags,
+ name: entry.name,
+ detail: entry.detail,
+ containerName: entry.containerName || overrideContainerLabel,
+ range: entry.range,
+ selectionRange: entry.selectionRange,
+ children: undefined, // we flatten it...
+ });
+
+ // Recurse over children
+ if (entry.children) {
+ this.flattenDocumentSymbols(bucket, entry.children, entry.name);
+ }
+ }
+ }
+}
+
+// #region NLS Helpers
+
+const FALLBACK_NLS_SYMBOL_KIND = localize('property', "properties ({0})");
+const NLS_SYMBOL_KIND_CACHE: { [type: number]: string } = {
+ [SymbolKind.Method]: localize('method', "methods ({0})"),
+ [SymbolKind.Function]: localize('function', "functions ({0})"),
+ [SymbolKind.Constructor]: localize('_constructor', "constructors ({0})"),
+ [SymbolKind.Variable]: localize('variable', "variables ({0})"),
+ [SymbolKind.Class]: localize('class', "classes ({0})"),
+ [SymbolKind.Struct]: localize('struct', "structs ({0})"),
+ [SymbolKind.Event]: localize('event', "events ({0})"),
+ [SymbolKind.Operator]: localize('operator', "operators ({0})"),
+ [SymbolKind.Interface]: localize('interface', "interfaces ({0})"),
+ [SymbolKind.Namespace]: localize('namespace', "namespaces ({0})"),
+ [SymbolKind.Package]: localize('package', "packages ({0})"),
+ [SymbolKind.TypeParameter]: localize('typeParameter', "type parameters ({0})"),
+ [SymbolKind.Module]: localize('modules', "modules ({0})"),
+ [SymbolKind.Property]: localize('property', "properties ({0})"),
+ [SymbolKind.Enum]: localize('enum', "enumerations ({0})"),
+ [SymbolKind.EnumMember]: localize('enumMember', "enumeration members ({0})"),
+ [SymbolKind.String]: localize('string', "strings ({0})"),
+ [SymbolKind.File]: localize('file', "files ({0})"),
+ [SymbolKind.Array]: localize('array', "arrays ({0})"),
+ [SymbolKind.Number]: localize('number', "numbers ({0})"),
+ [SymbolKind.Boolean]: localize('boolean', "booleans ({0})"),
+ [SymbolKind.Object]: localize('object', "objects ({0})"),
+ [SymbolKind.Key]: localize('key', "keys ({0})"),
+ [SymbolKind.Field]: localize('field', "fields ({0})"),
+ [SymbolKind.Constant]: localize('constant', "constants ({0})")
+};
+
+//#endregion
diff --git a/src/vs/editor/contrib/quickAccess/quickAccess.ts b/src/vs/editor/contrib/quickAccess/quickAccess.ts
deleted file mode 100644
index 3b315010d9c..00000000000
--- a/src/vs/editor/contrib/quickAccess/quickAccess.ts
+++ /dev/null
@@ -1,104 +0,0 @@
-/*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
-
-import { IQuickAccessProvider } from 'vs/platform/quickinput/common/quickAccess';
-import { IEditor } from 'vs/editor/common/editorCommon';
-import { IModelDeltaDecoration, OverviewRulerLane } from 'vs/editor/common/model';
-import { IRange } from 'vs/editor/common/core/range';
-import { themeColorFromId } from 'vs/platform/theme/common/themeService';
-import { overviewRulerRangeHighlight } from 'vs/editor/common/view/editorColorRegistry';
-import { IQuickPick, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
-import { CancellationToken } from 'vs/base/common/cancellation';
-import { IDisposable } from 'vs/base/common/lifecycle';
-import { Event } from 'vs/base/common/event';
-
-interface IEditorLineDecoration {
- rangeHighlightId: string;
- overviewRulerDecorationId: string;
-}
-
-/**
- * A reusable quick access provider for the editor with support for adding decorations.
- */
-export abstract class AbstractEditorQuickAccessProvider implements IQuickAccessProvider {
-
- /**
- * Subclasses to provide an event when the active editor control changes.
- */
- abstract readonly onDidActiveTextEditorControlChange: Event;
-
- /**
- * Subclasses to provide the current active editor control.
- */
- abstract activeTextEditorControl: IEditor | undefined;
-
- /**
- * Subclasses to implement the quick access picker.
- */
- abstract provide(picker: IQuickPick, token: CancellationToken): IDisposable;
-
-
- //#region Decorations Utils
-
- private rangeHighlightDecorationId: IEditorLineDecoration | undefined = undefined;
-
- protected addDecorations(editor: IEditor, range: IRange): void {
- editor.changeDecorations(changeAccessor => {
-
- // Reset old decorations if any
- const deleteDecorations: string[] = [];
- if (this.rangeHighlightDecorationId) {
- deleteDecorations.push(this.rangeHighlightDecorationId.overviewRulerDecorationId);
- deleteDecorations.push(this.rangeHighlightDecorationId.rangeHighlightId);
-
- this.rangeHighlightDecorationId = undefined;
- }
-
- // Add new decorations for the range
- const newDecorations: IModelDeltaDecoration[] = [
-
- // highlight the entire line on the range
- {
- range,
- options: {
- className: 'rangeHighlight',
- isWholeLine: true
- }
- },
-
- // also add overview ruler highlight
- {
- range,
- options: {
- overviewRuler: {
- color: themeColorFromId(overviewRulerRangeHighlight),
- position: OverviewRulerLane.Full
- }
- }
- }
- ];
-
- const [rangeHighlightId, overviewRulerDecorationId] = changeAccessor.deltaDecorations(deleteDecorations, newDecorations);
-
- this.rangeHighlightDecorationId = { rangeHighlightId, overviewRulerDecorationId };
- });
- }
-
- protected clearDecorations(editor: IEditor): void {
- const rangeHighlightDecorationId = this.rangeHighlightDecorationId;
- if (rangeHighlightDecorationId) {
- editor.changeDecorations(changeAccessor => {
- changeAccessor.deltaDecorations([
- rangeHighlightDecorationId.overviewRulerDecorationId,
- rangeHighlightDecorationId.rangeHighlightId
- ], []);
- });
-
- this.rangeHighlightDecorationId = undefined;
- }
- }
-
- //#endregion
-}
diff --git a/src/vs/editor/editor.main.ts b/src/vs/editor/editor.main.ts
index 875233ba7a8..0dceb4a9e8a 100644
--- a/src/vs/editor/editor.main.ts
+++ b/src/vs/editor/editor.main.ts
@@ -7,11 +7,10 @@ import 'vs/editor/editor.all';
import 'vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp';
import 'vs/editor/standalone/browser/iPadShowKeyboard/iPadShowKeyboard';
import 'vs/editor/standalone/browser/inspectTokens/inspectTokens';
-import 'vs/editor/standalone/browser/quickOpen/gotoLine';
-import 'vs/editor/standalone/browser/quickOpen/quickCommand';
-import 'vs/editor/standalone/browser/quickOpen/quickOutline';
import 'vs/editor/standalone/browser/quickAccess/standaloneHelpQuickAccess';
import 'vs/editor/standalone/browser/quickAccess/standaloneGotoLineQuickAccess';
+import 'vs/editor/standalone/browser/quickAccess/standaloneGotoSymbolQuickAccess';
+import 'vs/editor/standalone/browser/quickAccess/standaloneCommandsQuickAccess';
import 'vs/editor/standalone/browser/referenceSearch/standaloneReferenceSearch';
import 'vs/editor/standalone/browser/toggleHighContrast/toggleHighContrast';
diff --git a/src/vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp.ts b/src/vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp.ts
index 57912317852..274ad8a2060 100644
--- a/src/vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp.ts
+++ b/src/vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp.ts
@@ -330,7 +330,11 @@ class ShowAccessibilityHelpAction extends EditorAction {
kbOpts: {
kbExpr: EditorContextKeys.focus,
primary: KeyMod.Alt | KeyCode.F1,
- weight: KeybindingWeight.EditorContrib
+ weight: KeybindingWeight.EditorContrib,
+ linux: {
+ primary: KeyMod.Alt | KeyMod.Shift | KeyCode.F1,
+ secondary: [KeyMod.Alt | KeyCode.F1]
+ }
}
});
}
diff --git a/src/vs/editor/standalone/browser/quickAccess/standaloneCommandsQuickAccess.ts b/src/vs/editor/standalone/browser/quickAccess/standaloneCommandsQuickAccess.ts
new file mode 100644
index 00000000000..f4839b34f21
--- /dev/null
+++ b/src/vs/editor/standalone/browser/quickAccess/standaloneCommandsQuickAccess.ts
@@ -0,0 +1,76 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { Registry } from 'vs/platform/registry/common/platform';
+import { IQuickAccessRegistry, Extensions } from 'vs/platform/quickinput/common/quickAccess';
+import { QuickCommandNLS } from 'vs/editor/common/standaloneStrings';
+import { ICommandQuickPick } from 'vs/platform/quickinput/browser/commandsQuickAccess';
+import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
+import { AbstractEditorCommandsQuickAccessProvider } from 'vs/editor/contrib/quickAccess/commandsQuickAccess';
+import { IEditor } from 'vs/editor/common/editorCommon';
+import { withNullAsUndefined } from 'vs/base/common/types';
+import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
+import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
+import { ICommandService } from 'vs/platform/commands/common/commands';
+import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
+import { INotificationService } from 'vs/platform/notification/common/notification';
+import { EditorAction, registerEditorAction } from 'vs/editor/browser/editorExtensions';
+import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
+import { KeyCode } from 'vs/base/common/keyCodes';
+import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
+import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
+
+export class StandaloneCommandsQuickAccessProvider extends AbstractEditorCommandsQuickAccessProvider {
+
+ protected get activeTextEditorControl(): IEditor | undefined { return withNullAsUndefined(this.codeEditorService.getFocusedCodeEditor()); }
+
+ constructor(
+ @IInstantiationService instantiationService: IInstantiationService,
+ @ICodeEditorService private readonly codeEditorService: ICodeEditorService,
+ @IKeybindingService keybindingService: IKeybindingService,
+ @ICommandService commandService: ICommandService,
+ @ITelemetryService telemetryService: ITelemetryService,
+ @INotificationService notificationService: INotificationService
+ ) {
+ super({ showAlias: false }, instantiationService, keybindingService, commandService, telemetryService, notificationService);
+ }
+
+ protected async getCommandPicks(): Promise> {
+ return this.getCodeEditorCommandPicks();
+ }
+}
+
+Registry.as(Extensions.Quickaccess).registerQuickAccessProvider({
+ ctor: StandaloneCommandsQuickAccessProvider,
+ prefix: StandaloneCommandsQuickAccessProvider.PREFIX,
+ helpEntries: [{ description: QuickCommandNLS.quickCommandHelp, needsEditor: true }]
+});
+
+export class GotoLineAction extends EditorAction {
+
+ constructor() {
+ super({
+ id: 'editor.action.quickCommand',
+ label: QuickCommandNLS.quickCommandActionLabel,
+ alias: 'Command Palette',
+ precondition: undefined,
+ kbOpts: {
+ kbExpr: EditorContextKeys.focus,
+ primary: KeyCode.F1,
+ weight: KeybindingWeight.EditorContrib
+ },
+ contextMenuOpts: {
+ group: 'z_commands',
+ order: 1
+ }
+ });
+ }
+
+ run(accessor: ServicesAccessor): void {
+ accessor.get(IQuickInputService).quickAccess.show(StandaloneCommandsQuickAccessProvider.PREFIX);
+ }
+}
+
+registerEditorAction(GotoLineAction);
diff --git a/src/vs/editor/standalone/browser/quickAccess/standaloneGotoLineQuickAccess.ts b/src/vs/editor/standalone/browser/quickAccess/standaloneGotoLineQuickAccess.ts
index d513431044b..ab7735e221c 100644
--- a/src/vs/editor/standalone/browser/quickAccess/standaloneGotoLineQuickAccess.ts
+++ b/src/vs/editor/standalone/browser/quickAccess/standaloneGotoLineQuickAccess.ts
@@ -3,29 +3,58 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import { AbstractGotoLineQuickAccessProvider, GOTO_LINE_PREFIX } from 'vs/editor/contrib/quickAccess/gotoLine';
+import { AbstractGotoLineQuickAccessProvider } from 'vs/editor/contrib/quickAccess/gotoLineQuickAccess';
import { Registry } from 'vs/platform/registry/common/platform';
import { IQuickAccessRegistry, Extensions } from 'vs/platform/quickinput/common/quickAccess';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { withNullAsUndefined } from 'vs/base/common/types';
import { GoToLineNLS } from 'vs/editor/common/standaloneStrings';
import { Event } from 'vs/base/common/event';
+import { EditorAction, registerEditorAction, ServicesAccessor } from 'vs/editor/browser/editorExtensions';
+import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
+import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
+import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
+import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
export class StandaloneGotoLineQuickAccessProvider extends AbstractGotoLineQuickAccessProvider {
- readonly onDidActiveTextEditorControlChange = Event.None;
+ protected readonly onDidActiveTextEditorControlChange = Event.None;
constructor(@ICodeEditorService private readonly editorService: ICodeEditorService) {
super();
}
- get activeTextEditorControl() {
+ protected get activeTextEditorControl() {
return withNullAsUndefined(this.editorService.getFocusedCodeEditor());
}
}
Registry.as(Extensions.Quickaccess).registerQuickAccessProvider({
ctor: StandaloneGotoLineQuickAccessProvider,
- prefix: GOTO_LINE_PREFIX,
+ prefix: StandaloneGotoLineQuickAccessProvider.PREFIX,
helpEntries: [{ description: GoToLineNLS.gotoLineActionLabel, needsEditor: true }]
});
+
+export class GotoLineAction extends EditorAction {
+
+ constructor() {
+ super({
+ id: 'editor.action.gotoLine',
+ label: GoToLineNLS.gotoLineActionLabel,
+ alias: 'Go to Line...',
+ precondition: undefined,
+ kbOpts: {
+ kbExpr: EditorContextKeys.focus,
+ primary: KeyMod.CtrlCmd | KeyCode.KEY_G,
+ mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_G },
+ weight: KeybindingWeight.EditorContrib
+ }
+ });
+ }
+
+ run(accessor: ServicesAccessor): void {
+ accessor.get(IQuickInputService).quickAccess.show(StandaloneGotoLineQuickAccessProvider.PREFIX);
+ }
+}
+
+registerEditorAction(GotoLineAction);
diff --git a/src/vs/editor/standalone/browser/quickAccess/standaloneGotoSymbolQuickAccess.ts b/src/vs/editor/standalone/browser/quickAccess/standaloneGotoSymbolQuickAccess.ts
new file mode 100644
index 00000000000..b5850c3f761
--- /dev/null
+++ b/src/vs/editor/standalone/browser/quickAccess/standaloneGotoSymbolQuickAccess.ts
@@ -0,0 +1,67 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { AbstractGotoSymbolQuickAccessProvider } from 'vs/editor/contrib/quickAccess/gotoSymbolQuickAccess';
+import { Registry } from 'vs/platform/registry/common/platform';
+import { IQuickAccessRegistry, Extensions } from 'vs/platform/quickinput/common/quickAccess';
+import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
+import { withNullAsUndefined } from 'vs/base/common/types';
+import { QuickOutlineNLS } from 'vs/editor/common/standaloneStrings';
+import { Event } from 'vs/base/common/event';
+import { EditorAction, registerEditorAction } from 'vs/editor/browser/editorExtensions';
+import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
+import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
+import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
+import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
+import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
+
+export class StandaloneGotoSymbolQuickAccessProvider extends AbstractGotoSymbolQuickAccessProvider {
+
+ protected readonly onDidActiveTextEditorControlChange = Event.None;
+
+ constructor(@ICodeEditorService private readonly editorService: ICodeEditorService) {
+ super();
+ }
+
+ protected get activeTextEditorControl() {
+ return withNullAsUndefined(this.editorService.getFocusedCodeEditor());
+ }
+}
+
+Registry.as(Extensions.Quickaccess).registerQuickAccessProvider({
+ ctor: StandaloneGotoSymbolQuickAccessProvider,
+ prefix: AbstractGotoSymbolQuickAccessProvider.PREFIX,
+ helpEntries: [
+ { description: QuickOutlineNLS.quickOutlineActionLabel, prefix: AbstractGotoSymbolQuickAccessProvider.PREFIX, needsEditor: true },
+ { description: QuickOutlineNLS.quickOutlineByCategoryActionLabel, prefix: AbstractGotoSymbolQuickAccessProvider.PREFIX_BY_CATEGORY, needsEditor: true }
+ ]
+});
+
+export class GotoLineAction extends EditorAction {
+
+ constructor() {
+ super({
+ id: 'editor.action.quickOutline',
+ label: QuickOutlineNLS.quickOutlineActionLabel,
+ alias: 'Go to Symbol...',
+ precondition: EditorContextKeys.hasDocumentSymbolProvider,
+ kbOpts: {
+ kbExpr: EditorContextKeys.focus,
+ primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_O,
+ weight: KeybindingWeight.EditorContrib
+ },
+ contextMenuOpts: {
+ group: 'navigation',
+ order: 3
+ }
+ });
+ }
+
+ run(accessor: ServicesAccessor): void {
+ accessor.get(IQuickInputService).quickAccess.show(AbstractGotoSymbolQuickAccessProvider.PREFIX);
+ }
+}
+
+registerEditorAction(GotoLineAction);
diff --git a/src/vs/editor/standalone/browser/quickAccess/standaloneHelpQuickAccess.ts b/src/vs/editor/standalone/browser/quickAccess/standaloneHelpQuickAccess.ts
index 94deb2ba64e..9eecd4a9a4c 100644
--- a/src/vs/editor/standalone/browser/quickAccess/standaloneHelpQuickAccess.ts
+++ b/src/vs/editor/standalone/browser/quickAccess/standaloneHelpQuickAccess.ts
@@ -8,8 +8,8 @@ import { IQuickAccessRegistry, Extensions } from 'vs/platform/quickinput/common/
import { QuickHelpNLS } from 'vs/editor/common/standaloneStrings';
import { HelpQuickAccessProvider } from 'vs/platform/quickinput/browser/helpQuickAccess';
-Registry.as(Extensions.Quickaccess).defaultProvider = {
+Registry.as(Extensions.Quickaccess).registerQuickAccessProvider({
ctor: HelpQuickAccessProvider,
prefix: '',
helpEntries: [{ description: QuickHelpNLS.helpQuickAccessActionLabel, needsEditor: true }]
-};
+});
diff --git a/src/vs/editor/standalone/browser/quickOpen/editorQuickOpen.css b/src/vs/editor/standalone/browser/quickOpen/editorQuickOpen.css
deleted file mode 100644
index fdc3b7e9a24..00000000000
--- a/src/vs/editor/standalone/browser/quickOpen/editorQuickOpen.css
+++ /dev/null
@@ -1,19 +0,0 @@
-/*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
-
-.monaco-quick-open-widget .monaco-tree .monaco-tree-row .monaco-highlighted-label .highlight,
-.monaco-quick-open-widget .monaco-list .monaco-list-row .monaco-highlighted-label .highlight {
- color: #0066BF;
-}
-
-.vs-dark .monaco-quick-open-widget .monaco-tree .monaco-tree-row .monaco-highlighted-label .highlight,
-.vs-dark .monaco-quick-open-widget .monaco-list .monaco-list-row .monaco-highlighted-label .highlight {
- color: #0097fb;
-}
-
-.hc-black .monaco-quick-open-widget .monaco-tree .monaco-tree-row .monaco-highlighted-label .highlight,
-.hc-black .monaco-quick-open-widget .monaco-list .monaco-list-row .monaco-highlighted-label .highlight {
- color: #F38518;
-}
\ No newline at end of file
diff --git a/src/vs/editor/standalone/browser/quickOpen/editorQuickOpen.ts b/src/vs/editor/standalone/browser/quickOpen/editorQuickOpen.ts
deleted file mode 100644
index b5d6aea1470..00000000000
--- a/src/vs/editor/standalone/browser/quickOpen/editorQuickOpen.ts
+++ /dev/null
@@ -1,172 +0,0 @@
-/*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
-
-import 'vs/css!./editorQuickOpen';
-import { QuickOpenModel } from 'vs/base/parts/quickopen/browser/quickOpenModel';
-import { IAutoFocus } from 'vs/base/parts/quickopen/common/quickOpen';
-import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
-import { EditorAction, IActionOptions, registerEditorContribution } from 'vs/editor/browser/editorExtensions';
-import { Range } from 'vs/editor/common/core/range';
-import { Selection } from 'vs/editor/common/core/selection';
-import { IEditorContribution, ScrollType, IEditor } from 'vs/editor/common/editorCommon';
-import { IModelDeltaDecoration } from 'vs/editor/common/model';
-import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
-import { QuickOpenEditorWidget } from 'vs/editor/standalone/browser/quickOpen/quickOpenEditorWidget';
-import { IThemeService } from 'vs/platform/theme/common/themeService';
-
-export interface IQuickOpenControllerOpts {
- inputAriaLabel: string;
- getModel(value: string): QuickOpenModel;
- getAutoFocus(searchValue: string): IAutoFocus;
-}
-
-export class QuickOpenController implements IEditorContribution, IDecorator {
-
- public static readonly ID = 'editor.controller.quickOpenController';
-
- public static get(editor: ICodeEditor): QuickOpenController {
- return editor.getContribution(QuickOpenController.ID);
- }
-
- private readonly editor: ICodeEditor;
- private widget: QuickOpenEditorWidget | null = null;
- private rangeHighlightDecorationId: string | null = null;
- private lastKnownEditorSelection: Selection | null = null;
-
- constructor(editor: ICodeEditor, @IThemeService private readonly themeService: IThemeService) {
- this.editor = editor;
- }
-
- public dispose(): void {
- // Dispose widget
- if (this.widget) {
- this.widget.destroy();
- this.widget = null;
- }
- }
-
- public run(opts: IQuickOpenControllerOpts): void {
- if (this.widget) {
- this.widget.destroy();
- this.widget = null;
- }
-
- // Create goto line widget
- let onClose = (canceled: boolean) => {
- // Clear Highlight Decorations if present
- this.clearDecorations();
-
- // Restore selection if canceled
- if (canceled && this.lastKnownEditorSelection) {
- this.editor.setSelection(this.lastKnownEditorSelection);
- this.editor.revealRangeInCenterIfOutsideViewport(this.lastKnownEditorSelection, ScrollType.Smooth);
- }
-
- this.lastKnownEditorSelection = null;
-
- // Return focus to the editor if
- // - focus is back on the element because no other focusable element was clicked
- // - a command was picked from the picker which indicates the editor should get focused
- if (document.activeElement === document.body || !canceled) {
- this.editor.focus();
- }
- };
-
- this.widget = new QuickOpenEditorWidget(
- this.editor,
- () => onClose(false),
- () => onClose(true),
- (value: string) => {
- this.widget!.setInput(opts.getModel(value), opts.getAutoFocus(value));
- },
- {
- inputAriaLabel: opts.inputAriaLabel
- },
- this.themeService
- );
-
- // Remember selection to be able to restore on cancel
- if (!this.lastKnownEditorSelection) {
- this.lastKnownEditorSelection = this.editor.getSelection();
- }
-
- // Show
- this.widget.show('');
- }
-
- private static readonly _RANGE_HIGHLIGHT_DECORATION = ModelDecorationOptions.register({
- className: 'rangeHighlight',
- isWholeLine: true
- });
-
- public decorateLine(range: Range, editor: ICodeEditor): void {
- const oldDecorations: string[] = [];
- if (this.rangeHighlightDecorationId) {
- oldDecorations.push(this.rangeHighlightDecorationId);
- this.rangeHighlightDecorationId = null;
- }
-
- const newDecorations: IModelDeltaDecoration[] = [
- {
- range: range,
- options: QuickOpenController._RANGE_HIGHLIGHT_DECORATION
- }
- ];
-
- const decorations = editor.deltaDecorations(oldDecorations, newDecorations);
- this.rangeHighlightDecorationId = decorations[0];
- }
-
- public clearDecorations(): void {
- if (this.rangeHighlightDecorationId) {
- this.editor.deltaDecorations([this.rangeHighlightDecorationId], []);
- this.rangeHighlightDecorationId = null;
- }
- }
-}
-
-export interface IQuickOpenOpts {
- /**
- * provide the quick open model for the given search value.
- */
- getModel(value: string): QuickOpenModel;
-
- /**
- * provide the quick open auto focus mode for the given search value.
- */
- getAutoFocus(searchValue: string): IAutoFocus;
-}
-
-/**
- * Base class for providing quick open in the editor.
- */
-export abstract class BaseEditorQuickOpenAction extends EditorAction {
-
- private readonly _inputAriaLabel: string;
-
- constructor(inputAriaLabel: string, opts: IActionOptions) {
- super(opts);
- this._inputAriaLabel = inputAriaLabel;
- }
-
- protected getController(editor: ICodeEditor): QuickOpenController {
- return QuickOpenController.get(editor);
- }
-
- protected _show(controller: QuickOpenController, opts: IQuickOpenOpts): void {
- controller.run({
- inputAriaLabel: this._inputAriaLabel,
- getModel: (value: string): QuickOpenModel => opts.getModel(value),
- getAutoFocus: (searchValue: string): IAutoFocus => opts.getAutoFocus(searchValue)
- });
- }
-}
-
-export interface IDecorator {
- decorateLine(range: Range, editor: IEditor): void;
- clearDecorations(): void;
-}
-
-registerEditorContribution(QuickOpenController.ID, QuickOpenController);
diff --git a/src/vs/editor/standalone/browser/quickOpen/gotoLine.css b/src/vs/editor/standalone/browser/quickOpen/gotoLine.css
deleted file mode 100644
index e71a5e1dd76..00000000000
--- a/src/vs/editor/standalone/browser/quickOpen/gotoLine.css
+++ /dev/null
@@ -1,8 +0,0 @@
-/*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
-
-.monaco-quick-open-widget {
- font-size: 13px;
-}
\ No newline at end of file
diff --git a/src/vs/editor/standalone/browser/quickOpen/gotoLine.ts b/src/vs/editor/standalone/browser/quickOpen/gotoLine.ts
deleted file mode 100644
index 5274a3ae2c3..00000000000
--- a/src/vs/editor/standalone/browser/quickOpen/gotoLine.ts
+++ /dev/null
@@ -1,177 +0,0 @@
-/*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
-
-import 'vs/css!./gotoLine';
-import * as strings from 'vs/base/common/strings';
-import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
-import { QuickOpenEntry, QuickOpenModel } from 'vs/base/parts/quickopen/browser/quickOpenModel';
-import { IAutoFocus, Mode, IEntryRunContext } from 'vs/base/parts/quickopen/common/quickOpen';
-import { ICodeEditor, IDiffEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser';
-import { ServicesAccessor, registerEditorAction } from 'vs/editor/browser/editorExtensions';
-import { Position } from 'vs/editor/common/core/position';
-import { Range } from 'vs/editor/common/core/range';
-import { IEditor, ScrollType } from 'vs/editor/common/editorCommon';
-import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
-import { ITextModel } from 'vs/editor/common/model';
-import { BaseEditorQuickOpenAction, IDecorator } from 'vs/editor/standalone/browser/quickOpen/editorQuickOpen';
-import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
-import { GoToLineNLS } from 'vs/editor/common/standaloneStrings';
-
-interface ParseResult {
- position: Position;
- isValid: boolean;
- label: string;
-}
-
-export class GotoLineEntry extends QuickOpenEntry {
- private readonly parseResult: ParseResult;
- private readonly decorator: IDecorator;
- private readonly editor: IEditor;
-
- constructor(line: string, editor: IEditor, decorator: IDecorator) {
- super();
-
- this.editor = editor;
- this.decorator = decorator;
- this.parseResult = this.parseInput(line);
- }
-
- private parseInput(line: string): ParseResult {
- const numbers = line.split(',').map(part => parseInt(part, 10)).filter(part => !isNaN(part));
- let position: Position;
-
- if (numbers.length === 0) {
- position = new Position(-1, -1);
- } else if (numbers.length === 1) {
- position = new Position(numbers[0], 1);
- } else {
- position = new Position(numbers[0], numbers[1]);
- }
-
- let model: ITextModel | null;
- if (isCodeEditor(this.editor)) {
- model = this.editor.getModel();
- } else {
- const diffModel = (this.editor).getModel();
- model = diffModel ? diffModel.modified : null;
- }
-
- const isValid = model ? model.validatePosition(position).equals(position) : false;
- let label: string;
-
- if (isValid) {
- if (position.column && position.column > 1) {
- label = strings.format(GoToLineNLS.gotoLineLabelValidLineAndColumn, position.lineNumber, position.column);
- } else {
- label = strings.format(GoToLineNLS.gotoLineLabelValidLine, position.lineNumber);
- }
- } else if (position.lineNumber < 1 || position.lineNumber > (model ? model.getLineCount() : 0)) {
- label = strings.format(GoToLineNLS.gotoLineLabelEmptyWithLineLimit, model ? model.getLineCount() : 0);
- } else {
- label = strings.format(GoToLineNLS.gotoLineLabelEmptyWithLineAndColumnLimit, model ? model.getLineMaxColumn(position.lineNumber) : 0);
- }
-
- return {
- position: position,
- isValid: isValid,
- label: label
- };
- }
-
- getLabel(): string {
- return this.parseResult.label;
- }
-
- getAriaLabel(): string {
- const position = this.editor.getPosition();
- const currentLine = position ? position.lineNumber : 0;
- return strings.format(GoToLineNLS.gotoLineAriaLabel, currentLine, this.parseResult.label);
- }
-
- run(mode: Mode, _context: IEntryRunContext): boolean {
- if (mode === Mode.OPEN) {
- return this.runOpen();
- }
-
- return this.runPreview();
- }
-
- runOpen(): boolean {
-
- // No-op if range is not valid
- if (!this.parseResult.isValid) {
- return false;
- }
-
- // Apply selection and focus
- const range = this.toSelection();
- (this.editor).setSelection(range);
- (this.editor).revealRangeInCenter(range, ScrollType.Smooth);
- this.editor.focus();
-
- return true;
- }
-
- runPreview(): boolean {
-
- // No-op if range is not valid
- if (!this.parseResult.isValid) {
- this.decorator.clearDecorations();
- return false;
- }
-
- // Select Line Position
- const range = this.toSelection();
- this.editor.revealRangeInCenter(range, ScrollType.Smooth);
-
- // Decorate if possible
- this.decorator.decorateLine(range, this.editor);
-
- return false;
- }
-
- private toSelection(): Range {
- return new Range(
- this.parseResult.position.lineNumber,
- this.parseResult.position.column,
- this.parseResult.position.lineNumber,
- this.parseResult.position.column
- );
- }
-}
-
-export class GotoLineAction extends BaseEditorQuickOpenAction {
-
- constructor() {
- super(GoToLineNLS.gotoLineActionInput, {
- id: 'editor.action.gotoLine',
- label: GoToLineNLS.gotoLineActionLabel,
- alias: 'Go to Line...',
- precondition: undefined,
- kbOpts: {
- kbExpr: EditorContextKeys.focus,
- primary: KeyMod.CtrlCmd | KeyCode.KEY_G,
- mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_G },
- weight: KeybindingWeight.EditorContrib
- }
- });
- }
-
- run(accessor: ServicesAccessor, editor: ICodeEditor): void {
- this._show(this.getController(editor), {
- getModel: (value: string): QuickOpenModel => {
- return new QuickOpenModel([new GotoLineEntry(value, editor, this.getController(editor))]);
- },
-
- getAutoFocus: (searchValue: string): IAutoFocus => {
- return {
- autoFocusFirstEntry: searchValue.length > 0
- };
- }
- });
- }
-}
-
-registerEditorAction(GotoLineAction);
diff --git a/src/vs/editor/standalone/browser/quickOpen/quickCommand.ts b/src/vs/editor/standalone/browser/quickOpen/quickCommand.ts
deleted file mode 100644
index 2e49917211b..00000000000
--- a/src/vs/editor/standalone/browser/quickOpen/quickCommand.ts
+++ /dev/null
@@ -1,144 +0,0 @@
-/*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
-
-import * as strings from 'vs/base/common/strings';
-import { onUnexpectedError } from 'vs/base/common/errors';
-import { matchesFuzzy } from 'vs/base/common/filters';
-import { KeyCode } from 'vs/base/common/keyCodes';
-import { IHighlight, QuickOpenEntryGroup, QuickOpenModel } from 'vs/base/parts/quickopen/browser/quickOpenModel';
-import { IAutoFocus, Mode, IEntryRunContext } from 'vs/base/parts/quickopen/common/quickOpen';
-import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
-import { ServicesAccessor, registerEditorAction } from 'vs/editor/browser/editorExtensions';
-import { IEditor, IEditorAction } from 'vs/editor/common/editorCommon';
-import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
-import { BaseEditorQuickOpenAction } from 'vs/editor/standalone/browser/quickOpen/editorQuickOpen';
-import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
-import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
-import { QuickCommandNLS } from 'vs/editor/common/standaloneStrings';
-
-export class EditorActionCommandEntry extends QuickOpenEntryGroup {
- private readonly key: string;
- private readonly action: IEditorAction;
- private readonly editor: IEditor;
- private readonly keyAriaLabel: string;
-
- constructor(key: string, keyAriaLabel: string, highlights: IHighlight[], action: IEditorAction, editor: IEditor) {
- super();
-
- this.key = key;
- this.keyAriaLabel = keyAriaLabel;
- this.setHighlights(highlights);
- this.action = action;
- this.editor = editor;
- }
-
- public getLabel(): string {
- return this.action.label;
- }
-
- public getAriaLabel(): string {
- if (this.keyAriaLabel) {
- return strings.format(QuickCommandNLS.ariaLabelEntryWithKey, this.getLabel(), this.keyAriaLabel);
- }
-
- return strings.format(QuickCommandNLS.ariaLabelEntry, this.getLabel());
- }
-
- public getGroupLabel(): string {
- return this.key;
- }
-
- public run(mode: Mode, context: IEntryRunContext): boolean {
- if (mode === Mode.OPEN) {
-
- // Use a timeout to give the quick open widget a chance to close itself first
- setTimeout(() => {
-
- // Some actions are enabled only when editor has focus
- this.editor.focus();
-
- try {
- let promise = this.action.run() || Promise.resolve();
- promise.then(undefined, onUnexpectedError);
- } catch (error) {
- onUnexpectedError(error);
- }
- }, 50);
-
- return true;
- }
-
- return false;
- }
-}
-
-export class QuickCommandAction extends BaseEditorQuickOpenAction {
-
- constructor() {
- super(QuickCommandNLS.quickCommandActionInput, {
- id: 'editor.action.quickCommand',
- label: QuickCommandNLS.quickCommandActionLabel,
- alias: 'Command Palette',
- precondition: undefined,
- kbOpts: {
- kbExpr: EditorContextKeys.focus,
- primary: KeyCode.F1,
- weight: KeybindingWeight.EditorContrib
- },
- contextMenuOpts: {
- group: 'z_commands',
- order: 1
- }
- });
- }
-
- public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
- const keybindingService = accessor.get(IKeybindingService);
-
- this._show(this.getController(editor), {
- getModel: (value: string): QuickOpenModel => {
- return new QuickOpenModel(this._editorActionsToEntries(keybindingService, editor, value));
- },
-
- getAutoFocus: (searchValue: string): IAutoFocus => {
- return {
- autoFocusFirstEntry: true,
- autoFocusPrefixMatch: searchValue
- };
- }
- });
- }
-
- private _sort(elementA: QuickOpenEntryGroup, elementB: QuickOpenEntryGroup): number {
- let elementAName = (elementA.getLabel() || '').toLowerCase();
- let elementBName = (elementB.getLabel() || '').toLowerCase();
-
- return elementAName.localeCompare(elementBName);
- }
-
- private _editorActionsToEntries(keybindingService: IKeybindingService, editor: ICodeEditor, searchValue: string): EditorActionCommandEntry[] {
- let actions: IEditorAction[] = editor.getSupportedActions();
- let entries: EditorActionCommandEntry[] = [];
-
- for (const action of actions) {
-
- let keybinding = keybindingService.lookupKeybinding(action.id);
-
- if (action.label) {
- let highlights = matchesFuzzy(searchValue, action.label);
- if (highlights) {
- entries.push(new EditorActionCommandEntry(keybinding ? keybinding.getLabel() || '' : '', keybinding ? keybinding.getAriaLabel() || '' : '', highlights, action, editor));
- }
- }
- }
-
- // Sort by name
- entries = entries.sort(this._sort);
-
- return entries;
- }
-}
-
-registerEditorAction(QuickCommandAction);
diff --git a/src/vs/editor/standalone/browser/quickOpen/quickOpenEditorWidget.ts b/src/vs/editor/standalone/browser/quickOpen/quickOpenEditorWidget.ts
deleted file mode 100644
index 17fcaf121bf..00000000000
--- a/src/vs/editor/standalone/browser/quickOpen/quickOpenEditorWidget.ts
+++ /dev/null
@@ -1,107 +0,0 @@
-/*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
-
-import { Dimension } from 'vs/base/browser/dom';
-import { IDisposable } from 'vs/base/common/lifecycle';
-import { QuickOpenModel } from 'vs/base/parts/quickopen/browser/quickOpenModel';
-import { QuickOpenWidget } from 'vs/base/parts/quickopen/browser/quickOpenWidget';
-import { IAutoFocus } from 'vs/base/parts/quickopen/common/quickOpen';
-import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition, OverlayWidgetPositionPreference } from 'vs/editor/browser/editorBrowser';
-import { foreground } from 'vs/platform/theme/common/colorRegistry';
-import { attachQuickOpenStyler } from 'vs/platform/theme/common/styler';
-import { IThemeService } from 'vs/platform/theme/common/themeService';
-
-export interface IQuickOpenEditorWidgetOptions {
- inputAriaLabel: string;
-}
-
-export class QuickOpenEditorWidget implements IOverlayWidget {
-
- private static readonly ID = 'editor.contrib.quickOpenEditorWidget';
-
- private readonly codeEditor: ICodeEditor;
- private readonly themeService: IThemeService;
- private visible: boolean | undefined;
- private quickOpenWidget: QuickOpenWidget;
- private domNode: HTMLElement;
- private styler: IDisposable;
-
- constructor(codeEditor: ICodeEditor, onOk: () => void, onCancel: () => void, onType: (value: string) => void, configuration: IQuickOpenEditorWidgetOptions, themeService: IThemeService) {
- this.codeEditor = codeEditor;
- this.themeService = themeService;
- this.visible = false;
-
- this.domNode = document.createElement('div');
-
- this.quickOpenWidget = new QuickOpenWidget(
- this.domNode,
- {
- onOk: onOk,
- onCancel: onCancel,
- onType: onType
- }, {
- inputPlaceHolder: undefined,
- inputAriaLabel: configuration.inputAriaLabel,
- keyboardSupport: true
- }
- );
- this.styler = attachQuickOpenStyler(this.quickOpenWidget, this.themeService, {
- pickerGroupForeground: foreground
- });
-
- this.quickOpenWidget.create();
- this.codeEditor.addOverlayWidget(this);
- }
-
- setInput(model: QuickOpenModel, focus: IAutoFocus): void {
- this.quickOpenWidget.setInput(model, focus);
- }
-
- getId(): string {
- return QuickOpenEditorWidget.ID;
- }
-
- getDomNode(): HTMLElement {
- return this.domNode;
- }
-
- destroy(): void {
- this.codeEditor.removeOverlayWidget(this);
- this.quickOpenWidget.dispose();
- this.styler.dispose();
- }
-
- isVisible(): boolean {
- return !!this.visible;
- }
-
- show(value: string): void {
- this.visible = true;
-
- const editorLayout = this.codeEditor.getLayoutInfo();
- if (editorLayout) {
- this.quickOpenWidget.layout(new Dimension(editorLayout.width, editorLayout.height));
- }
-
- this.quickOpenWidget.show(value);
- this.codeEditor.layoutOverlayWidget(this);
- }
-
- hide(): void {
- this.visible = false;
- this.quickOpenWidget.hide();
- this.codeEditor.layoutOverlayWidget(this);
- }
-
- getPosition(): IOverlayWidgetPosition | null {
- if (this.visible) {
- return {
- preference: OverlayWidgetPositionPreference.TOP_CENTER
- };
- }
-
- return null;
- }
-}
diff --git a/src/vs/editor/standalone/browser/quickOpen/quickOutline.css b/src/vs/editor/standalone/browser/quickOpen/quickOutline.css
deleted file mode 100644
index 75309ce3318..00000000000
--- a/src/vs/editor/standalone/browser/quickOpen/quickOutline.css
+++ /dev/null
@@ -1,8 +0,0 @@
-/*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
-
-.monaco-quick-open-widget {
- font-size: 13px;
-}
diff --git a/src/vs/editor/standalone/browser/quickOpen/quickOutline.ts b/src/vs/editor/standalone/browser/quickOpen/quickOutline.ts
deleted file mode 100644
index 772073939ef..00000000000
--- a/src/vs/editor/standalone/browser/quickOpen/quickOutline.ts
+++ /dev/null
@@ -1,325 +0,0 @@
-/*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
-
-import 'vs/css!./quickOutline';
-import 'vs/base/browser/ui/codiconLabel/codiconLabel'; // The codicon symbol styles are defined here and must be loaded
-import 'vs/editor/contrib/documentSymbols/outlineTree'; // The codicon symbol colors are defined here and must be loaded
-import { CancellationToken } from 'vs/base/common/cancellation';
-import { matchesFuzzy } from 'vs/base/common/filters';
-import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
-import * as strings from 'vs/base/common/strings';
-import { IHighlight, QuickOpenEntryGroup, QuickOpenModel } from 'vs/base/parts/quickopen/browser/quickOpenModel';
-import { IAutoFocus, Mode, IEntryRunContext } from 'vs/base/parts/quickopen/common/quickOpen';
-import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
-import { ServicesAccessor, registerEditorAction } from 'vs/editor/browser/editorExtensions';
-import { IRange, Range } from 'vs/editor/common/core/range';
-import { ScrollType } from 'vs/editor/common/editorCommon';
-import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
-import { DocumentSymbol, DocumentSymbolProviderRegistry, SymbolKinds } from 'vs/editor/common/modes';
-import { getDocumentSymbols } from 'vs/editor/contrib/quickOpen/quickOpen';
-import { BaseEditorQuickOpenAction, IDecorator } from 'vs/editor/standalone/browser/quickOpen/editorQuickOpen';
-import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
-import { QuickOutlineNLS } from 'vs/editor/common/standaloneStrings';
-
-let SCOPE_PREFIX = ':';
-
-export class SymbolEntry extends QuickOpenEntryGroup {
- private readonly name: string;
- private readonly type: string;
- private readonly description: string | undefined;
- private readonly range: Range;
- private readonly editor: ICodeEditor;
- private readonly decorator: IDecorator;
-
- constructor(name: string, type: string, description: string | undefined, range: Range, highlights: IHighlight[], editor: ICodeEditor, decorator: IDecorator) {
- super();
-
- this.name = name;
- this.type = type;
- this.description = description;
- this.range = range;
- this.setHighlights(highlights);
- this.editor = editor;
- this.decorator = decorator;
- }
-
- public getLabel(): string {
- return this.name;
- }
-
- public getAriaLabel(): string {
- return strings.format(QuickOutlineNLS.entryAriaLabel, this.name);
- }
-
- public getIcon(): string {
- return this.type;
- }
-
- public getDescription(): string | undefined {
- return this.description;
- }
-
- public getType(): string {
- return this.type;
- }
-
- public getRange(): Range {
- return this.range;
- }
-
- public run(mode: Mode, context: IEntryRunContext): boolean {
- if (mode === Mode.OPEN) {
- return this.runOpen(context);
- }
-
- return this.runPreview();
- }
-
- private runOpen(_context: IEntryRunContext): boolean {
-
- // Apply selection and focus
- let range = this.toSelection();
- this.editor.setSelection(range);
- this.editor.revealRangeInCenter(range, ScrollType.Smooth);
- this.editor.focus();
-
- return true;
- }
-
- private runPreview(): boolean {
-
- // Select Outline Position
- let range = this.toSelection();
- this.editor.revealRangeInCenter(range, ScrollType.Smooth);
-
- // Decorate if possible
- this.decorator.decorateLine(this.range, this.editor);
-
- return false;
- }
-
- private toSelection(): Range {
- return new Range(
- this.range.startLineNumber,
- this.range.startColumn || 1,
- this.range.startLineNumber,
- this.range.startColumn || 1
- );
- }
-}
-
-export class QuickOutlineAction extends BaseEditorQuickOpenAction {
-
- constructor() {
- super(QuickOutlineNLS.quickOutlineActionInput, {
- id: 'editor.action.quickOutline',
- label: QuickOutlineNLS.quickOutlineActionLabel,
- alias: 'Go to Symbol...',
- precondition: EditorContextKeys.hasDocumentSymbolProvider,
- kbOpts: {
- kbExpr: EditorContextKeys.focus,
- primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_O,
- weight: KeybindingWeight.EditorContrib
- },
- contextMenuOpts: {
- group: 'navigation',
- order: 3
- }
- });
- }
-
- public run(accessor: ServicesAccessor, editor: ICodeEditor) {
- if (!editor.hasModel()) {
- return undefined;
- }
-
- const model = editor.getModel();
-
- if (!DocumentSymbolProviderRegistry.has(model)) {
- return undefined;
- }
-
- // Resolve outline
- return getDocumentSymbols(model, true, CancellationToken.None).then((result: DocumentSymbol[]) => {
- if (result.length === 0) {
- return;
- }
-
- this._run(editor, result);
- });
- }
-
- private _run(editor: ICodeEditor, result: DocumentSymbol[]): void {
- this._show(this.getController(editor), {
- getModel: (value: string): QuickOpenModel => {
- return new QuickOpenModel(this.toQuickOpenEntries(editor, result, value));
- },
-
- getAutoFocus: (searchValue: string): IAutoFocus => {
- // Remove any type pattern (:) from search value as needed
- if (searchValue.indexOf(SCOPE_PREFIX) === 0) {
- searchValue = searchValue.substr(SCOPE_PREFIX.length);
- }
-
- return {
- autoFocusPrefixMatch: searchValue,
- autoFocusFirstEntry: !!searchValue
- };
- }
- });
- }
-
- private symbolEntry(name: string, type: string, description: string | undefined, range: IRange, highlights: IHighlight[], editor: ICodeEditor, decorator: IDecorator): SymbolEntry {
- return new SymbolEntry(name, type, description, Range.lift(range), highlights, editor, decorator);
- }
-
- private toQuickOpenEntries(editor: ICodeEditor, flattened: DocumentSymbol[], searchValue: string): SymbolEntry[] {
- const controller = this.getController(editor);
-
- let results: SymbolEntry[] = [];
-
- // Convert to Entries
- let normalizedSearchValue = searchValue;
- if (searchValue.indexOf(SCOPE_PREFIX) === 0) {
- normalizedSearchValue = normalizedSearchValue.substr(SCOPE_PREFIX.length);
- }
-
- for (const element of flattened) {
- let label = strings.trim(element.name);
-
- // Check for meatch
- let highlights = matchesFuzzy(normalizedSearchValue, label);
- if (highlights) {
-
- // Show parent scope as description
- let description: string | undefined = undefined;
- if (element.containerName) {
- description = element.containerName;
- }
-
- // Add
- results.push(this.symbolEntry(label, SymbolKinds.toCssClassName(element.kind), description, element.range, highlights, editor, controller));
- }
- }
-
- // Sort properly if actually searching
- if (searchValue) {
- if (searchValue.indexOf(SCOPE_PREFIX) === 0) {
- results = results.sort(this.sortScoped.bind(this, searchValue.toLowerCase()));
- } else {
- results = results.sort(this.sortNormal.bind(this, searchValue.toLowerCase()));
- }
- }
-
- // Mark all type groups
- if (results.length > 0 && searchValue.indexOf(SCOPE_PREFIX) === 0) {
- let currentType: string | null = null;
- let currentResult: SymbolEntry | null = null;
- let typeCounter = 0;
-
- for (let i = 0; i < results.length; i++) {
- let result = results[i];
-
- // Found new type
- if (currentType !== result.getType()) {
-
- // Update previous result with count
- if (currentResult) {
- currentResult.setGroupLabel(this.typeToLabel(currentType || '', typeCounter));
- }
-
- currentType = result.getType();
- currentResult = result;
- typeCounter = 1;
-
- result.setShowBorder(i > 0);
- }
-
- // Existing type, keep counting
- else {
- typeCounter++;
- }
- }
-
- // Update previous result with count
- if (currentResult) {
- currentResult.setGroupLabel(this.typeToLabel(currentType || '', typeCounter));
- }
- }
-
- // Mark first entry as outline
- else if (results.length > 0) {
- results[0].setGroupLabel(strings.format(QuickOutlineNLS._symbols_, results.length));
- }
-
- return results;
- }
-
- private typeToLabel(type: string, count: number): string {
- switch (type) {
- case 'module': return strings.format(QuickOutlineNLS._modules_, count);
- case 'class': return strings.format(QuickOutlineNLS._class_, count);
- case 'interface': return strings.format(QuickOutlineNLS._interface_, count);
- case 'method': return strings.format(QuickOutlineNLS._method_, count);
- case 'function': return strings.format(QuickOutlineNLS._function_, count);
- case 'property': return strings.format(QuickOutlineNLS._property_, count);
- case 'variable': return strings.format(QuickOutlineNLS._variable_, count);
- case 'var': return strings.format(QuickOutlineNLS._variable2_, count);
- case 'constructor': return strings.format(QuickOutlineNLS._constructor_, count);
- case 'call': return strings.format(QuickOutlineNLS._call_, count);
- }
-
- return type;
- }
-
- private sortNormal(searchValue: string, elementA: SymbolEntry, elementB: SymbolEntry): number {
- let elementAName = elementA.getLabel().toLowerCase();
- let elementBName = elementB.getLabel().toLowerCase();
-
- // Compare by name
- let r = elementAName.localeCompare(elementBName);
- if (r !== 0) {
- return r;
- }
-
- // If name identical sort by range instead
- let elementARange = elementA.getRange();
- let elementBRange = elementB.getRange();
- return elementARange.startLineNumber - elementBRange.startLineNumber;
- }
-
- private sortScoped(searchValue: string, elementA: SymbolEntry, elementB: SymbolEntry): number {
-
- // Remove scope char
- searchValue = searchValue.substr(SCOPE_PREFIX.length);
-
- // Sort by type first if scoped search
- let elementAType = elementA.getType();
- let elementBType = elementB.getType();
- let r = elementAType.localeCompare(elementBType);
- if (r !== 0) {
- return r;
- }
-
- // Special sort when searching in scoped mode
- if (searchValue) {
- let elementAName = elementA.getLabel().toLowerCase();
- let elementBName = elementB.getLabel().toLowerCase();
-
- // Compare by name
- let r = elementAName.localeCompare(elementBName);
- if (r !== 0) {
- return r;
- }
- }
-
- // Default to sort by range
- let elementARange = elementA.getRange();
- let elementBRange = elementB.getRange();
- return elementARange.startLineNumber - elementBRange.startLineNumber;
- }
-}
-
-registerEditorAction(QuickOutlineAction);
diff --git a/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts b/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts
index 406410b2abe..2fb24d8a345 100644
--- a/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts
+++ b/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts
@@ -138,6 +138,8 @@ class StandaloneTheme implements IStandaloneTheme {
public get tokenColorMap(): string[] {
return [];
}
+
+ public readonly semanticHighlighting = false;
}
function isBuiltinTheme(themeName: string): themeName is BuiltinTheme {
diff --git a/src/vs/editor/standalone/test/browser/standaloneLanguages.test.ts b/src/vs/editor/standalone/test/browser/standaloneLanguages.test.ts
index 16b3d8c120c..fa53430b10e 100644
--- a/src/vs/editor/standalone/test/browser/standaloneLanguages.test.ts
+++ b/src/vs/editor/standalone/test/browser/standaloneLanguages.test.ts
@@ -60,6 +60,8 @@ suite('TokenizationSupport2Adapter', () => {
return undefined;
},
+ semanticHighlighting: false,
+
tokenColorMap: []
};
}
diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts
index 8b4ab293ccc..08918576d0e 100644
--- a/src/vs/monaco.d.ts
+++ b/src/vs/monaco.d.ts
@@ -74,6 +74,7 @@ declare namespace monaco {
* (http://tools.ietf.org/html/rfc3986#section-3) with minimal validation
* and encoding.
*
+ * ```txt
* foo://example.com:8042/over/there?name=ferret#nose
* \_/ \______________/\_________/ \_________/ \__/
* | | | | |
@@ -81,6 +82,7 @@ declare namespace monaco {
* | _____________________|__
* / \ / \
* urn:example:animal:ferret:nose
+ * ```
*/
export class Uri implements UriComponents {
static isUri(thing: any): thing is Uri;
diff --git a/src/vs/platform/actions/browser/menuEntryActionViewItem.ts b/src/vs/platform/actions/browser/menuEntryActionViewItem.ts
index 6b561aaef76..4e3e1dec7a2 100644
--- a/src/vs/platform/actions/browser/menuEntryActionViewItem.ts
+++ b/src/vs/platform/actions/browser/menuEntryActionViewItem.ts
@@ -12,7 +12,7 @@ import { IdGenerator } from 'vs/base/common/idGenerator';
import { IDisposable, toDisposable, MutableDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { isLinux, isWindows } from 'vs/base/common/platform';
import { localize } from 'vs/nls';
-import { ICommandAction, IMenu, IMenuActionOptions, MenuItemAction, SubmenuItemAction } from 'vs/platform/actions/common/actions';
+import { ICommandAction, IMenu, IMenuActionOptions, MenuItemAction, SubmenuItemAction, Icon } from 'vs/platform/actions/common/actions';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { INotificationService } from 'vs/platform/notification/common/notification';
@@ -216,9 +216,10 @@ export class MenuEntryActionViewItem extends ActionViewItem {
const keybinding = this._keybindingService.lookupKeybinding(this._commandAction.id);
const keybindingLabel = keybinding && keybinding.getLabel();
+ const tooltip = this._commandAction.tooltip || this._commandAction.label;
this.label.title = keybindingLabel
- ? localize('titleAndKb', "{0} ({1})", this._commandAction.label, keybindingLabel)
- : this._commandAction.label;
+ ? localize('titleAndKb', "{0} ({1})", tooltip, keybindingLabel)
+ : tooltip;
}
}
@@ -237,9 +238,11 @@ export class MenuEntryActionViewItem extends ActionViewItem {
_updateItemClass(item: ICommandAction): void {
this._itemClassDispose.value = undefined;
- if (ThemeIcon.isThemeIcon(item.icon)) {
+ const icon = this._commandAction.checked && (item.toggled as { icon?: Icon })?.icon ? (item.toggled as { icon: Icon }).icon : item.icon;
+
+ if (ThemeIcon.isThemeIcon(icon)) {
// theme icons
- const iconClass = ThemeIcon.asClassName(item.icon);
+ const iconClass = ThemeIcon.asClassName(icon);
if (this.label && iconClass) {
addClasses(this.label, iconClass);
this._itemClassDispose.value = toDisposable(() => {
@@ -249,20 +252,20 @@ export class MenuEntryActionViewItem extends ActionViewItem {
});
}
- } else if (item.icon) {
+ } else if (icon) {
// icon path
let iconClass: string;
- if (item.icon?.dark?.scheme) {
+ if (icon?.dark?.scheme) {
- const iconPathMapKey = item.icon.dark.toString();
+ const iconPathMapKey = icon.dark.toString();
if (MenuEntryActionViewItem.ICON_PATH_TO_CSS_RULES.has(iconPathMapKey)) {
iconClass = MenuEntryActionViewItem.ICON_PATH_TO_CSS_RULES.get(iconPathMapKey)!;
} else {
iconClass = ids.nextId();
- createCSSRule(`.icon.${iconClass}`, `background-image: ${asCSSUrl(item.icon.light || item.icon.dark)}`);
- createCSSRule(`.vs-dark .icon.${iconClass}, .hc-black .icon.${iconClass}`, `background-image: ${asCSSUrl(item.icon.dark)}`);
+ createCSSRule(`.icon.${iconClass}`, `background-image: ${asCSSUrl(icon.light || icon.dark)}`);
+ createCSSRule(`.vs-dark .icon.${iconClass}, .hc-black .icon.${iconClass}`, `background-image: ${asCSSUrl(icon.dark)}`);
MenuEntryActionViewItem.ICON_PATH_TO_CSS_RULES.set(iconPathMapKey, iconClass);
}
diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts
index ade8ecf75f3..f5a1a2e00bc 100644
--- a/src/vs/platform/actions/common/actions.ts
+++ b/src/vs/platform/actions/common/actions.ts
@@ -20,13 +20,16 @@ export interface ILocalizedString {
original: string;
}
+export type Icon = { dark?: URI; light?: URI; } | ThemeIcon;
+
export interface ICommandAction {
id: string;
title: string | ILocalizedString;
category?: string | ILocalizedString;
- icon?: { dark?: URI; light?: URI; } | ThemeIcon;
+ tooltip?: string | ILocalizedString;
+ icon?: Icon;
precondition?: ContextKeyExpression;
- toggled?: ContextKeyExpression;
+ toggled?: ContextKeyExpression | { condition: ContextKeyExpression, icon?: Icon, tooltip?: string | ILocalizedString };
}
export type ISerializableCommandAction = UriDto;
@@ -275,9 +278,20 @@ export class MenuItemAction extends ExecuteCommandAction {
@ICommandService commandService: ICommandService
) {
typeof item.title === 'string' ? super(item.id, item.title, commandService) : super(item.id, item.title.value, commandService);
+
this._cssClass = undefined;
this._enabled = !item.precondition || contextKeyService.contextMatchesRules(item.precondition);
- this._checked = Boolean(item.toggled && contextKeyService.contextMatchesRules(item.toggled));
+ this._tooltip = item.tooltip ? typeof item.tooltip === 'string' ? item.tooltip : item.tooltip.value : undefined;
+
+ if (item.toggled) {
+ const toggled = ((item.toggled as { condition: ContextKeyExpression }).condition ? item.toggled : { condition: item.toggled }) as {
+ condition: ContextKeyExpression, icon?: Icon, tooltip?: string | ILocalizedString
+ };
+ this._checked = contextKeyService.contextMatchesRules(toggled.condition);
+ if (this._checked && toggled.tooltip) {
+ this._tooltip = typeof toggled.tooltip === 'string' ? toggled.tooltip : toggled.tooltip.value;
+ }
+ }
this._options = options || {};
@@ -373,7 +387,7 @@ export interface IAction2Options extends ICommandAction {
/**
* One or many menu items.
*/
- menu?: OneOrN<{ id: MenuId } & Omit & { command?: Partial> }>;
+ menu?: OneOrN<{ id: MenuId } & Omit>;
/**
* One keybinding.
@@ -396,7 +410,7 @@ export function registerAction2(ctor: { new(): Action2 }): IDisposable {
const disposables = new DisposableStore();
const action = new ctor();
- const { f1, menu: menus, keybinding, description, ...command } = action.desc;
+ const { f1, menu, keybinding, description, ...command } = action.desc;
// command
disposables.add(CommandsRegistry.registerCommand({
@@ -406,14 +420,12 @@ export function registerAction2(ctor: { new(): Action2 }): IDisposable {
}));
// menu
- if (Array.isArray(menus)) {
- for (let item of menus) {
- const { command: commandOverrides, ...menu } = item;
- disposables.add(MenuRegistry.appendMenuItem(item.id, { command: { ...command, ...commandOverrides }, ...menu }));
+ if (Array.isArray(menu)) {
+ for (let item of menu) {
+ disposables.add(MenuRegistry.appendMenuItem(item.id, { command: { ...command }, ...item }));
}
- } else if (menus) {
- const { command: commandOverrides, ...menu } = menus;
- disposables.add(MenuRegistry.appendMenuItem(menu.id, { command: { ...command, ...commandOverrides }, ...menu }));
+ } else if (menu) {
+ disposables.add(MenuRegistry.appendMenuItem(menu.id, { command: { ...command }, ...menu }));
}
if (f1) {
disposables.add(MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: command }));
diff --git a/src/vs/platform/actions/common/menuService.ts b/src/vs/platform/actions/common/menuService.ts
index 2bfa84d15eb..96987a4c911 100644
--- a/src/vs/platform/actions/common/menuService.ts
+++ b/src/vs/platform/actions/common/menuService.ts
@@ -94,7 +94,8 @@ class Menu implements IMenu {
// keep toggled keys for event if applicable
if (isIMenuItem(item) && item.command.toggled) {
- Menu._fillInKbExprKeys(item.command.toggled, this._contextKeys);
+ const toggledExpression: ContextKeyExpression = (item.command.toggled as { condition: ContextKeyExpression }).condition || item.command.toggled;
+ Menu._fillInKbExprKeys(toggledExpression, this._contextKeys);
}
}
this._onDidChange.fire(this);
diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts
index 123c84684da..5df93a4f3f2 100644
--- a/src/vs/platform/environment/common/environment.ts
+++ b/src/vs/platform/environment/common/environment.ts
@@ -39,7 +39,6 @@ export interface ParsedArgs {
'builtin-extensions-dir'?: string;
extensionDevelopmentPath?: string[]; // // undefined or array of 1 or more local paths or URIs
extensionTestsPath?: string; // either a local path or a URI
- 'extension-development-confirm-save'?: boolean;
'inspect-extensions'?: string;
'inspect-brk-extensions'?: string;
debugId?: string;
@@ -74,6 +73,7 @@ export interface ParsedArgs {
'disable-user-env-probe'?: boolean;
'force'?: boolean;
'force-user-env'?: boolean;
+ 'sync'?: 'on' | 'off';
// chromium command line args: https://electronjs.org/docs/all#supported-chrome-command-line-switches
'no-proxy-server'?: boolean;
@@ -167,5 +167,5 @@ export interface IEnvironmentService extends IUserHomeProvider {
driverHandle?: string;
driverVerbose: boolean;
- galleryMachineIdResource?: URI;
+ serviceMachineIdResource?: URI;
}
diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts
index e68e0647c32..f6f818485d9 100644
--- a/src/vs/platform/environment/node/argv.ts
+++ b/src/vs/platform/environment/node/argv.ts
@@ -52,6 +52,7 @@ export const OPTIONS: OptionDescriptions> = {
'telemetry': { type: 'boolean', cat: 'o', description: localize('telemetry', "Shows all telemetry events which VS code collects.") },
'folder-uri': { type: 'string[]', cat: 'o', args: 'uri', description: localize('folderUri', "Opens a window with given folder uri(s)") },
'file-uri': { type: 'string[]', cat: 'o', args: 'uri', description: localize('fileUri', "Opens a window with given file uri(s)") },
+ 'sync': { type: 'string', cat: 'o', description: localize('turn sync', "Turn sync on or off"), args: ['on', 'off'] },
'extensions-dir': { type: 'string', deprecates: 'extensionHomePath', cat: 'e', args: 'dir', description: localize('extensionHomePath', "Set the root path for extensions.") },
'builtin-extensions-dir': { type: 'string' },
@@ -80,7 +81,6 @@ export const OPTIONS: OptionDescriptions> = {
'locate-extension': { type: 'string[]' },
'extensionDevelopmentPath': { type: 'string[]' },
'extensionTestsPath': { type: 'string' },
- 'extension-development-confirm-save': { type: 'boolean' },
'debugId': { type: 'string' },
'inspect-search': { type: 'string', deprecates: 'debugSearch' },
'inspect-brk-search': { type: 'string', deprecates: 'debugBrkSearch' },
diff --git a/src/vs/platform/environment/node/environmentService.ts b/src/vs/platform/environment/node/environmentService.ts
index 58b8f6ffcd1..4738addf8c7 100644
--- a/src/vs/platform/environment/node/environmentService.ts
+++ b/src/vs/platform/environment/node/environmentService.ts
@@ -255,7 +255,7 @@ export class EnvironmentService implements IEnvironmentService {
get nodeCachedDataDir(): string | undefined { return process.env['VSCODE_NODE_CACHED_DATA_DIR'] || undefined; }
@memoize
- get galleryMachineIdResource(): URI { return resources.joinPath(URI.file(this.userDataPath), 'machineid'); }
+ get serviceMachineIdResource(): URI { return resources.joinPath(URI.file(this.userDataPath), 'machineid'); }
get disableUpdates(): boolean { return !!this._args['disable-updates']; }
get disableCrashReporter(): boolean { return !!this._args['disable-crash-reporter']; }
diff --git a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts
index f3fb5ecca84..a0b43ec818c 100644
--- a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts
+++ b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts
@@ -13,7 +13,7 @@ import { IRequestService, asJson, asText } from 'vs/platform/request/common/requ
import { IRequestOptions, IRequestContext, IHeaders } from 'vs/base/parts/request/common/request';
import { isEngineValid } from 'vs/platform/extensions/common/extensionValidator';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
-import { generateUuid, isUUID } from 'vs/base/common/uuid';
+import { generateUuid } from 'vs/base/common/uuid';
import { values } from 'vs/base/common/map';
import { CancellationToken } from 'vs/base/common/cancellation';
import { ILogService } from 'vs/platform/log/common/log';
@@ -21,10 +21,9 @@ import { IExtensionManifest } from 'vs/platform/extensions/common/extensions';
import { IFileService } from 'vs/platform/files/common/files';
import { URI } from 'vs/base/common/uri';
import { joinPath } from 'vs/base/common/resources';
-import { VSBuffer } from 'vs/base/common/buffer';
import { IProductService } from 'vs/platform/product/common/productService';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
-import { optional } from 'vs/platform/instantiation/common/instantiation';
+import { getServiceMachineId } from 'vs/platform/serviceMachineId/common/serviceMachineId';
interface IRawGalleryExtensionFile {
assetType: string;
@@ -341,7 +340,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
@ITelemetryService private readonly telemetryService: ITelemetryService,
@IFileService private readonly fileService: IFileService,
@IProductService private readonly productService: IProductService,
- @optional(IStorageService) private readonly storageService: IStorageService,
+ @IStorageService private readonly storageService: IStorageService,
) {
const config = productService.extensionsGallery;
this.extensionsGalleryUrl = config && config.serviceUrl;
@@ -760,43 +759,15 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
}
}
-export async function resolveMarketplaceHeaders(version: string, environmentService: IEnvironmentService, fileService: IFileService, storageService?: IStorageService): Promise<{ [key: string]: string; }> {
+export async function resolveMarketplaceHeaders(version: string, environmentService: IEnvironmentService, fileService: IFileService, storageService: {
+ get: (key: string, scope: StorageScope) => string | undefined,
+ store: (key: string, value: string, scope: StorageScope) => void
+}): Promise<{ [key: string]: string; }> {
const headers: IHeaders = {
'X-Market-Client-Id': `VSCode ${version}`,
'User-Agent': `VSCode ${version}`
};
- let uuid: string | null = null;
- if (environmentService.galleryMachineIdResource) {
- try {
- const contents = await fileService.readFile(environmentService.galleryMachineIdResource);
- const value = contents.value.toString();
- uuid = isUUID(value) ? value : null;
- } catch (e) {
- uuid = null;
- }
-
- if (!uuid) {
- uuid = generateUuid();
- try {
- await fileService.writeFile(environmentService.galleryMachineIdResource, VSBuffer.fromString(uuid));
- } catch (error) {
- //noop
- }
- }
- }
-
- if (storageService) {
- uuid = storageService.get('marketplace.userid', StorageScope.GLOBAL) || null;
- if (!uuid) {
- uuid = generateUuid();
- storageService.store('marketplace.userid', uuid, StorageScope.GLOBAL);
- }
- }
-
- if (uuid) {
- headers['X-Market-User-Id'] = uuid;
- }
-
+ const uuid: string = await getServiceMachineId(environmentService, fileService, storageService);
+ headers['X-Market-User-Id'] = uuid;
return headers;
-
}
diff --git a/src/vs/platform/extensionManagement/test/node/extensionGalleryService.test.ts b/src/vs/platform/extensionManagement/test/node/extensionGalleryService.test.ts
index 07bb0a2e00f..377a92abbe9 100644
--- a/src/vs/platform/extensionManagement/test/node/extensionGalleryService.test.ts
+++ b/src/vs/platform/extensionManagement/test/node/extensionGalleryService.test.ts
@@ -19,6 +19,8 @@ import { NullLogService } from 'vs/platform/log/common/log';
import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider';
import { Schemas } from 'vs/base/common/network';
import product from 'vs/platform/product/common/product';
+import { TestStorageService } from 'vs/workbench/test/common/workbenchTestServices';
+import { IStorageService } from 'vs/platform/storage/common/storage';
suite('Extension Gallery Service', () => {
const parentDir = getRandomTestPath(os.tmpdir(), 'vsctests', 'extensiongalleryservice');
@@ -52,11 +54,12 @@ suite('Extension Gallery Service', () => {
test('marketplace machine id', () => {
const args = ['--user-data-dir', marketplaceHome];
const environmentService = new EnvironmentService(parseArgs(args, OPTIONS), process.execPath);
+ const storageService: IStorageService = new TestStorageService();
- return resolveMarketplaceHeaders(product.version, environmentService, fileService).then(headers => {
+ return resolveMarketplaceHeaders(product.version, environmentService, fileService, storageService).then(headers => {
assert.ok(isUUID(headers['X-Market-User-Id']));
- return resolveMarketplaceHeaders(product.version, environmentService, fileService).then(headers2 => {
+ return resolveMarketplaceHeaders(product.version, environmentService, fileService, storageService).then(headers2 => {
assert.equal(headers['X-Market-User-Id'], headers2['X-Market-User-Id']);
});
});
diff --git a/src/vs/platform/files/test/electron-browser/diskFileService.test.ts b/src/vs/platform/files/test/electron-browser/diskFileService.test.ts
index b0af12007e7..a5ed2443744 100644
--- a/src/vs/platform/files/test/electron-browser/diskFileService.test.ts
+++ b/src/vs/platform/files/test/electron-browser/diskFileService.test.ts
@@ -132,10 +132,12 @@ suite('Disk File Service', function () {
const disposables = new DisposableStore();
// Given issues such as https://github.com/microsoft/vscode/issues/78602
- // we see random test failures when accessing the native file system. To
- // diagnose further, we retry node.js file access tests up to 3 times to
- // rule out any random disk issue.
+ // and https://github.com/microsoft/vscode/issues/92334 we see random test
+ // failures when accessing the native file system. To diagnose further, we
+ // retry node.js file access tests up to 3 times to rule out any random disk
+ // issue and increase the timeout.
this.retries(3);
+ this.timeout(1000 * 10);
setup(async () => {
const logService = new NullLogService();
diff --git a/src/vs/platform/opener/browser/link.ts b/src/vs/platform/opener/browser/link.ts
index daf424fa848..7b6863cd048 100644
--- a/src/vs/platform/opener/browser/link.ts
+++ b/src/vs/platform/opener/browser/link.ts
@@ -5,7 +5,7 @@
import { Event } from 'vs/base/common/event';
import { IOpenerService } from 'vs/platform/opener/common/opener';
-import { $ } from 'vs/base/browser/dom';
+import { $, EventHelper, EventLike } from 'vs/base/browser/dom';
import { domEvent } from 'vs/base/browser/event';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode } from 'vs/base/common/keyCodes';
@@ -46,9 +46,12 @@ export class Link extends Disposable {
.map(e => new StandardKeyboardEvent(e))
.filter(e => e.keyCode === KeyCode.Enter)
.event;
- const onOpen = Event.any(onClick, onEnterPress);
+ const onOpen = Event.any(onClick, onEnterPress);
- this._register(onOpen(_ => openerService.open(link.href)));
+ this._register(onOpen(e => {
+ EventHelper.stop(e, true);
+ openerService.open(link.href);
+ }));
this.applyStyles();
}
diff --git a/src/vs/platform/quickinput/browser/commandsQuickAccess.ts b/src/vs/platform/quickinput/browser/commandsQuickAccess.ts
new file mode 100644
index 00000000000..d3e1a048da2
--- /dev/null
+++ b/src/vs/platform/quickinput/browser/commandsQuickAccess.ts
@@ -0,0 +1,302 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { localize } from 'vs/nls';
+import { IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput';
+import { PickerQuickAccessProvider, IPickerQuickAccessItem } from 'vs/platform/quickinput/browser/pickerQuickAccess';
+import { distinct } from 'vs/base/common/arrays';
+import { CancellationToken } from 'vs/base/common/cancellation';
+import { DisposableStore, Disposable, IDisposable } from 'vs/base/common/lifecycle';
+import { or, matchesPrefix, matchesWords, matchesContiguousSubString } from 'vs/base/common/filters';
+import { withNullAsUndefined } from 'vs/base/common/types';
+import { LRUCache } from 'vs/base/common/map';
+import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
+import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
+import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
+import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
+import { ICommandService } from 'vs/platform/commands/common/commands';
+import { WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification } from 'vs/base/common/actions';
+import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
+import { isPromiseCanceledError } from 'vs/base/common/errors';
+import { INotificationService } from 'vs/platform/notification/common/notification';
+import { toErrorMessage } from 'vs/base/common/errorMessage';
+import { isFirefox } from 'vs/base/browser/browser';
+import { timeout } from 'vs/base/common/async';
+
+export interface ICommandQuickPick extends IPickerQuickAccessItem {
+ commandId: string;
+ commandAlias: string | undefined;
+}
+
+export interface ICommandsQuickAccessOptions {
+ showAlias: boolean;
+}
+
+export abstract class AbstractCommandsQuickAccessProvider extends PickerQuickAccessProvider implements IDisposable {
+
+ static PREFIX = '>';
+
+ private static WORD_FILTER = or(matchesPrefix, matchesWords, matchesContiguousSubString);
+
+ private readonly commandsHistory = this._register(this.instantiationService.createInstance(CommandsHistory));
+
+ constructor(
+ private options: ICommandsQuickAccessOptions,
+ @IInstantiationService private readonly instantiationService: IInstantiationService,
+ @IKeybindingService private readonly keybindingService: IKeybindingService,
+ @ICommandService private readonly commandService: ICommandService,
+ @ITelemetryService private readonly telemetryService: ITelemetryService,
+ @INotificationService private readonly notificationService: INotificationService
+ ) {
+ super(AbstractCommandsQuickAccessProvider.PREFIX);
+ }
+
+ protected async getPicks(filter: string, disposables: DisposableStore, token: CancellationToken): Promise> {
+
+ // Ask subclass for all command picks
+ const allCommandPicks = await this.getCommandPicks(disposables, token);
+
+ // Filter
+ const filteredCommandPicks: ICommandQuickPick[] = [];
+ for (const commandPick of allCommandPicks) {
+ const labelHighlights = withNullAsUndefined(AbstractCommandsQuickAccessProvider.WORD_FILTER(filter, commandPick.label));
+ const aliasHighlights = commandPick.commandAlias ? withNullAsUndefined(AbstractCommandsQuickAccessProvider.WORD_FILTER(filter, commandPick.commandAlias)) : undefined;
+
+ if (labelHighlights || aliasHighlights) {
+ commandPick.highlights = {
+ label: labelHighlights,
+ detail: this.options.showAlias ? aliasHighlights : undefined
+ };
+
+ filteredCommandPicks.push(commandPick);
+ }
+ }
+
+ // Remove duplicates
+ const distinctCommandPicks = distinct(filteredCommandPicks, pick => `${pick.label}${pick.commandId}`);
+
+ // Add description to commands that have duplicate labels
+ const mapLabelToCommand = new Map();
+ for (const commandPick of distinctCommandPicks) {
+ const existingCommandForLabel = mapLabelToCommand.get(commandPick.label);
+ if (existingCommandForLabel) {
+ commandPick.description = commandPick.commandId;
+ existingCommandForLabel.description = existingCommandForLabel.commandId;
+ } else {
+ mapLabelToCommand.set(commandPick.label, commandPick);
+ }
+ }
+
+ // Sort by MRU order and fallback to name otherwise
+ distinctCommandPicks.sort((commandPickA, commandPickB) => {
+ const commandACounter = this.commandsHistory.peek(commandPickA.commandId);
+ const commandBCounter = this.commandsHistory.peek(commandPickB.commandId);
+
+ if (commandACounter && commandBCounter) {
+ return commandACounter > commandBCounter ? -1 : 1; // use more recently used command before older
+ }
+
+ if (commandACounter) {
+ return -1; // first command was used, so it wins over the non used one
+ }
+
+ if (commandBCounter) {
+ return 1; // other command was used so it wins over the command
+ }
+
+ // both commands were never used, so we sort by name
+ return commandPickA.label.localeCompare(commandPickB.label);
+ });
+
+ const commandPicks: Array = [];
+
+ let addSeparator = false;
+ for (let i = 0; i < distinctCommandPicks.length; i++) {
+ const commandPick = distinctCommandPicks[i];
+ const keybinding = this.keybindingService.lookupKeybinding(commandPick.commandId);
+ const ariaLabel = keybinding ?
+ localize('commandPickAriaLabelWithKeybinding', "{0}, {1}, commands picker", commandPick.label, keybinding.getAriaLabel()) :
+ localize('commandPickAriaLabel', "{0}, commands picker", commandPick.label);
+
+ // Separator: recently used
+ if (i === 0 && this.commandsHistory.peek(commandPick.commandId)) {
+ commandPicks.push({ type: 'separator', label: localize('recentlyUsed', "recently used") });
+ addSeparator = true;
+ }
+
+ // Separator: other commands
+ if (i !== 0 && addSeparator && !this.commandsHistory.peek(commandPick.commandId)) {
+ commandPicks.push({ type: 'separator', label: localize('morecCommands', "other commands") });
+ addSeparator = false; // only once
+ }
+
+ // Command
+ commandPicks.push({
+ ...commandPick,
+ ariaLabel,
+ detail: this.options.showAlias ? commandPick.commandAlias : undefined,
+ keybinding,
+ accept: async () => {
+
+ // Add to history
+ this.commandsHistory.push(commandPick.commandId);
+
+ if (!isFirefox) {
+ // Use a timeout to give the quick open widget a chance to close itself first
+ // Firefox: since the browser is quite picky for certain commands, we do not
+ // use a timeout (https://github.com/microsoft/vscode/issues/83288)
+ await timeout(50);
+ }
+
+ // Telementry
+ this.telemetryService.publicLog2('workbenchActionExecuted', {
+ id: commandPick.commandId,
+ from: 'quick open'
+ });
+
+ // Run
+ try {
+ await this.commandService.executeCommand(commandPick.commandId);
+ } catch (error) {
+ if (!isPromiseCanceledError(error)) {
+ this.notificationService.error(localize('canNotRun', "Command '{0}' resulted in an error ({1})", commandPick.label, toErrorMessage(error)));
+ }
+ }
+ }
+ });
+ }
+
+ return commandPicks;
+ }
+
+ /**
+ * Subclasses to provide the actual command entries.
+ */
+ protected abstract getCommandPicks(disposables: DisposableStore, token: CancellationToken): Promise>;
+}
+
+interface ISerializedCommandHistory {
+ usesLRU?: boolean;
+ entries: { key: string; value: number }[];
+}
+
+interface ICommandsQuickAccessConfiguration {
+ workbench: {
+ commandPalette: {
+ history: number;
+ preserveInput: boolean;
+ }
+ };
+}
+
+class CommandsHistory extends Disposable {
+
+ static readonly DEFAULT_COMMANDS_HISTORY_LENGTH = 50;
+
+ private static readonly PREF_KEY_CACHE = 'commandPalette.mru.cache';
+ private static readonly PREF_KEY_COUNTER = 'commandPalette.mru.counter';
+
+ private static cache: LRUCache | undefined;
+ private static counter = 1;
+
+ private configuredCommandsHistoryLength = 0;
+
+ constructor(
+ @IStorageService private readonly storageService: IStorageService,
+ @IConfigurationService private readonly configurationService: IConfigurationService
+ ) {
+ super();
+
+ this.updateConfiguration();
+ this.load();
+
+ this.registerListeners();
+ }
+
+ private registerListeners(): void {
+ this._register(this.configurationService.onDidChangeConfiguration(() => this.updateConfiguration()));
+ }
+
+ private updateConfiguration(): void {
+ this.configuredCommandsHistoryLength = CommandsHistory.getConfiguredCommandHistoryLength(this.configurationService);
+
+ if (CommandsHistory.cache && CommandsHistory.cache.limit !== this.configuredCommandsHistoryLength) {
+ CommandsHistory.cache.limit = this.configuredCommandsHistoryLength;
+
+ CommandsHistory.saveState(this.storageService);
+ }
+ }
+
+ private load(): void {
+ const raw = this.storageService.get(CommandsHistory.PREF_KEY_CACHE, StorageScope.GLOBAL);
+ let serializedCache: ISerializedCommandHistory | undefined;
+ if (raw) {
+ try {
+ serializedCache = JSON.parse(raw);
+ } catch (error) {
+ // invalid data
+ }
+ }
+
+ const cache = CommandsHistory.cache = new LRUCache(this.configuredCommandsHistoryLength, 1);
+ if (serializedCache) {
+ let entries: { key: string; value: number }[];
+ if (serializedCache.usesLRU) {
+ entries = serializedCache.entries;
+ } else {
+ entries = serializedCache.entries.sort((a, b) => a.value - b.value);
+ }
+ entries.forEach(entry => cache.set(entry.key, entry.value));
+ }
+
+ CommandsHistory.counter = this.storageService.getNumber(CommandsHistory.PREF_KEY_COUNTER, StorageScope.GLOBAL, CommandsHistory.counter);
+ }
+
+ push(commandId: string): void {
+ if (!CommandsHistory.cache) {
+ return;
+ }
+
+ CommandsHistory.cache.set(commandId, CommandsHistory.counter++); // set counter to command
+
+ CommandsHistory.saveState(this.storageService);
+ }
+
+ peek(commandId: string): number | undefined {
+ return CommandsHistory.cache?.peek(commandId);
+ }
+
+ static saveState(storageService: IStorageService): void {
+ if (!CommandsHistory.cache) {
+ return;
+ }
+
+ const serializedCache: ISerializedCommandHistory = { usesLRU: true, entries: [] };
+ CommandsHistory.cache.forEach((value, key) => serializedCache.entries.push({ key, value }));
+
+ storageService.store(CommandsHistory.PREF_KEY_CACHE, JSON.stringify(serializedCache), StorageScope.GLOBAL);
+ storageService.store(CommandsHistory.PREF_KEY_COUNTER, CommandsHistory.counter, StorageScope.GLOBAL);
+ }
+
+ static getConfiguredCommandHistoryLength(configurationService: IConfigurationService): number {
+ const config = configurationService.getValue();
+
+ const configuredCommandHistoryLength = config.workbench?.commandPalette?.history;
+ if (typeof configuredCommandHistoryLength === 'number') {
+ return configuredCommandHistoryLength;
+ }
+
+ return CommandsHistory.DEFAULT_COMMANDS_HISTORY_LENGTH;
+ }
+
+ static clearHistory(configurationService: IConfigurationService, storageService: IStorageService): void {
+ const commandHistoryLength = CommandsHistory.getConfiguredCommandHistoryLength(configurationService);
+ CommandsHistory.cache = new LRUCache(commandHistoryLength);
+ CommandsHistory.counter = 1;
+
+ CommandsHistory.saveState(storageService);
+ }
+}
+
diff --git a/src/vs/platform/quickinput/browser/helpQuickAccess.ts b/src/vs/platform/quickinput/browser/helpQuickAccess.ts
index d4a66d3556b..22e0879e475 100644
--- a/src/vs/platform/quickinput/browser/helpQuickAccess.ts
+++ b/src/vs/platform/quickinput/browser/helpQuickAccess.ts
@@ -6,7 +6,6 @@
import { IQuickPick, IQuickPickItem, IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
import { IQuickAccessProvider, IQuickAccessRegistry, Extensions } from 'vs/platform/quickinput/common/quickAccess';
import { Registry } from 'vs/platform/registry/common/platform';
-import { CancellationToken } from 'vs/base/common/cancellation';
import { localize } from 'vs/nls';
import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
@@ -16,11 +15,13 @@ interface IHelpQuickAccessPickItem extends IQuickPickItem {
export class HelpQuickAccessProvider implements IQuickAccessProvider {
+ static PREFIX = '?';
+
private readonly registry = Registry.as(Extensions.Quickaccess);
constructor(@IQuickInputService private readonly quickInputService: IQuickInputService) { }
- provide(picker: IQuickPick, token: CancellationToken): IDisposable {
+ provide(picker: IQuickPick): IDisposable {
const disposables = new DisposableStore();
// Open a picker with the selected value if picked
@@ -31,6 +32,15 @@ export class HelpQuickAccessProvider implements IQuickAccessProvider {
}
}));
+ // Also open a picker when we detect the user typed the exact
+ // name of a provider (e.g. `?term` for terminals)
+ disposables.add(picker.onDidChangeValue(value => {
+ const providerDescriptor = this.registry.getQuickAccessProvider(value.substr(HelpQuickAccessProvider.PREFIX.length));
+ if (providerDescriptor && providerDescriptor.prefix && providerDescriptor.prefix !== HelpQuickAccessProvider.PREFIX) {
+ this.quickInputService.quickAccess.show(providerDescriptor.prefix);
+ }
+ }));
+
// Fill in all providers separated by editor/global scope
const { editorProviders, globalProviders } = this.getQuickAccessProviders();
picker.items = editorProviders.length === 0 || globalProviders.length === 0 ?
@@ -55,7 +65,7 @@ export class HelpQuickAccessProvider implements IQuickAccessProvider {
const globalProviders: IHelpQuickAccessPickItem[] = [];
const editorProviders: IHelpQuickAccessPickItem[] = [];
- for (const provider of this.registry.getQuickAccessProviders().sort((p1, p2) => p1.prefix.localeCompare(p2.prefix))) {
+ for (const provider of this.registry.getQuickAccessProviders().sort((providerA, providerB) => providerA.prefix.localeCompare(providerB.prefix))) {
for (const helpEntry of provider.helpEntries) {
const prefix = helpEntry.prefix || provider.prefix;
const label = prefix || '\u2026' /* ... */;
@@ -63,8 +73,8 @@ export class HelpQuickAccessProvider implements IQuickAccessProvider {
(helpEntry.needsEditor ? editorProviders : globalProviders).push({
prefix,
label,
- description: helpEntry.description,
- ariaLabel: localize('entryAriaLabel', "{0}, picker help", label)
+ ariaLabel: localize('entryAriaLabel', "{0}, quick access help picker", label),
+ description: helpEntry.description
});
}
}
diff --git a/src/vs/platform/quickinput/browser/pickerQuickAccess.ts b/src/vs/platform/quickinput/browser/pickerQuickAccess.ts
new file mode 100644
index 00000000000..fca14db0d98
--- /dev/null
+++ b/src/vs/platform/quickinput/browser/pickerQuickAccess.ts
@@ -0,0 +1,167 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { IQuickPick, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
+import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
+import { IQuickPickSeparator, IKeyMods, IQuickPickAcceptEvent } from 'vs/base/parts/quickinput/common/quickInput';
+import { IQuickAccessProvider } from 'vs/platform/quickinput/common/quickAccess';
+import { IDisposable, DisposableStore, Disposable } from 'vs/base/common/lifecycle';
+
+export enum TriggerAction {
+
+ /**
+ * Do nothing after the button was clicked.
+ */
+ NO_ACTION,
+
+ /**
+ * Close the picker.
+ */
+ CLOSE_PICKER,
+
+ /**
+ * Update the results of the picker.
+ */
+ REFRESH_PICKER
+}
+
+export interface IPickerQuickAccessItem extends IQuickPickItem {
+
+ /**
+ * A method that will be executed when the pick item is accepted from
+ * the picker. The picker will close automatically before running this.
+ *
+ * @param keyMods the state of modifier keys when the item was accepted.
+ * @param event the underlying event that caused the accept to trigger.
+ */
+ accept?(keyMods: IKeyMods, event: IQuickPickAcceptEvent): void;
+
+ /**
+ * A method that will be executed when a button of the pick item was
+ * clicked on.
+ *
+ * @param buttonIndex index of the button of the item that
+ * was clicked.
+ *
+ * @param the state of modifier keys when the button was triggered.
+ *
+ * @returns a value that indicates what should happen after the trigger
+ * which can be a `Promise` for long running operations.
+ */
+ trigger?(buttonIndex: number, keyMods: IKeyMods): TriggerAction | Promise;
+}
+
+export abstract class PickerQuickAccessProvider extends Disposable implements IQuickAccessProvider {
+
+ constructor(private prefix: string) {
+ super();
+ }
+
+ provide(picker: IQuickPick, token: CancellationToken): IDisposable {
+ const disposables = new DisposableStore();
+
+ // Allow subclasses to configure picker
+ this.configure(picker);
+
+ // Disable filtering & sorting, we control the results
+ picker.matchOnLabel = picker.matchOnDescription = picker.matchOnDetail = picker.sortByLabel = false;
+
+ // Set initial picks and update on type
+ let picksCts: CancellationTokenSource | undefined = undefined;
+ const updatePickerItems = async () => {
+
+ // Cancel any previous ask for picks and busy
+ picksCts?.dispose(true);
+ picker.busy = false;
+
+ // Create new cancellation source for this run
+ picksCts = new CancellationTokenSource(token);
+
+ // Collect picks and support both long running and short
+ const res = this.getPicks(picker.value.substr(this.prefix.length).trim(), disposables.add(new DisposableStore()), picksCts.token);
+ if (Array.isArray(res)) {
+ picker.items = res;
+ } else {
+ picker.busy = true;
+ try {
+ const items = await res;
+ if (token.isCancellationRequested) {
+ return;
+ }
+
+ picker.items = items;
+ } finally {
+ if (!token.isCancellationRequested) {
+ picker.busy = false;
+ }
+ }
+ }
+ };
+ disposables.add(picker.onDidChangeValue(() => updatePickerItems()));
+ updatePickerItems();
+
+ // Accept the pick on accept and hide picker
+ disposables.add(picker.onDidAccept(event => {
+ const [item] = picker.selectedItems;
+ if (typeof item?.accept === 'function') {
+ if (!event.inBackground) {
+ picker.hide(); // hide picker unless we accept in background
+ }
+ item.accept(picker.keyMods, event);
+ }
+ }));
+
+ // Trigger the pick with button index if button triggered
+ disposables.add(picker.onDidTriggerItemButton(async ({ button, item }) => {
+ if (typeof item.trigger === 'function') {
+ const buttonIndex = item.buttons?.indexOf(button) ?? -1;
+ if (buttonIndex >= 0) {
+ const result = item.trigger(buttonIndex, picker.keyMods);
+ const action = (typeof result === 'number') ? result : await result;
+
+ if (token.isCancellationRequested) {
+ return;
+ }
+
+ switch (action) {
+ case TriggerAction.NO_ACTION:
+ break;
+ case TriggerAction.CLOSE_PICKER:
+ picker.hide();
+ break;
+ case TriggerAction.REFRESH_PICKER:
+ updatePickerItems();
+ break;
+ }
+ }
+ }
+ }));
+
+ return disposables;
+ }
+
+ /**
+ * Subclasses can override this method to configure the picker before showing it.
+ *
+ * @param picker the picker instance used for the quick access before it opens.
+ */
+ protected configure(picker: IQuickPick): void { }
+
+ /**
+ * Returns an array of picks and separators as needed. If the picks are resolved
+ * long running, the provided cancellation token should be used to cancel the
+ * operation when the token signals this.
+ *
+ * The implementor is responsible for filtering and sorting the picks given the
+ * provided `filter`.
+ *
+ * @param filter a filter to apply to the picks.
+ * @param disposables can be used to register disposables that should be cleaned
+ * up when the picker closes.
+ * @param token for long running tasks, implementors need to check on cancellation
+ * through this token.
+ */
+ protected abstract getPicks(filter: string, disposables: DisposableStore, token: CancellationToken): Array | Promise>;
+}
diff --git a/src/vs/platform/quickinput/browser/quickAccess.ts b/src/vs/platform/quickinput/browser/quickAccess.ts
index 377db89fbd3..ca96d93fdbe 100644
--- a/src/vs/platform/quickinput/browser/quickAccess.ts
+++ b/src/vs/platform/quickinput/browser/quickAccess.ts
@@ -37,11 +37,11 @@ export class QuickAccessController extends Disposable implements IQuickAccessCon
// Create a picker for the provider to use with the initial value
// and adjust the filtering to exclude the prefix from filtering
const picker = disposables.add(this.quickInputService.createQuickPick());
- picker.placeholder = descriptor.placeholder;
+ picker.placeholder = descriptor?.placeholder;
picker.value = value;
picker.valueSelection = [value.length, value.length];
- picker.contextKey = descriptor.contextKey;
- picker.filterValue = (value: string) => value.substring(descriptor.prefix.length);
+ picker.contextKey = descriptor?.contextKey;
+ picker.filterValue = (value: string) => value.substring(descriptor ? descriptor.prefix.length : 0);
// Remember as last active picker and clean up once picker get's disposed
this.lastActivePicker = picker;
@@ -72,8 +72,10 @@ export class QuickAccessController extends Disposable implements IQuickAccessCon
}
}));
- // Ask provider to fill the picker as needed
- disposables.add(provider.provide(picker, cts.token));
+ // Ask provider to fill the picker as needed if we have one
+ if (provider) {
+ disposables.add(provider.provide(picker, cts.token));
+ }
// Finally, show the picker. This is important because a provider
// may not call this and then our disposables would leak that rely
@@ -81,8 +83,11 @@ export class QuickAccessController extends Disposable implements IQuickAccessCon
picker.show();
}
- private getOrInstantiateProvider(value: string): [IQuickAccessProvider, IQuickAccessProviderDescriptor] {
- const providerDescriptor = this.registry.getQuickAccessProvider(value) || this.registry.defaultProvider;
+ private getOrInstantiateProvider(value: string): [IQuickAccessProvider | undefined, IQuickAccessProviderDescriptor | undefined] {
+ const providerDescriptor = this.registry.getQuickAccessProvider(value);
+ if (!providerDescriptor) {
+ return [undefined, undefined];
+ }
let provider = this.mapProviderToDescriptor.get(providerDescriptor);
if (!provider) {
diff --git a/src/vs/platform/quickinput/common/quickAccess.ts b/src/vs/platform/quickinput/common/quickAccess.ts
index be8fba3f311..9da4698d02b 100644
--- a/src/vs/platform/quickinput/common/quickAccess.ts
+++ b/src/vs/platform/quickinput/common/quickAccess.ts
@@ -6,9 +6,8 @@
import { IQuickPick, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
import { CancellationToken } from 'vs/base/common/cancellation';
import { Registry } from 'vs/platform/registry/common/platform';
-import { first } from 'vs/base/common/arrays';
+import { first, coalesce } from 'vs/base/common/arrays';
import { startsWith } from 'vs/base/common/strings';
-import { assertIsDefined } from 'vs/base/common/types';
import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
export interface IQuickAccessController {
@@ -92,11 +91,6 @@ export const Extensions = {
export interface IQuickAccessRegistry {
- /**
- * The default provider to use when no other provider matches.
- */
- defaultProvider: IQuickAccessProviderDescriptor;
-
/**
* Registers a quick access provider to the platform.
*/
@@ -113,29 +107,53 @@ export interface IQuickAccessRegistry {
getQuickAccessProvider(prefix: string): IQuickAccessProviderDescriptor | undefined;
}
-class QuickAccessRegistry implements IQuickAccessRegistry {
+export class QuickAccessRegistry implements IQuickAccessRegistry {
private providers: IQuickAccessProviderDescriptor[] = [];
-
- private _defaultProvider: IQuickAccessProviderDescriptor | undefined = undefined;
- get defaultProvider(): IQuickAccessProviderDescriptor { return assertIsDefined(this._defaultProvider); }
- set defaultProvider(provider: IQuickAccessProviderDescriptor) { this._defaultProvider = provider; }
+ private defaultProvider: IQuickAccessProviderDescriptor | undefined = undefined;
registerQuickAccessProvider(provider: IQuickAccessProviderDescriptor): IDisposable {
- this.providers.push(provider);
+
+ // Extract the default provider when no prefix is present
+ if (provider.prefix.length === 0) {
+ this.defaultProvider = provider;
+ } else {
+ this.providers.push(provider);
+ }
// sort the providers by decreasing prefix length, such that longer
// prefixes take priority: 'ext' vs 'ext install' - the latter should win
this.providers.sort((providerA, providerB) => providerB.prefix.length - providerA.prefix.length);
- return toDisposable(() => this.providers.splice(this.providers.indexOf(provider), 1));
+ return toDisposable(() => {
+ this.providers.splice(this.providers.indexOf(provider), 1);
+
+ if (this.defaultProvider === provider) {
+ this.defaultProvider = undefined;
+ }
+ });
}
getQuickAccessProviders(): IQuickAccessProviderDescriptor[] {
- return [this.defaultProvider, ...this.providers];
+ return coalesce([this.defaultProvider, ...this.providers]);
}
getQuickAccessProvider(prefix: string): IQuickAccessProviderDescriptor | undefined {
- return prefix ? (first(this.providers, provider => startsWith(prefix, provider.prefix)) || undefined) : undefined;
+ const result = prefix ? (first(this.providers, provider => startsWith(prefix, provider.prefix)) || undefined) : undefined;
+
+ return result || this.defaultProvider;
+ }
+
+ clear(): Function {
+ const providers = [...this.providers];
+ const defaultProvider = this.defaultProvider;
+
+ this.providers = [];
+ this.defaultProvider = undefined;
+
+ return () => {
+ this.providers = providers;
+ this.defaultProvider = defaultProvider;
+ };
}
}
diff --git a/src/vs/platform/resource/common/resourceIdentityService.ts b/src/vs/platform/resource/common/resourceIdentityService.ts
new file mode 100644
index 00000000000..d81e3dc8e2f
--- /dev/null
+++ b/src/vs/platform/resource/common/resourceIdentityService.ts
@@ -0,0 +1,22 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
+import { URI } from 'vs/base/common/uri';
+import { hash } from 'vs/base/common/hash';
+import { Disposable } from 'vs/base/common/lifecycle';
+
+export const IResourceIdentityService = createDecorator('IResourceIdentityService');
+export interface IResourceIdentityService {
+ _serviceBrand: undefined;
+ resolveResourceIdentity(resource: URI): Promise;
+}
+
+export class WebResourceIdentityService extends Disposable implements IResourceIdentityService {
+ _serviceBrand: undefined;
+ async resolveResourceIdentity(resource: URI): Promise {
+ return hash(resource.toString()).toString(16);
+ }
+}
diff --git a/src/vs/platform/resource/node/resourceIdentityServiceImpl.ts b/src/vs/platform/resource/node/resourceIdentityServiceImpl.ts
new file mode 100644
index 00000000000..71cb292b6ec
--- /dev/null
+++ b/src/vs/platform/resource/node/resourceIdentityServiceImpl.ts
@@ -0,0 +1,54 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { createHash } from 'crypto';
+import { stat } from 'vs/base/node/pfs';
+import { Schemas } from 'vs/base/common/network';
+import { URI } from 'vs/base/common/uri';
+import { isLinux, isMacintosh, isWindows } from 'vs/base/common/platform';
+import { IResourceIdentityService } from 'vs/platform/resource/common/resourceIdentityService';
+import { Disposable } from 'vs/base/common/lifecycle';
+import { ResourceMap } from 'vs/base/common/map';
+
+export class NativeResourceIdentityService extends Disposable implements IResourceIdentityService {
+
+ _serviceBrand: undefined;
+
+ private readonly cache: ResourceMap> = new ResourceMap>();
+
+ resolveResourceIdentity(resource: URI): Promise {
+ let promise = this.cache.get(resource);
+ if (!promise) {
+ promise = this.createIdentity(resource);
+ this.cache.set(resource, promise);
+ }
+ return promise;
+ }
+
+ private async createIdentity(resource: URI): Promise {
+ // Return early the folder is not local
+ if (resource.scheme !== Schemas.file) {
+ return createHash('md5').update(resource.toString()).digest('hex');
+ }
+
+ const fileStat = await stat(resource.fsPath);
+ let ctime: number | undefined;
+ if (isLinux) {
+ ctime = fileStat.ino; // Linux: birthtime is ctime, so we cannot use it! We use the ino instead!
+ } else if (isMacintosh) {
+ ctime = fileStat.birthtime.getTime(); // macOS: birthtime is fine to use as is
+ } else if (isWindows) {
+ if (typeof fileStat.birthtimeMs === 'number') {
+ ctime = Math.floor(fileStat.birthtimeMs); // Windows: fix precision issue in node.js 8.x to get 7.x results (see https://github.com/nodejs/node/issues/19897)
+ } else {
+ ctime = fileStat.birthtime.getTime();
+ }
+ }
+
+ // we use the ctime as extra salt to the ID so that we catch the case of a folder getting
+ // deleted and recreated. in that case we do not want to carry over previous state
+ return createHash('md5').update(resource.fsPath).update(ctime ? String(ctime) : '').digest('hex');
+ }
+}
diff --git a/src/vs/platform/serviceMachineId/common/serviceMachineId.ts b/src/vs/platform/serviceMachineId/common/serviceMachineId.ts
new file mode 100644
index 00000000000..04957244a2a
--- /dev/null
+++ b/src/vs/platform/serviceMachineId/common/serviceMachineId.ts
@@ -0,0 +1,42 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { IFileService } from 'vs/platform/files/common/files';
+import { StorageScope } from 'vs/platform/storage/common/storage';
+import { IEnvironmentService } from 'vs/platform/environment/common/environment';
+import { isUUID, generateUuid } from 'vs/base/common/uuid';
+import { VSBuffer } from 'vs/base/common/buffer';
+
+export async function getServiceMachineId(environmentService: IEnvironmentService, fileService: IFileService, storageService: {
+ get: (key: string, scope: StorageScope, fallbackValue?: string | undefined) => string | undefined,
+ store: (key: string, value: string, scope: StorageScope) => void
+}): Promise {
+ let uuid: string | null = storageService.get('storage.serviceMachineId', StorageScope.GLOBAL) || null;
+ if (uuid) {
+ return uuid;
+ }
+ if (environmentService.serviceMachineIdResource) {
+ try {
+ const contents = await fileService.readFile(environmentService.serviceMachineIdResource);
+ const value = contents.value.toString();
+ uuid = isUUID(value) ? value : null;
+ } catch (e) {
+ uuid = null;
+ }
+
+ if (!uuid) {
+ uuid = generateUuid();
+ try {
+ await fileService.writeFile(environmentService.serviceMachineIdResource, VSBuffer.fromString(uuid));
+ } catch (error) {
+ //noop
+ }
+ }
+ } else {
+ uuid = generateUuid();
+ }
+ storageService.store('storage.serviceMachineId', uuid, StorageScope.GLOBAL);
+ return uuid;
+}
diff --git a/src/vs/platform/theme/common/colorRegistry.ts b/src/vs/platform/theme/common/colorRegistry.ts
index 35599b40d05..54575132334 100644
--- a/src/vs/platform/theme/common/colorRegistry.ts
+++ b/src/vs/platform/theme/common/colorRegistry.ts
@@ -365,6 +365,7 @@ export const listFilterWidgetNoMatchesOutline = registerColor('listFilterWidget.
export const listFilterMatchHighlight = registerColor('list.filterMatchBackground', { dark: editorFindMatchHighlight, light: editorFindMatchHighlight, hc: null }, nls.localize('listFilterMatchHighlight', 'Background color of the filtered match.'));
export const listFilterMatchHighlightBorder = registerColor('list.filterMatchBorder', { dark: editorFindMatchHighlightBorder, light: editorFindMatchHighlightBorder, hc: contrastBorder }, nls.localize('listFilterMatchHighlightBorder', 'Border color of the filtered match.'));
export const treeIndentGuidesStroke = registerColor('tree.indentGuidesStroke', { dark: '#585858', light: '#a9a9a9', hc: '#a9a9a9' }, nls.localize('treeIndentGuidesStroke', "Tree stroke color for the indentation guides."));
+export const listDeemphasizedForeground = registerColor('list.deemphasizedForeground', { dark: '#8C8C8C', light: '#8E8E90', hc: '#A7A8A9' }, nls.localize('listDeemphasizedForeground', "List/Tree foreground color for items that are deemphasized. "));
/**
* Menu colors
diff --git a/src/vs/platform/theme/common/themeService.ts b/src/vs/platform/theme/common/themeService.ts
index 6a1fb3a6966..bca0e9bc03f 100644
--- a/src/vs/platform/theme/common/themeService.ts
+++ b/src/vs/platform/theme/common/themeService.ts
@@ -112,6 +112,11 @@ export interface IColorTheme {
* List of all colors used with tokens. getTokenStyleMetadata references the colors by index into this list.
*/
readonly tokenColorMap: string[];
+
+ /**
+ * Defines whether semantic highlighting should be enabled for the theme.
+ */
+ readonly semanticHighlighting: boolean;
}
export interface IFileIconTheme {
diff --git a/src/vs/platform/theme/common/tokenClassificationRegistry.ts b/src/vs/platform/theme/common/tokenClassificationRegistry.ts
index 2b3bebbb553..8911630ed91 100644
--- a/src/vs/platform/theme/common/tokenClassificationRegistry.ts
+++ b/src/vs/platform/theme/common/tokenClassificationRegistry.ts
@@ -433,6 +433,7 @@ function registerDefaultClassifications(): void {
registerTokenStyleDefault('variable.readonly', [['variable.other.constant']]);
+ registerTokenStyleDefault('property.readonly', [['variable.other.constant.property']]);
}
export function getTokenClassificationRegistry(): ITokenClassificationRegistry {
diff --git a/src/vs/platform/theme/test/common/testThemeService.ts b/src/vs/platform/theme/test/common/testThemeService.ts
index c50b168605c..acada67c302 100644
--- a/src/vs/platform/theme/test/common/testThemeService.ts
+++ b/src/vs/platform/theme/test/common/testThemeService.ts
@@ -28,6 +28,8 @@ export class TestColorTheme implements IColorTheme {
return undefined;
}
+ readonly semanticHighlighting = false;
+
get tokenColorMap(): string[] {
return [];
}
diff --git a/src/vs/platform/userDataSync/common/abstractSynchronizer.ts b/src/vs/platform/userDataSync/common/abstractSynchronizer.ts
index 8f79415fe07..2b6180c9ab2 100644
--- a/src/vs/platform/userDataSync/common/abstractSynchronizer.ts
+++ b/src/vs/platform/userDataSync/common/abstractSynchronizer.ts
@@ -7,7 +7,7 @@ import { Disposable } from 'vs/base/common/lifecycle';
import { IFileService, IFileContent, FileChangesEvent, FileSystemProviderError, FileSystemProviderErrorCode, FileOperationResult, FileOperationError } from 'vs/platform/files/common/files';
import { VSBuffer } from 'vs/base/common/buffer';
import { URI } from 'vs/base/common/uri';
-import { SyncSource, SyncStatus, IUserData, IUserDataSyncStoreService, UserDataSyncErrorCode, UserDataSyncError, IUserDataSyncLogService, IUserDataSyncUtilService, ResourceKey, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync';
+import { SyncResource, SyncStatus, IUserData, IUserDataSyncStoreService, UserDataSyncErrorCode, UserDataSyncError, IUserDataSyncLogService, IUserDataSyncUtilService, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { joinPath, dirname } from 'vs/base/common/resources';
import { CancelablePromise } from 'vs/base/common/async';
@@ -19,6 +19,7 @@ import { IStringDictionary } from 'vs/base/common/collections';
import { localize } from 'vs/nls';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { isString } from 'vs/base/common/types';
+import { uppercaseFirstLetter } from 'vs/base/common/strings';
type SyncSourceClassification = {
source?: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true };
@@ -54,10 +55,10 @@ export abstract class AbstractSynchroniser extends Disposable {
readonly onDidChangeLocal: Event = this._onDidChangeLocal.event;
protected readonly lastSyncResource: URI;
+ protected readonly syncResourceLogLabel: string;
constructor(
- readonly source: SyncSource,
- readonly resourceKey: ResourceKey,
+ readonly resource: SyncResource,
@IFileService protected readonly fileService: IFileService,
@IEnvironmentService environmentService: IEnvironmentService,
@IUserDataSyncStoreService protected readonly userDataSyncStoreService: IUserDataSyncStoreService,
@@ -68,8 +69,9 @@ export abstract class AbstractSynchroniser extends Disposable {
@IConfigurationService protected readonly configurationService: IConfigurationService,
) {
super();
- this.syncFolder = joinPath(environmentService.userDataSyncHome, source);
- this.lastSyncResource = joinPath(this.syncFolder, `lastSync${this.resourceKey}.json`);
+ this.syncResourceLogLabel = uppercaseFirstLetter(this.resource);
+ this.syncFolder = joinPath(environmentService.userDataSyncHome, resource);
+ this.lastSyncResource = joinPath(this.syncFolder, `lastSync${this.resource}.json`);
}
protected setStatus(status: SyncStatus): void {
@@ -79,32 +81,32 @@ export abstract class AbstractSynchroniser extends Disposable {
this._onDidChangStatus.fire(status);
if (status === SyncStatus.HasConflicts) {
// Log to telemetry when there is a sync conflict
- this.telemetryService.publicLog2<{ source: string }, SyncSourceClassification>('sync/conflictsDetected', { source: this.source });
+ this.telemetryService.publicLog2<{ source: string }, SyncSourceClassification>('sync/conflictsDetected', { source: this.resource });
}
if (oldStatus === SyncStatus.HasConflicts && status === SyncStatus.Idle) {
// Log to telemetry when conflicts are resolved
- this.telemetryService.publicLog2<{ source: string }, SyncSourceClassification>('sync/conflictsResolved', { source: this.source });
+ this.telemetryService.publicLog2<{ source: string }, SyncSourceClassification>('sync/conflictsResolved', { source: this.resource });
}
}
}
- protected isEnabled(): boolean { return this.userDataSyncEnablementService.isResourceEnabled(this.resourceKey); }
+ protected isEnabled(): boolean { return this.userDataSyncEnablementService.isResourceEnabled(this.resource); }
async sync(ref?: string): Promise {
if (!this.isEnabled()) {
- this.logService.info(`${this.source}: Skipped synchronizing ${this.source.toLowerCase()} as it is disabled.`);
+ this.logService.info(`${this.syncResourceLogLabel}: Skipped synchronizing ${this.resource.toLowerCase()} as it is disabled.`);
return;
}
if (this.status === SyncStatus.HasConflicts) {
- this.logService.info(`${this.source}: Skipped synchronizing ${this.source.toLowerCase()} as there are conflicts.`);
+ this.logService.info(`${this.syncResourceLogLabel}: Skipped synchronizing ${this.resource.toLowerCase()} as there are conflicts.`);
return;
}
if (this.status === SyncStatus.Syncing) {
- this.logService.info(`${this.source}: Skipped synchronizing ${this.source.toLowerCase()} as it is running already.`);
+ this.logService.info(`${this.syncResourceLogLabel}: Skipped synchronizing ${this.resource.toLowerCase()} as it is running already.`);
return;
}
- this.logService.trace(`${this.source}: Started synchronizing ${this.source.toLowerCase()}...`);
+ this.logService.trace(`${this.syncResourceLogLabel}: Started synchronizing ${this.resource.toLowerCase()}...`);
this.setStatus(SyncStatus.Syncing);
const lastSyncUserData = await this.getLastSyncUserData();
@@ -114,9 +116,9 @@ export abstract class AbstractSynchroniser extends Disposable {
try {
status = await this.doSync(remoteUserData, lastSyncUserData);
if (status === SyncStatus.HasConflicts) {
- this.logService.info(`${this.source}: Detected conflicts while synchronizing ${this.source.toLowerCase()}.`);
+ this.logService.info(`${this.syncResourceLogLabel}: Detected conflicts while synchronizing ${this.resource.toLowerCase()}.`);
} else if (status === SyncStatus.Idle) {
- this.logService.trace(`${this.source}: Finished synchronizing ${this.source.toLowerCase()}.`);
+ this.logService.trace(`${this.syncResourceLogLabel}: Finished synchronizing ${this.resource.toLowerCase()}.`);
}
} finally {
this.setStatus(status);
@@ -126,8 +128,8 @@ export abstract class AbstractSynchroniser extends Disposable {
protected async doSync(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null): Promise {
if (remoteUserData.syncData && remoteUserData.syncData.version > this.version) {
// current version is not compatible with cloud version
- this.telemetryService.publicLog2<{ source: string }, SyncSourceClassification>('sync/incompatible', { source: this.source });
- throw new UserDataSyncError(localize('incompatible', "Cannot sync {0} as its version {1} is not compatible with cloud {2}", this.source, this.version, remoteUserData.syncData.version), UserDataSyncErrorCode.Incompatible, this.source);
+ this.telemetryService.publicLog2<{ source: string }, SyncSourceClassification>('sync/incompatible', { source: this.resource });
+ throw new UserDataSyncError(localize('incompatible', "Cannot sync {0} as its version {1} is not compatible with cloud {2}", this.resource, this.version, remoteUserData.syncData.version), UserDataSyncErrorCode.Incompatible, this.resource);
}
try {
const status = await this.performSync(remoteUserData, lastSyncUserData);
@@ -137,7 +139,7 @@ export abstract class AbstractSynchroniser extends Disposable {
switch (e.code) {
case UserDataSyncErrorCode.RemotePreconditionFailed:
// Rejected as there is a new remote version. Syncing again,
- this.logService.info(`${this.source}: Failed to synchronize as there is a new remote version available. Synchronizing again...`);
+ this.logService.info(`${this.syncResourceLogLabel}: Failed to synchronize as there is a new remote version available. Synchronizing again...`);
// Avoid cache and get latest remote user data - https://github.com/microsoft/vscode/issues/90624
remoteUserData = await this.getRemoteUserData(null);
return this.doSync(remoteUserData, lastSyncUserData);
@@ -163,7 +165,7 @@ export abstract class AbstractSynchroniser extends Disposable {
}
async getLocalBackupContent(ref?: string): Promise {
- return this.userDataSyncBackupStoreService.resolveContent(this.resourceKey, ref);
+ return this.userDataSyncBackupStoreService.resolveContent(this.resource, ref);
}
async resetLocal(): Promise {
@@ -225,23 +227,23 @@ export abstract class AbstractSynchroniser extends Disposable {
private async getUserData(refOrLastSyncData: string | IRemoteUserData | null): Promise {
if (isString(refOrLastSyncData)) {
- const content = await this.userDataSyncStoreService.resolveContent(this.resourceKey, refOrLastSyncData);
+ const content = await this.userDataSyncStoreService.resolveContent(this.resource, refOrLastSyncData);
return { ref: refOrLastSyncData, content };
} else {
const lastSyncUserData: IUserData | null = refOrLastSyncData ? { ref: refOrLastSyncData.ref, content: refOrLastSyncData.syncData ? JSON.stringify(refOrLastSyncData.syncData) : null } : null;
- return this.userDataSyncStoreService.read(this.resourceKey, lastSyncUserData, this.source);
+ return this.userDataSyncStoreService.read(this.resource, lastSyncUserData);
}
}
protected async updateRemoteUserData(content: string, ref: string | null): Promise {
const syncData: ISyncData = { version: this.version, content };
- ref = await this.userDataSyncStoreService.write(this.resourceKey, JSON.stringify(syncData), ref, this.source);
+ ref = await this.userDataSyncStoreService.write(this.resource, JSON.stringify(syncData), ref);
return { ref, syncData };
}
protected async backupLocal(content: string): Promise {
const syncData: ISyncData = { version: this.version, content };
- return this.userDataSyncBackupStoreService.backup(this.resourceKey, JSON.stringify(syncData));
+ return this.userDataSyncBackupStoreService.backup(this.resource, JSON.stringify(syncData));
}
protected abstract readonly version: number;
@@ -264,8 +266,7 @@ export abstract class AbstractFileSynchroniser extends AbstractSynchroniser {
constructor(
protected readonly file: URI,
- source: SyncSource,
- resourceKey: ResourceKey,
+ resource: SyncResource,
@IFileService fileService: IFileService,
@IEnvironmentService environmentService: IEnvironmentService,
@IUserDataSyncStoreService userDataSyncStoreService: IUserDataSyncStoreService,
@@ -275,14 +276,14 @@ export abstract class AbstractFileSynchroniser extends AbstractSynchroniser {
@IUserDataSyncLogService logService: IUserDataSyncLogService,
@IConfigurationService configurationService: IConfigurationService,
) {
- super(source, resourceKey, fileService, environmentService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncEnablementService, telemetryService, logService, configurationService);
+ super(resource, fileService, environmentService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncEnablementService, telemetryService, logService, configurationService);
this._register(this.fileService.watch(dirname(file)));
this._register(this.fileService.onDidFilesChange(e => this.onFileChanges(e)));
}
async stop(): Promise {
this.cancel();
- this.logService.trace(`${this.source}: Stopped synchronizing ${this.source.toLowerCase()}.`);
+ this.logService.trace(`${this.syncResourceLogLabel}: Stopped synchronizing ${this.resource.toLowerCase()}.`);
try {
await this.fileService.del(this.conflictsPreviewResource);
} catch (e) { /* ignore */ }
@@ -362,8 +363,7 @@ export abstract class AbstractJsonFileSynchroniser extends AbstractFileSynchroni
constructor(
file: URI,
- source: SyncSource,
- resourceKey: ResourceKey,
+ resource: SyncResource,
@IFileService fileService: IFileService,
@IEnvironmentService environmentService: IEnvironmentService,
@IUserDataSyncStoreService userDataSyncStoreService: IUserDataSyncStoreService,
@@ -374,7 +374,7 @@ export abstract class AbstractJsonFileSynchroniser extends AbstractFileSynchroni
@IUserDataSyncUtilService protected readonly userDataSyncUtilService: IUserDataSyncUtilService,
@IConfigurationService configurationService: IConfigurationService,
) {
- super(file, source, resourceKey, fileService, environmentService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncEnablementService, telemetryService, logService, configurationService);
+ super(file, resource, fileService, environmentService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncEnablementService, telemetryService, logService, configurationService);
}
protected hasErrors(content: string): boolean {
diff --git a/src/vs/platform/userDataSync/common/extensionsSync.ts b/src/vs/platform/userDataSync/common/extensionsSync.ts
index cdc3d6faab5..ad91ac82436 100644
--- a/src/vs/platform/userDataSync/common/extensionsSync.ts
+++ b/src/vs/platform/userDataSync/common/extensionsSync.ts
@@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import { SyncStatus, IUserDataSyncStoreService, ISyncExtension, IUserDataSyncLogService, IUserDataSynchroniser, SyncSource, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync';
+import { SyncStatus, IUserDataSyncStoreService, ISyncExtension, IUserDataSyncLogService, IUserDataSynchroniser, SyncResource, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync';
import { Event } from 'vs/base/common/event';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IExtensionManagementService, IExtensionGalleryService, IGlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement';
@@ -50,7 +50,7 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
@IUserDataSyncEnablementService userDataSyncEnablementService: IUserDataSyncEnablementService,
@ITelemetryService telemetryService: ITelemetryService,
) {
- super(SyncSource.Extensions, 'extensions', fileService, environmentService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncEnablementService, telemetryService, logService, configurationService);
+ super(SyncResource.Extensions, fileService, environmentService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncEnablementService, telemetryService, logService, configurationService);
this._register(
Event.debounce(
Event.any(
@@ -62,14 +62,14 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
async pull(): Promise {
if (!this.isEnabled()) {
- this.logService.info('Extensions: Skipped pulling extensions as it is disabled.');
+ this.logService.info(`${this.syncResourceLogLabel}: Skipped pulling extensions as it is disabled.`);
return;
}
this.stop();
try {
- this.logService.info('Extensions: Started pulling extensions...');
+ this.logService.info(`${this.syncResourceLogLabel}: Started pulling extensions...`);
this.setStatus(SyncStatus.Syncing);
const lastSyncUserData = await this.getLastSyncUserData();
@@ -84,10 +84,10 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
// No remote exists to pull
else {
- this.logService.info('Extensions: Remote extensions does not exist.');
+ this.logService.info(`${this.syncResourceLogLabel}: Remote extensions does not exist.`);
}
- this.logService.info('Extensions: Finished pulling extensions.');
+ this.logService.info(`${this.syncResourceLogLabel}: Finished pulling extensions.`);
} finally {
this.setStatus(SyncStatus.Idle);
}
@@ -95,14 +95,14 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
async push(): Promise {
if (!this.isEnabled()) {
- this.logService.info('Extensions: Skipped pushing extensions as it is disabled.');
+ this.logService.info(`${this.syncResourceLogLabel}: Skipped pushing extensions as it is disabled.`);
return;
}
this.stop();
try {
- this.logService.info('Extensions: Started pushing extensions...');
+ this.logService.info(`${this.syncResourceLogLabel}: Started pushing extensions...`);
this.setStatus(SyncStatus.Syncing);
const localExtensions = await this.getLocalExtensions();
@@ -111,7 +111,7 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
const remoteUserData = await this.getRemoteUserData(lastSyncUserData);
await this.apply({ added, removed, updated, remote, remoteUserData, localExtensions, skippedExtensions: [], lastSyncUserData }, true);
- this.logService.info('Extensions: Finished pushing extensions.');
+ this.logService.info(`${this.syncResourceLogLabel}: Finished pushing extensions.`);
} finally {
this.setStatus(SyncStatus.Idle);
}
@@ -148,7 +148,7 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
}
accept(content: string): Promise {
- throw new Error('Extensions: Conflicts should not occur');
+ throw new Error(`${this.syncResourceLogLabel}: Conflicts should not occur`);
}
async hasLocalData(): Promise {
@@ -177,9 +177,9 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
const localExtensions = await this.getLocalExtensions();
if (remoteExtensions) {
- this.logService.trace('Extensions: Merging remote extensions with local extensions...');
+ this.logService.trace(`${this.syncResourceLogLabel}: Merging remote extensions with local extensions...`);
} else {
- this.logService.trace('Extensions: Remote extensions does not exist. Synchronizing extensions for the first time.');
+ this.logService.trace(`${this.syncResourceLogLabel}: Remote extensions does not exist. Synchronizing extensions for the first time.`);
}
const { added, removed, updated, remote } = merge(localExtensions, remoteExtensions, lastSyncExtensions, skippedExtensions, this.getIgnoredExtensions());
@@ -196,7 +196,7 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
const hasChanges = added.length || removed.length || updated.length || remote;
if (!hasChanges) {
- this.logService.info('Extensions: No changes found during synchronizing extensions.');
+ this.logService.info(`${this.syncResourceLogLabel}: No changes found during synchronizing extensions.`);
}
if (added.length || removed.length || updated.length) {
@@ -208,17 +208,17 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
if (remote) {
// update remote
- this.logService.trace('Extensions: Updating remote extensions...');
+ this.logService.trace(`${this.syncResourceLogLabel}: Updating remote extensions...`);
const content = JSON.stringify(remote);
remoteUserData = await this.updateRemoteUserData(content, forcePush ? null : remoteUserData.ref);
- this.logService.info('Extensions: Updated remote extensions');
+ this.logService.info(`${this.syncResourceLogLabel}: Updated remote extensions`);
}
if (lastSyncUserData?.ref !== remoteUserData.ref) {
// update last sync
- this.logService.trace('Extensions: Updating last synchronized extensions...');
+ this.logService.trace(`${this.syncResourceLogLabel}: Updating last synchronized extensions...`);
await this.updateLastSyncUserData(remoteUserData, { skippedExtensions });
- this.logService.info('Extensions: Updated last synchronized extensions');
+ this.logService.info(`${this.syncResourceLogLabel}: Updated last synchronized extensions`);
}
}
@@ -230,9 +230,9 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
const installedExtensions = await this.extensionManagementService.getInstalled(ExtensionType.User);
const extensionsToRemove = installedExtensions.filter(({ identifier }) => removed.some(r => areSameExtensions(identifier, r)));
await Promise.all(extensionsToRemove.map(async extensionToRemove => {
- this.logService.trace('Extensions: Uninstalling local extension...', extensionToRemove.identifier.id);
+ this.logService.trace(`${this.syncResourceLogLabel}: Uninstalling local extension...', extensionToRemove.identifier.i`);
await this.extensionManagementService.uninstall(extensionToRemove);
- this.logService.info('Extensions: Uninstalled local extension.', extensionToRemove.identifier.id);
+ this.logService.info(`${this.syncResourceLogLabel}: Uninstalled local extension.', extensionToRemove.identifier.i`);
removeFromSkipped.push(extensionToRemove.identifier);
}));
}
@@ -245,13 +245,13 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
// Builtin Extension: Sync only enablement state
if (installedExtension && installedExtension.type === ExtensionType.System) {
if (e.disabled) {
- this.logService.trace('Extensions: Disabling extension...', e.identifier.id);
+ this.logService.trace(`${this.syncResourceLogLabel}: Disabling extension...', e.identifier.i`);
await this.extensionEnablementService.disableExtension(e.identifier);
- this.logService.info('Extensions: Disabled extension', e.identifier.id);
+ this.logService.info(`${this.syncResourceLogLabel}: Disabled extension', e.identifier.i`);
} else {
- this.logService.trace('Extensions: Enabling extension...', e.identifier.id);
+ this.logService.trace(`${this.syncResourceLogLabel}: Enabling extension...', e.identifier.i`);
await this.extensionEnablementService.enableExtension(e.identifier);
- this.logService.info('Extensions: Enabled extension', e.identifier.id);
+ this.logService.info(`${this.syncResourceLogLabel}: Enabled extension', e.identifier.i`);
}
removeFromSkipped.push(e.identifier);
return;
@@ -261,19 +261,19 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
if (extension) {
try {
if (e.disabled) {
- this.logService.trace('Extensions: Disabling extension...', e.identifier.id, extension.version);
+ this.logService.trace(`${this.syncResourceLogLabel}: Disabling extension...', e.identifier.id, extension.versio`);
await this.extensionEnablementService.disableExtension(extension.identifier);
- this.logService.info('Extensions: Disabled extension', e.identifier.id, extension.version);
+ this.logService.info(`${this.syncResourceLogLabel}: Disabled extension', e.identifier.id, extension.versio`);
} else {
- this.logService.trace('Extensions: Enabling extension...', e.identifier.id, extension.version);
+ this.logService.trace(`${this.syncResourceLogLabel}: Enabling extension...', e.identifier.id, extension.versio`);
await this.extensionEnablementService.enableExtension(extension.identifier);
- this.logService.info('Extensions: Enabled extension', e.identifier.id, extension.version);
+ this.logService.info(`${this.syncResourceLogLabel}: Enabled extension', e.identifier.id, extension.versio`);
}
// Install only if the extension does not exist
if (!installedExtension || installedExtension.manifest.version !== extension.version) {
- this.logService.trace('Extensions: Installing extension...', e.identifier.id, extension.version);
+ this.logService.trace(`${this.syncResourceLogLabel}: Installing extension...', e.identifier.id, extension.versio`);
await this.extensionManagementService.installFromGallery(extension);
- this.logService.info('Extensions: Installed extension.', e.identifier.id, extension.version);
+ this.logService.info(`${this.syncResourceLogLabel}: Installed extension.', e.identifier.id, extension.versio`);
removeFromSkipped.push(extension.identifier);
}
} catch (error) {
diff --git a/src/vs/platform/userDataSync/common/globalStateSync.ts b/src/vs/platform/userDataSync/common/globalStateSync.ts
index a095a09b482..0be9b802764 100644
--- a/src/vs/platform/userDataSync/common/globalStateSync.ts
+++ b/src/vs/platform/userDataSync/common/globalStateSync.ts
@@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import { SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IGlobalState, SyncSource, IUserDataSynchroniser, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync';
+import { SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IGlobalState, SyncResource, IUserDataSynchroniser, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync';
import { VSBuffer } from 'vs/base/common/buffer';
import { Event } from 'vs/base/common/event';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
@@ -41,21 +41,21 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
@ITelemetryService telemetryService: ITelemetryService,
@IConfigurationService configurationService: IConfigurationService,
) {
- super(SyncSource.GlobalState, 'globalState', fileService, environmentService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncEnablementService, telemetryService, logService, configurationService);
+ super(SyncResource.GlobalState, fileService, environmentService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncEnablementService, telemetryService, logService, configurationService);
this._register(this.fileService.watch(dirname(this.environmentService.argvResource)));
this._register(Event.filter(this.fileService.onDidFilesChange, e => e.contains(this.environmentService.argvResource))(() => this._onDidChangeLocal.fire()));
}
async pull(): Promise {
if (!this.isEnabled()) {
- this.logService.info('UI State: Skipped pulling ui state as it is disabled.');
+ this.logService.info(`${this.syncResourceLogLabel}: Skipped pulling ui state as it is disabled.`);
return;
}
this.stop();
try {
- this.logService.info('UI State: Started pulling ui state...');
+ this.logService.info(`${this.syncResourceLogLabel}: Started pulling ui state...`);
this.setStatus(SyncStatus.Syncing);
const lastSyncUserData = await this.getLastSyncUserData();
@@ -69,10 +69,10 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
// No remote exists to pull
else {
- this.logService.info('UI State: Remote UI state does not exist.');
+ this.logService.info(`${this.syncResourceLogLabel}: Remote UI state does not exist.`);
}
- this.logService.info('UI State: Finished pulling UI state.');
+ this.logService.info(`${this.syncResourceLogLabel}: Finished pulling UI state.`);
} finally {
this.setStatus(SyncStatus.Idle);
}
@@ -80,14 +80,14 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
async push(): Promise {
if (!this.isEnabled()) {
- this.logService.info('UI State: Skipped pushing UI State as it is disabled.');
+ this.logService.info(`${this.syncResourceLogLabel}: Skipped pushing UI State as it is disabled.`);
return;
}
this.stop();
try {
- this.logService.info('UI State: Started pushing UI State...');
+ this.logService.info(`${this.syncResourceLogLabel}: Started pushing UI State...`);
this.setStatus(SyncStatus.Syncing);
const localUserData = await this.getLocalGlobalState();
@@ -95,7 +95,7 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
const remoteUserData = await this.getRemoteUserData(lastSyncUserData);
await this.apply({ local: undefined, remote: localUserData, remoteUserData, localUserData, lastSyncUserData }, true);
- this.logService.info('UI State: Finished pushing UI State.');
+ this.logService.info(`${this.syncResourceLogLabel}: Finished pushing UI State.`);
} finally {
this.setStatus(SyncStatus.Idle);
}
@@ -132,7 +132,7 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
}
accept(content: string): Promise {
- throw new Error('UI State: Conflicts should not occur');
+ throw new Error(`${this.syncResourceLogLabel}: Conflicts should not occur`);
}
async hasLocalData(): Promise {
@@ -160,9 +160,9 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
const localGloablState = await this.getLocalGlobalState();
if (remoteGlobalState) {
- this.logService.trace('UI State: Merging remote ui state with local ui state...');
+ this.logService.trace(`${this.syncResourceLogLabel}: Merging remote ui state with local ui state...`);
} else {
- this.logService.trace('UI State: Remote ui state does not exist. Synchronizing ui state for the first time.');
+ this.logService.trace(`${this.syncResourceLogLabel}: Remote ui state does not exist. Synchronizing ui state for the first time.`);
}
const { local, remote } = merge(localGloablState, remoteGlobalState, lastSyncGlobalState);
@@ -175,30 +175,30 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
const hasChanges = local || remote;
if (!hasChanges) {
- this.logService.info('UI State: No changes found during synchronizing ui state.');
+ this.logService.info(`${this.syncResourceLogLabel}: No changes found during synchronizing ui state.`);
}
if (local) {
// update local
- this.logService.trace('UI State: Updating local ui state...');
+ this.logService.trace(`${this.syncResourceLogLabel}: Updating local ui state...`);
await this.backupLocal(JSON.stringify(localUserData));
await this.writeLocalGlobalState(local);
- this.logService.info('UI State: Updated local ui state');
+ this.logService.info(`${this.syncResourceLogLabel}: Updated local ui state`);
}
if (remote) {
// update remote
- this.logService.trace('UI State: Updating remote ui state...');
+ this.logService.trace(`${this.syncResourceLogLabel}: Updating remote ui state...`);
const content = JSON.stringify(remote);
remoteUserData = await this.updateRemoteUserData(content, forcePush ? null : remoteUserData.ref);
- this.logService.info('UI State: Updated remote ui state');
+ this.logService.info(`${this.syncResourceLogLabel}: Updated remote ui state`);
}
if (lastSyncUserData?.ref !== remoteUserData.ref) {
// update last sync
- this.logService.trace('UI State: Updating last synchronized ui state...');
+ this.logService.trace(`${this.syncResourceLogLabel}: Updating last synchronized ui state...`);
await this.updateLastSyncUserData(remoteUserData);
- this.logService.info('UI State: Updated last synchronized ui state');
+ this.logService.info(`${this.syncResourceLogLabel}: Updated last synchronized ui state`);
}
}
diff --git a/src/vs/platform/userDataSync/common/keybindingsSync.ts b/src/vs/platform/userDataSync/common/keybindingsSync.ts
index c3f02bac0da..c5248fe5a8b 100644
--- a/src/vs/platform/userDataSync/common/keybindingsSync.ts
+++ b/src/vs/platform/userDataSync/common/keybindingsSync.ts
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { IFileService, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files';
-import { UserDataSyncError, UserDataSyncErrorCode, SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IUserDataSyncUtilService, SyncSource, IUserDataSynchroniser, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync';
+import { UserDataSyncError, UserDataSyncErrorCode, SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IUserDataSyncUtilService, SyncResource, IUserDataSynchroniser, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync';
import { merge } from 'vs/platform/userDataSync/common/keybindingsMerge';
import { VSBuffer } from 'vs/base/common/buffer';
import { parse } from 'vs/base/common/json';
@@ -43,19 +43,19 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem
@IUserDataSyncUtilService userDataSyncUtilService: IUserDataSyncUtilService,
@ITelemetryService telemetryService: ITelemetryService,
) {
- super(environmentService.keybindingsResource, SyncSource.Keybindings, 'keybindings', fileService, environmentService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncEnablementService, telemetryService, logService, userDataSyncUtilService, configurationService);
+ super(environmentService.keybindingsResource, SyncResource.Keybindings, fileService, environmentService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncEnablementService, telemetryService, logService, userDataSyncUtilService, configurationService);
}
async pull(): Promise {
if (!this.isEnabled()) {
- this.logService.info('Keybindings: Skipped pulling keybindings as it is disabled.');
+ this.logService.info(`${this.syncResourceLogLabel}: Skipped pulling keybindings as it is disabled.`);
return;
}
this.stop();
try {
- this.logService.info('Keybindings: Started pulling keybindings...');
+ this.logService.info(`${this.syncResourceLogLabel}: Started pulling keybindings...`);
this.setStatus(SyncStatus.Syncing);
const lastSyncUserData = await this.getLastSyncUserData();
@@ -78,10 +78,10 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem
// No remote exists to pull
else {
- this.logService.info('Keybindings: Remote keybindings does not exist.');
+ this.logService.info(`${this.syncResourceLogLabel}: Remote keybindings does not exist.`);
}
- this.logService.info('Keybindings: Finished pulling keybindings.');
+ this.logService.info(`${this.syncResourceLogLabel}: Finished pulling keybindings.`);
} finally {
this.setStatus(SyncStatus.Idle);
}
@@ -90,14 +90,14 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem
async push(): Promise {
if (!this.isEnabled()) {
- this.logService.info('Keybindings: Skipped pushing keybindings as it is disabled.');
+ this.logService.info(`${this.syncResourceLogLabel}: Skipped pushing keybindings as it is disabled.`);
return;
}
this.stop();
try {
- this.logService.info('Keybindings: Started pushing keybindings...');
+ this.logService.info(`${this.syncResourceLogLabel}: Started pushing keybindings...`);
this.setStatus(SyncStatus.Syncing);
const fileContent = await this.getLocalFileContent();
@@ -119,10 +119,10 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem
// No local exists to push
else {
- this.logService.info('Keybindings: Local keybindings does not exist.');
+ this.logService.info(`${this.syncResourceLogLabel}: Local keybindings does not exist.`);
}
- this.logService.info('Keybindings: Finished pushing keybindings.');
+ this.logService.info(`${this.syncResourceLogLabel}: Finished pushing keybindings.`);
} finally {
this.setStatus(SyncStatus.Idle);
}
@@ -202,7 +202,7 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem
switch (e.code) {
case UserDataSyncErrorCode.LocalPreconditionFailed:
// Rejected as there is a new local version. Syncing again.
- this.logService.info('Keybindings: Failed to synchronize keybindings as there is a new local version available. Synchronizing again...');
+ this.logService.info(`${this.syncResourceLogLabel}: Failed to synchronize keybindings as there is a new local version available. Synchronizing again...`);
return this.performSync(remoteUserData, lastSyncUserData);
}
}
@@ -219,21 +219,21 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem
if (content !== null) {
if (this.hasErrors(content)) {
- throw new UserDataSyncError(localize('errorInvalidSettings', "Unable to sync keybindings as there are errors/warning in keybindings file."), UserDataSyncErrorCode.LocalInvalidContent, this.source);
+ throw new UserDataSyncError(localize('errorInvalidSettings', "Unable to sync keybindings as there are errors/warning in keybindings file."), UserDataSyncErrorCode.LocalInvalidContent, this.resource);
}
if (hasLocalChanged) {
- this.logService.trace('Keybindings: Updating local keybindings...');
+ this.logService.trace(`${this.syncResourceLogLabel}: Updating local keybindings...`);
await this.backupLocal(this.toSyncContent(content, null));
await this.updateLocalFileContent(content, fileContent);
- this.logService.info('Keybindings: Updated local keybindings');
+ this.logService.info(`${this.syncResourceLogLabel}: Updated local keybindings`);
}
if (hasRemoteChanged) {
- this.logService.trace('Keybindings: Updating remote keybindings...');
+ this.logService.trace(`${this.syncResourceLogLabel}: Updating remote keybindings...`);
const remoteContents = this.toSyncContent(content, remoteUserData.syncData ? remoteUserData.syncData.content : null);
remoteUserData = await this.updateRemoteUserData(remoteContents, forcePush ? null : remoteUserData.ref);
- this.logService.info('Keybindings: Updated remote keybindings');
+ this.logService.info(`${this.syncResourceLogLabel}: Updated remote keybindings`);
}
// Delete the preview
@@ -241,14 +241,14 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem
await this.fileService.del(this.conflictsPreviewResource);
} catch (e) { /* ignore */ }
} else {
- this.logService.info('Keybindings: No changes found during synchronizing keybindings.');
+ this.logService.info(`${this.syncResourceLogLabel}: No changes found during synchronizing keybindings.`);
}
if (lastSyncUserData?.ref !== remoteUserData.ref && (content !== null || fileContent !== null)) {
- this.logService.trace('Keybindings: Updating last synchronized keybindings...');
+ this.logService.trace(`${this.syncResourceLogLabel}: Updating last synchronized keybindings...`);
const lastSyncContent = this.toSyncContent(content !== null ? content : fileContent!.value.toString(), null);
await this.updateLastSyncUserData({ ref: remoteUserData.ref, syncData: { version: remoteUserData.syncData!.version, content: lastSyncContent } });
- this.logService.info('Keybindings: Updated last synchronized keybindings');
+ this.logService.info(`${this.syncResourceLogLabel}: Updated last synchronized keybindings`);
}
this.syncPreviewResultPromise = null;
@@ -276,14 +276,14 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem
if (remoteContent) {
const localContent: string = fileContent ? fileContent.value.toString() : '[]';
if (this.hasErrors(localContent)) {
- throw new UserDataSyncError(localize('errorInvalidSettings', "Unable to sync keybindings as there are errors/warning in keybindings file."), UserDataSyncErrorCode.LocalInvalidContent, this.source);
+ throw new UserDataSyncError(localize('errorInvalidSettings', "Unable to sync keybindings as there are errors/warning in keybindings file."), UserDataSyncErrorCode.LocalInvalidContent, this.resource);
}
if (!lastSyncContent // First time sync
|| lastSyncContent !== localContent // Local has forwarded
|| lastSyncContent !== remoteContent // Remote has forwarded
) {
- this.logService.trace('Keybindings: Merging remote keybindings with local keybindings...');
+ this.logService.trace(`${this.syncResourceLogLabel}: Merging remote keybindings with local keybindings...`);
const result = await merge(localContent, remoteContent, lastSyncContent, formattingOptions, this.userDataSyncUtilService);
// Sync only if there are changes
if (result.hasChanges) {
@@ -297,7 +297,7 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem
// First time syncing to remote
else if (fileContent) {
- this.logService.trace('Keybindings: Remote keybindings does not exist. Synchronizing keybindings for the first time.');
+ this.logService.trace(`${this.syncResourceLogLabel}: Remote keybindings does not exist. Synchronizing keybindings for the first time.`);
content = fileContent.value.toString();
hasRemoteChanged = true;
}
diff --git a/src/vs/platform/userDataSync/common/settingsSync.ts b/src/vs/platform/userDataSync/common/settingsSync.ts
index 219f2c2a698..6704fa622c3 100644
--- a/src/vs/platform/userDataSync/common/settingsSync.ts
+++ b/src/vs/platform/userDataSync/common/settingsSync.ts
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { IFileService, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files';
-import { UserDataSyncError, UserDataSyncErrorCode, SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IUserDataSyncUtilService, IConflictSetting, ISettingsSyncService, CONFIGURATION_SYNC_STORE_KEY, SyncSource, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync';
+import { UserDataSyncError, UserDataSyncErrorCode, SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IUserDataSyncUtilService, IConflictSetting, CONFIGURATION_SYNC_STORE_KEY, SyncResource, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync';
import { VSBuffer } from 'vs/base/common/buffer';
import { parse } from 'vs/base/common/json';
import { localize } from 'vs/nls';
@@ -33,7 +33,7 @@ function isSettingsSyncContent(thing: any): thing is ISettingsSyncContent {
&& Object.keys(thing).length === 1;
}
-export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implements ISettingsSyncService {
+export class SettingsSynchroniser extends AbstractJsonFileSynchroniser {
_serviceBrand: any;
@@ -57,7 +57,7 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement
@ITelemetryService telemetryService: ITelemetryService,
@IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService,
) {
- super(environmentService.settingsResource, SyncSource.Settings, 'settings', fileService, environmentService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncEnablementService, telemetryService, logService, userDataSyncUtilService, configurationService);
+ super(environmentService.settingsResource, SyncResource.Settings, fileService, environmentService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncEnablementService, telemetryService, logService, userDataSyncUtilService, configurationService);
}
protected setStatus(status: SyncStatus): void {
@@ -78,14 +78,14 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement
async pull(): Promise {
if (!this.isEnabled()) {
- this.logService.info('Settings: Skipped pulling settings as it is disabled.');
+ this.logService.info(`${this.syncResourceLogLabel}: Skipped pulling settings as it is disabled.`);
return;
}
this.stop();
try {
- this.logService.info('Settings: Started pulling settings...');
+ this.logService.info(`${this.syncResourceLogLabel}: Started pulling settings...`);
this.setStatus(SyncStatus.Syncing);
const lastSyncUserData = await this.getLastSyncUserData();
@@ -113,10 +113,10 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement
// No remote exists to pull
else {
- this.logService.info('Settings: Remote settings does not exist.');
+ this.logService.info(`${this.syncResourceLogLabel}: Remote settings does not exist.`);
}
- this.logService.info('Settings: Finished pulling settings.');
+ this.logService.info(`${this.syncResourceLogLabel}: Finished pulling settings.`);
} finally {
this.setStatus(SyncStatus.Idle);
}
@@ -124,14 +124,14 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement
async push(): Promise {
if (!this.isEnabled()) {
- this.logService.info('Settings: Skipped pushing settings as it is disabled.');
+ this.logService.info(`${this.syncResourceLogLabel}: Skipped pushing settings as it is disabled.`);
return;
}
this.stop();
try {
- this.logService.info('Settings: Started pushing settings...');
+ this.logService.info(`${this.syncResourceLogLabel}: Started pushing settings...`);
this.setStatus(SyncStatus.Syncing);
const fileContent = await this.getLocalFileContent();
@@ -159,10 +159,10 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement
// No local exists to push
else {
- this.logService.info('Settings: Local settings does not exist.');
+ this.logService.info(`${this.syncResourceLogLabel}: Local settings does not exist.`);
}
- this.logService.info('Settings: Finished pushing settings.');
+ this.logService.info(`${this.syncResourceLogLabel}: Finished pushing settings.`);
} finally {
this.setStatus(SyncStatus.Idle);
}
@@ -268,7 +268,7 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement
switch (e.code) {
case UserDataSyncErrorCode.LocalPreconditionFailed:
// Rejected as there is a new local version. Syncing again.
- this.logService.info('Settings: Failed to synchronize settings as there is a new local version available. Synchronizing again...');
+ this.logService.info(`${this.syncResourceLogLabel}: Failed to synchronize settings as there is a new local version available. Synchronizing again...`);
return this.performSync(remoteUserData, lastSyncUserData, resolvedConflicts);
}
}
@@ -288,10 +288,10 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement
this.validateContent(content);
if (hasLocalChanged) {
- this.logService.trace('Settings: Updating local settings...');
+ this.logService.trace(`${this.syncResourceLogLabel}: Updating local settings...`);
await this.backupLocal(JSON.stringify(this.toSettingsSyncContent(content)));
await this.updateLocalFileContent(content, fileContent);
- this.logService.info('Settings: Updated local settings');
+ this.logService.info(`${this.syncResourceLogLabel}: Updated local settings`);
}
if (hasRemoteChanged) {
const formatUtils = await this.getFormattingOptions();
@@ -299,9 +299,9 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement
const remoteSettingsSyncContent = this.getSettingsSyncContent(remoteUserData);
const ignoredSettings = await this.getIgnoredSettings(content);
content = updateIgnoredSettings(content, remoteSettingsSyncContent ? remoteSettingsSyncContent.settings : '{}', ignoredSettings, formatUtils);
- this.logService.trace('Settings: Updating remote settings...');
+ this.logService.trace(`${this.syncResourceLogLabel}: Updating remote settings...`);
remoteUserData = await this.updateRemoteUserData(JSON.stringify(this.toSettingsSyncContent(content)), forcePush ? null : remoteUserData.ref);
- this.logService.info('Settings: Updated remote settings');
+ this.logService.info(`${this.syncResourceLogLabel}: Updated remote settings`);
}
// Delete the preview
@@ -309,13 +309,13 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement
await this.fileService.del(this.conflictsPreviewResource);
} catch (e) { /* ignore */ }
} else {
- this.logService.info('Settings: No changes found during synchronizing settings.');
+ this.logService.info(`${this.syncResourceLogLabel}: No changes found during synchronizing settings.`);
}
if (lastSyncUserData?.ref !== remoteUserData.ref) {
- this.logService.trace('Settings: Updating last synchronized settings...');
+ this.logService.trace(`${this.syncResourceLogLabel}: Updating last synchronized settings...`);
await this.updateLastSyncUserData(remoteUserData);
- this.logService.info('Settings: Updated last synchronized settings');
+ this.logService.info(`${this.syncResourceLogLabel}: Updated last synchronized settings`);
}
this.syncPreviewResultPromise = null;
@@ -343,7 +343,7 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement
if (remoteSettingsSyncContent) {
const localContent: string = fileContent ? fileContent.value.toString() : '{}';
this.validateContent(localContent);
- this.logService.trace('Settings: Merging remote settings with local settings...');
+ this.logService.trace(`${this.syncResourceLogLabel}: Merging remote settings with local settings...`);
const ignoredSettings = await this.getIgnoredSettings();
const result = merge(localContent, remoteSettingsSyncContent.settings, lastSettingsSyncContent ? lastSettingsSyncContent.settings : null, ignoredSettings, resolvedConflicts, formattingOptions);
content = result.localContent || result.remoteContent;
@@ -355,7 +355,7 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement
// First time syncing to remote
else if (fileContent) {
- this.logService.trace('Settings: Remote settings does not exist. Synchronizing settings for the first time.');
+ this.logService.trace(`${this.syncResourceLogLabel}: Remote settings does not exist. Synchronizing settings for the first time.`);
content = fileContent.value.toString();
hasRemoteChanged = true;
}
@@ -406,7 +406,7 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement
private validateContent(content: string): void {
if (this.hasErrors(content)) {
- throw new UserDataSyncError(localize('errorInvalidSettings', "Unable to sync settings as there are errors/warning in settings file."), UserDataSyncErrorCode.LocalInvalidContent, this.source);
+ throw new UserDataSyncError(localize('errorInvalidSettings', "Unable to sync settings as there are errors/warning in settings file."), UserDataSyncErrorCode.LocalInvalidContent, this.resource);
}
}
}
diff --git a/src/vs/platform/userDataSync/common/userDataSync.ts b/src/vs/platform/userDataSync/common/userDataSync.ts
index 0744514d2f5..260ca1554d8 100644
--- a/src/vs/platform/userDataSync/common/userDataSync.ts
+++ b/src/vs/platform/userDataSync/common/userDataSync.ts
@@ -135,11 +135,16 @@ export function getUserDataSyncStore(productService: IProductService, configurat
return undefined;
}
-export const ALL_RESOURCE_KEYS: ResourceKey[] = ['settings', 'keybindings', 'extensions', 'globalState'];
-export type ResourceKey = 'settings' | 'keybindings' | 'extensions' | 'globalState';
+export const enum SyncResource {
+ Settings = 'settings',
+ Keybindings = 'keybindings',
+ Extensions = 'extensions',
+ GlobalState = 'globalState'
+}
+export const ALL_SYNC_RESOURCES: SyncResource[] = [SyncResource.Settings, SyncResource.Keybindings, SyncResource.Extensions, SyncResource.GlobalState];
export interface IUserDataManifest {
- latest?: Record
+ latest?: Record
session: string;
}
@@ -152,21 +157,21 @@ export const IUserDataSyncStoreService = createDecorator;
- write(key: ResourceKey, content: string, ref: string | null, source?: SyncSource): Promise;
+ read(resource: SyncResource, oldValue: IUserData | null): Promise;
+ write(resource: SyncResource, content: string, ref: string | null): Promise;
manifest(): Promise;
clear(): Promise;
- getAllRefs(key: ResourceKey): Promise;
- resolveContent(key: ResourceKey, ref: string): Promise;
- delete(key: ResourceKey): Promise;
+ getAllRefs(resource: SyncResource): Promise;
+ resolveContent(resource: SyncResource, ref: string): Promise;
+ delete(resource: SyncResource): Promise;
}
export const IUserDataSyncBackupStoreService = createDecorator('IUserDataSyncBackupStoreService');
export interface IUserDataSyncBackupStoreService {
_serviceBrand: undefined;
- backup(resourceKey: ResourceKey, content: string): Promise;
- getAllRefs(key: ResourceKey): Promise;
- resolveContent(key: ResourceKey, ref?: string): Promise;
+ backup(resource: SyncResource, content: string): Promise;
+ getAllRefs(resource: SyncResource): Promise;
+ resolveContent(resource: SyncResource, ref?: string): Promise;
}
//#endregion
@@ -195,9 +200,9 @@ export enum UserDataSyncErrorCode {
export class UserDataSyncError extends Error {
- constructor(message: string, public readonly code: UserDataSyncErrorCode, public readonly source?: SyncSource) {
+ constructor(message: string, public readonly code: UserDataSyncErrorCode, public readonly resource?: SyncResource) {
super(message);
- this.name = `${this.code} (UserDataSyncError) ${this.source}`;
+ this.name = `${this.code} (UserDataSyncError) ${this.resource}`;
}
static toUserDataSyncError(error: Error): UserDataSyncError {
@@ -206,7 +211,7 @@ export class UserDataSyncError extends Error {
}
const match = /^(.+) \(UserDataSyncError\) (.+)?$/.exec(error.name);
if (match && match[1]) {
- return new UserDataSyncError(error.message, match[1], match[2]);
+ return new UserDataSyncError(error.message, match[1], match[2]);
}
return new UserDataSyncError(error.message, UserDataSyncErrorCode.Unknown);
}
@@ -230,13 +235,6 @@ export interface IGlobalState {
storage: IStringDictionary;
}
-export const enum SyncSource {
- Settings = 'Settings',
- Keybindings = 'Keybindings',
- Extensions = 'Extensions',
- GlobalState = 'GlobalState'
-}
-
export const enum SyncStatus {
Uninitialized = 'uninitialized',
Idle = 'idle',
@@ -246,8 +244,7 @@ export const enum SyncStatus {
export interface IUserDataSynchroniser {
- readonly resourceKey: ResourceKey;
- readonly source: SyncSource;
+ readonly resource: SyncResource;
readonly status: SyncStatus;
readonly onDidChangeStatus: Event;
readonly onDidChangeLocal: Event;
@@ -276,13 +273,13 @@ export interface IUserDataSyncEnablementService {
_serviceBrand: any;
readonly onDidChangeEnablement: Event;
- readonly onDidChangeResourceEnablement: Event<[ResourceKey, boolean]>;
+ readonly onDidChangeResourceEnablement: Event<[SyncResource, boolean]>;
isEnabled(): boolean;
setEnablement(enabled: boolean): void;
- isResourceEnabled(key: ResourceKey): boolean;
- setResourceEnablement(key: ResourceKey, enabled: boolean): void;
+ isResourceEnabled(resource: SyncResource): boolean;
+ setResourceEnablement(resource: SyncResource, enabled: boolean): void;
}
export const IUserDataSyncService = createDecorator('IUserDataSyncService');
@@ -292,11 +289,11 @@ export interface IUserDataSyncService {
readonly status: SyncStatus;
readonly onDidChangeStatus: Event;
- readonly conflictsSources: SyncSource[];
- readonly onDidChangeConflicts: Event;
+ readonly conflictsSources: SyncResource[];
+ readonly onDidChangeConflicts: Event;
- readonly onDidChangeLocal: Event;
- readonly onSyncErrors: Event<[SyncSource, UserDataSyncError][]>;
+ readonly onDidChangeLocal: Event;
+ readonly onSyncErrors: Event<[SyncResource, UserDataSyncError][]>;
readonly lastSyncTime: number | undefined;
readonly onDidChangeLastSyncTime: Event;
@@ -309,7 +306,7 @@ export interface IUserDataSyncService {
isFirstTimeSyncWithMerge(): Promise;
resolveContent(resource: URI): Promise;
- accept(source: SyncSource, content: string): Promise;
+ accept(source: SyncResource, content: string): Promise;
}
export const IUserDataAutoSyncService = createDecorator('IUserDataAutoSyncService');
@@ -336,14 +333,6 @@ export interface IConflictSetting {
remoteValue: any | undefined;
}
-export const ISettingsSyncService = createDecorator('ISettingsSyncService');
-export interface ISettingsSyncService extends IUserDataSynchroniser {
- _serviceBrand: any;
- readonly onDidChangeConflicts: Event;
- readonly conflicts: IConflictSetting[];
- resolveSettingsConflicts(resolvedConflicts: { key: string, value: any | undefined }[]): Promise;
-}
-
//#endregion
export const CONTEXT_SYNC_STATE = new RawContextKey('syncStatus', SyncStatus.Uninitialized);
@@ -351,50 +340,31 @@ export const CONTEXT_SYNC_ENABLEMENT = new RawContextKey('syncEnabled',
export const USER_DATA_SYNC_SCHEME = 'vscode-userdata-sync';
export const PREVIEW_QUERY = 'preview=true';
-export function toRemoteSyncResourceFromSource(source: SyncSource, ref?: string): URI {
- return toRemoteSyncResource(getResourceKeyFromSyncSource(source), ref);
+export function toRemoteSyncResource(resource: SyncResource, ref?: string): URI {
+ return URI.from({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote', path: `/${resource}/${ref ? ref : 'latest'}` });
}
-export function toRemoteSyncResource(resourceKey: ResourceKey, ref?: string): URI {
- return URI.from({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote', path: `/${resourceKey}/${ref ? ref : 'latest'}` });
-}
-export function toLocalBackupSyncResource(resourceKey: ResourceKey, ref?: string): URI {
- return URI.from({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local-backup', path: `/${resourceKey}/${ref ? ref : 'latest'}` });
+export function toLocalBackupSyncResource(resource: SyncResource, ref?: string): URI {
+ return URI.from({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local-backup', path: `/${resource}/${ref ? ref : 'latest'}` });
}
-export function resolveSyncResource(resource: URI): { remote: boolean, resourceKey: ResourceKey, ref?: string } | null {
- const remote = resource.authority === 'remote';
- const resourceKey: ResourceKey = basename(dirname(resource)) as ResourceKey;
- const ref = basename(resource);
- if (resourceKey && ref) {
- return { remote, resourceKey, ref: ref !== 'latest' ? ref : undefined };
+export function resolveSyncResource(resource: URI): { remote: boolean, resource: SyncResource, ref?: string } | null {
+ if (resource.scheme === USER_DATA_SYNC_SCHEME) {
+ const remote = resource.authority === 'remote';
+ const resourceKey: SyncResource = basename(dirname(resource)) as SyncResource;
+ const ref = basename(resource);
+ if (resourceKey && ref) {
+ return { remote, resource: resourceKey, ref: ref !== 'latest' ? ref : undefined };
+ }
}
return null;
}
-export function getSyncSourceFromPreviewResource(uri: URI, environmentService: IEnvironmentService): SyncSource | undefined {
+export function getSyncSourceFromPreviewResource(uri: URI, environmentService: IEnvironmentService): SyncResource | undefined {
if (isEqual(uri, environmentService.settingsSyncPreviewResource)) {
- return SyncSource.Settings;
+ return SyncResource.Settings;
}
if (isEqual(uri, environmentService.keybindingsSyncPreviewResource)) {
- return SyncSource.Keybindings;
+ return SyncResource.Keybindings;
}
return undefined;
}
-
-export function getResourceKeyFromSyncSource(source: SyncSource): ResourceKey {
- switch (source) {
- case SyncSource.Settings: return 'settings';
- case SyncSource.Keybindings: return 'keybindings';
- case SyncSource.Extensions: return 'extensions';
- case SyncSource.GlobalState: return 'globalState';
- }
-}
-
-export function getSyncSourceFromResourceKey(resourceKey: ResourceKey): SyncSource {
- switch (resourceKey) {
- case 'settings': return SyncSource.Settings;
- case 'keybindings': return SyncSource.Keybindings;
- case 'extensions': return SyncSource.Extensions;
- case 'globalState': return SyncSource.GlobalState;
- }
-}
diff --git a/src/vs/platform/userDataSync/common/userDataSyncBackupStoreService.ts b/src/vs/platform/userDataSync/common/userDataSyncBackupStoreService.ts
index b439de0bc02..8f9333c9600 100644
--- a/src/vs/platform/userDataSync/common/userDataSyncBackupStoreService.ts
+++ b/src/vs/platform/userDataSync/common/userDataSyncBackupStoreService.ts
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { Disposable, } from 'vs/base/common/lifecycle';
-import { IUserDataSyncLogService, ResourceKey, ALL_RESOURCE_KEYS, IUserDataSyncBackupStoreService, IResourceRefHandle } from 'vs/platform/userDataSync/common/userDataSync';
+import { IUserDataSyncLogService, ALL_SYNC_RESOURCES, IUserDataSyncBackupStoreService, IResourceRefHandle, SyncResource } from 'vs/platform/userDataSync/common/userDataSync';
import { joinPath } from 'vs/base/common/resources';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IFileService, IFileStat } from 'vs/platform/files/common/files';
@@ -23,11 +23,11 @@ export class UserDataSyncBackupStoreService extends Disposable implements IUserD
@IUserDataSyncLogService private readonly logService: IUserDataSyncLogService,
) {
super();
- ALL_RESOURCE_KEYS.forEach(resourceKey => this.cleanUpBackup(resourceKey));
+ ALL_SYNC_RESOURCES.forEach(resourceKey => this.cleanUpBackup(resourceKey));
}
- async getAllRefs(resourceKey: ResourceKey): Promise {
- const folder = joinPath(this.environmentService.userDataSyncHome, resourceKey);
+ async getAllRefs(resource: SyncResource): Promise {
+ const folder = joinPath(this.environmentService.userDataSyncHome, resource);
const stat = await this.fileService.resolve(folder);
if (stat.children) {
const all = stat.children.filter(stat => stat.isFile && /^\d{8}T\d{6}(\.json)?$/.test(stat.name)).sort().reverse();
@@ -39,22 +39,22 @@ export class UserDataSyncBackupStoreService extends Disposable implements IUserD
return [];
}
- async resolveContent(resourceKey: ResourceKey, ref?: string): Promise {
+ async resolveContent(resource: SyncResource, ref?: string): Promise {
if (!ref) {
- const refs = await this.getAllRefs(resourceKey);
+ const refs = await this.getAllRefs(resource);
if (refs.length) {
ref = refs[refs.length - 1].ref;
}
}
if (ref) {
- const file = joinPath(this.environmentService.userDataSyncHome, resourceKey, ref);
+ const file = joinPath(this.environmentService.userDataSyncHome, resource, ref);
const content = await this.fileService.readFile(file);
return content.value.toString();
}
return null;
}
- async backup(resourceKey: ResourceKey, content: string): Promise {
+ async backup(resourceKey: SyncResource, content: string): Promise {
const folder = joinPath(this.environmentService.userDataSyncHome, resourceKey);
const resource = joinPath(folder, `${toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')}.json`);
try {
@@ -67,8 +67,8 @@ export class UserDataSyncBackupStoreService extends Disposable implements IUserD
} catch (e) { /* Ignore */ }
}
- private async cleanUpBackup(resourceKey: ResourceKey): Promise {
- const folder = joinPath(this.environmentService.userDataSyncHome, resourceKey);
+ private async cleanUpBackup(resource: SyncResource): Promise {
+ const folder = joinPath(this.environmentService.userDataSyncHome, resource);
try {
try {
if (!(await this.fileService.exists(folder))) {
diff --git a/src/vs/platform/userDataSync/common/userDataSyncEnablementService.ts b/src/vs/platform/userDataSync/common/userDataSyncEnablementService.ts
index 7adbf97471b..250dcbe66eb 100644
--- a/src/vs/platform/userDataSync/common/userDataSyncEnablementService.ts
+++ b/src/vs/platform/userDataSync/common/userDataSyncEnablementService.ts
@@ -3,18 +3,19 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import { IUserDataSyncEnablementService, ResourceKey, ALL_RESOURCE_KEYS } from 'vs/platform/userDataSync/common/userDataSync';
+import { IUserDataSyncEnablementService, ALL_SYNC_RESOURCES, SyncResource } from 'vs/platform/userDataSync/common/userDataSync';
import { Disposable } from 'vs/base/common/lifecycle';
import { Emitter, Event } from 'vs/base/common/event';
import { IStorageService, IWorkspaceStorageChangeEvent, StorageScope } from 'vs/platform/storage/common/storage';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
+import { IEnvironmentService } from 'vs/platform/environment/common/environment';
type SyncEnablementClassification = {
enabled?: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true };
};
const enablementKey = 'sync.enable';
-function getEnablementKey(resourceKey: ResourceKey) { return `${enablementKey}.${resourceKey}`; }
+function getEnablementKey(resource: SyncResource) { return `${enablementKey}.${resource}`; }
export class UserDataSyncEnablementService extends Disposable implements IUserDataSyncEnablementService {
@@ -23,14 +24,23 @@ export class UserDataSyncEnablementService extends Disposable implements IUserDa
private _onDidChangeEnablement = new Emitter();
readonly onDidChangeEnablement: Event = this._onDidChangeEnablement.event;
- private _onDidChangeResourceEnablement = new Emitter<[ResourceKey, boolean]>();
- readonly onDidChangeResourceEnablement: Event<[ResourceKey, boolean]> = this._onDidChangeResourceEnablement.event;
+ private _onDidChangeResourceEnablement = new Emitter<[SyncResource, boolean]>();
+ readonly onDidChangeResourceEnablement: Event<[SyncResource, boolean]> = this._onDidChangeResourceEnablement.event;
constructor(
@IStorageService private readonly storageService: IStorageService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
+ @IEnvironmentService environmentService: IEnvironmentService,
) {
super();
+ switch (environmentService.args['sync']) {
+ case 'on':
+ this.setEnablement(true);
+ break;
+ case 'off':
+ this.setEnablement(false);
+ break;
+ }
this._register(storageService.onDidChangeStorage(e => this.onDidStorageChange(e)));
}
@@ -45,13 +55,13 @@ export class UserDataSyncEnablementService extends Disposable implements IUserDa
}
}
- isResourceEnabled(resourceKey: ResourceKey): boolean {
- return this.storageService.getBoolean(getEnablementKey(resourceKey), StorageScope.GLOBAL, true);
+ isResourceEnabled(resource: SyncResource): boolean {
+ return this.storageService.getBoolean(getEnablementKey(resource), StorageScope.GLOBAL, true);
}
- setResourceEnablement(resourceKey: ResourceKey, enabled: boolean): void {
- if (this.isResourceEnabled(resourceKey) !== enabled) {
- const resourceEnablementKey = getEnablementKey(resourceKey);
+ setResourceEnablement(resource: SyncResource, enabled: boolean): void {
+ if (this.isResourceEnabled(resource) !== enabled) {
+ const resourceEnablementKey = getEnablementKey(resource);
this.telemetryService.publicLog2<{ enabled: boolean }, SyncEnablementClassification>(resourceEnablementKey, { enabled });
this.storageService.store(resourceEnablementKey, enabled, StorageScope.GLOBAL);
}
@@ -63,7 +73,7 @@ export class UserDataSyncEnablementService extends Disposable implements IUserDa
this._onDidChangeEnablement.fire(this.isEnabled());
return;
}
- const resourceKey = ALL_RESOURCE_KEYS.filter(resourceKey => getEnablementKey(resourceKey) === workspaceStorageChangeEvent.key)[0];
+ const resourceKey = ALL_SYNC_RESOURCES.filter(resourceKey => getEnablementKey(resourceKey) === workspaceStorageChangeEvent.key)[0];
if (resourceKey) {
this._onDidChangeResourceEnablement.fire([resourceKey, this.isEnabled()]);
return;
diff --git a/src/vs/platform/userDataSync/common/userDataSyncIpc.ts b/src/vs/platform/userDataSync/common/userDataSyncIpc.ts
index de2736a2fe3..6b8ef2b6710 100644
--- a/src/vs/platform/userDataSync/common/userDataSyncIpc.ts
+++ b/src/vs/platform/userDataSync/common/userDataSyncIpc.ts
@@ -5,7 +5,7 @@
import { IServerChannel, IChannel } from 'vs/base/parts/ipc/common/ipc';
import { Event } from 'vs/base/common/event';
-import { IUserDataSyncService, IUserDataSyncUtilService, ISettingsSyncService, IUserDataAutoSyncService, IUserDataSyncStoreService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync';
+import { IUserDataSyncService, IUserDataSyncUtilService, IUserDataAutoSyncService, IUserDataSyncStoreService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync';
import { URI } from 'vs/base/common/uri';
import { IStringDictionary } from 'vs/base/common/collections';
import { FormattingOptions } from 'vs/base/common/jsonFormatter';
@@ -41,40 +41,6 @@ export class UserDataSyncChannel implements IServerChannel {
}
}
-export class SettingsSyncChannel implements IServerChannel {
-
- constructor(private readonly service: ISettingsSyncService) { }
-
- listen(_: unknown, event: string): Event {
- switch (event) {
- case 'onDidChangeStatus': return this.service.onDidChangeStatus;
- case 'onDidChangeLocal': return this.service.onDidChangeLocal;
- case 'onDidChangeConflicts': return this.service.onDidChangeConflicts;
- }
- throw new Error(`Event not found: ${event}`);
- }
-
- call(context: any, command: string, args?: any): Promise {
- switch (command) {
- case 'sync': return this.service.sync();
- case 'accept': return this.service.accept(args[0]);
- case 'pull': return this.service.pull();
- case 'push': return this.service.push();
- case '_getInitialStatus': return Promise.resolve(this.service.status);
- case '_getInitialConflicts': return Promise.resolve(this.service.conflicts);
- case 'stop': this.service.stop(); return Promise.resolve();
- case 'resetLocal': return this.service.resetLocal();
- case 'hasPreviouslySynced': return this.service.hasPreviouslySynced();
- case 'hasLocalData': return this.service.hasLocalData();
- case 'resolveSettingsConflicts': return this.service.resolveSettingsConflicts(args[0]);
- case 'getRemoteContentFromPreview': return this.service.getRemoteContentFromPreview();
- case 'getRemoteContent': return this.service.getRemoteContent(args[0], args[1]);
- case 'getLocalBackupContent': return this.service.getLocalBackupContent(args[0], args[1]);
- }
- throw new Error('Invalid call');
- }
-}
-
export class UserDataAutoSyncChannel implements IServerChannel {
constructor(private readonly service: IUserDataAutoSyncService) { }
diff --git a/src/vs/platform/userDataSync/common/userDataSyncService.ts b/src/vs/platform/userDataSync/common/userDataSyncService.ts
index 4be50d8387a..fd8d3a93cea 100644
--- a/src/vs/platform/userDataSync/common/userDataSyncService.ts
+++ b/src/vs/platform/userDataSync/common/userDataSyncService.ts
@@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import { IUserDataSyncService, SyncStatus, IUserDataSyncStoreService, SyncSource, ISettingsSyncService, IUserDataSyncLogService, IUserDataSynchroniser, UserDataSyncStoreError, UserDataSyncErrorCode, UserDataSyncError, resolveSyncResource, PREVIEW_QUERY } from 'vs/platform/userDataSync/common/userDataSync';
+import { IUserDataSyncService, SyncStatus, IUserDataSyncStoreService, SyncResource, IUserDataSyncLogService, IUserDataSynchroniser, UserDataSyncStoreError, UserDataSyncErrorCode, UserDataSyncError, resolveSyncResource, PREVIEW_QUERY } from 'vs/platform/userDataSync/common/userDataSync';
import { Disposable } from 'vs/base/common/lifecycle';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { Emitter, Event } from 'vs/base/common/event';
@@ -16,6 +16,7 @@ import { equals } from 'vs/base/common/arrays';
import { localize } from 'vs/nls';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { URI } from 'vs/base/common/uri';
+import { SettingsSynchroniser } from 'vs/platform/userDataSync/common/settingsSync';
type SyncErrorClassification = {
source: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true };
@@ -35,22 +36,23 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
private _onDidChangeStatus: Emitter = this._register(new Emitter());
readonly onDidChangeStatus: Event = this._onDidChangeStatus.event;
- readonly onDidChangeLocal: Event;
+ readonly onDidChangeLocal: Event;
- private _conflictsSources: SyncSource[] = [];
- get conflictsSources(): SyncSource[] { return this._conflictsSources; }
- private _onDidChangeConflicts: Emitter = this._register(new Emitter());
- readonly onDidChangeConflicts: Event = this._onDidChangeConflicts.event;
+ private _conflictsSources: SyncResource[] = [];
+ get conflictsSources(): SyncResource[] { return this._conflictsSources; }
+ private _onDidChangeConflicts: Emitter = this._register(new Emitter());
+ readonly onDidChangeConflicts: Event = this._onDidChangeConflicts.event;
- private _syncErrors: [SyncSource, UserDataSyncError][] = [];
- private _onSyncErrors: Emitter<[SyncSource, UserDataSyncError][]> = this._register(new Emitter<[SyncSource, UserDataSyncError][]>());
- readonly onSyncErrors: Event<[SyncSource, UserDataSyncError][]> = this._onSyncErrors.event;
+ private _syncErrors: [SyncResource, UserDataSyncError][] = [];
+ private _onSyncErrors: Emitter<[SyncResource, UserDataSyncError][]> = this._register(new Emitter<[SyncResource, UserDataSyncError][]>());
+ readonly onSyncErrors: Event<[SyncResource, UserDataSyncError][]> = this._onSyncErrors.event;
private _lastSyncTime: number | undefined = undefined;
get lastSyncTime(): number | undefined { return this._lastSyncTime; }
private _onDidChangeLastSyncTime: Emitter = this._register(new Emitter());
readonly onDidChangeLastSyncTime: Event = this._onDidChangeLastSyncTime.event;
+ private readonly settingsSynchroniser: SettingsSynchroniser;
private readonly keybindingsSynchroniser: KeybindingsSynchroniser;
private readonly extensionsSynchroniser: ExtensionsSynchroniser;
private readonly globalStateSynchroniser: GlobalStateSynchroniser;
@@ -58,12 +60,12 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
constructor(
@IUserDataSyncStoreService private readonly userDataSyncStoreService: IUserDataSyncStoreService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
- @ISettingsSyncService private readonly settingsSynchroniser: ISettingsSyncService,
@IUserDataSyncLogService private readonly logService: IUserDataSyncLogService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
@IStorageService private readonly storageService: IStorageService,
) {
super();
+ this.settingsSynchroniser = this._register(this.instantiationService.createInstance(SettingsSynchroniser));
this.keybindingsSynchroniser = this._register(this.instantiationService.createInstance(KeybindingsSynchroniser));
this.globalStateSynchroniser = this._register(this.instantiationService.createInstance(GlobalStateSynchroniser));
this.extensionsSynchroniser = this._register(this.instantiationService.createInstance(ExtensionsSynchroniser));
@@ -75,7 +77,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
}
this._lastSyncTime = this.storageService.getNumber(LAST_SYNC_TIME_KEY, StorageScope.GLOBAL, undefined);
- this.onDidChangeLocal = Event.any(...this.synchronisers.map(s => Event.map(s.onDidChangeLocal, () => s.source)));
+ this.onDidChangeLocal = Event.any(...this.synchronisers.map(s => Event.map(s.onDidChangeLocal, () => s.resource)));
}
async pull(): Promise {
@@ -84,7 +86,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
try {
await synchroniser.pull();
} catch (e) {
- this.handleSyncError(e, synchroniser.source);
+ this.handleSyncError(e, synchroniser.resource);
}
}
this.updateLastSyncTime();
@@ -96,7 +98,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
try {
await synchroniser.push();
} catch (e) {
- this.handleSyncError(e, synchroniser.source);
+ this.handleSyncError(e, synchroniser.resource);
}
}
this.updateLastSyncTime();
@@ -129,10 +131,10 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
for (const synchroniser of this.synchronisers) {
try {
- await synchroniser.sync(manifest && manifest.latest ? manifest.latest[synchroniser.resourceKey] : undefined);
+ await synchroniser.sync(manifest && manifest.latest ? manifest.latest[synchroniser.resource] : undefined);
} catch (e) {
- this.handleSyncError(e, synchroniser.source);
- this._syncErrors.push([synchroniser.source, UserDataSyncError.toUserDataSyncError(e)]);
+ this.handleSyncError(e, synchroniser.resource);
+ this._syncErrors.push([synchroniser.resource, UserDataSyncError.toUserDataSyncError(e)]);
}
}
@@ -171,7 +173,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
}
}
- async accept(source: SyncSource, content: string): Promise {
+ async accept(source: SyncResource, content: string): Promise {
await this.checkEnablement();
const synchroniser = this.getSynchroniser(source);
await synchroniser.accept(content);
@@ -180,7 +182,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
async resolveContent(resource: URI): Promise {
const result = resolveSyncResource(resource);
if (result) {
- const synchronizer = this.synchronisers.filter(s => s.resourceKey === result.resourceKey)[0];
+ const synchronizer = this.synchronisers.filter(s => s.resource === result.resource)[0];
if (synchronizer) {
if (PREVIEW_QUERY === resource.query) {
return result.remote ? synchronizer.getRemoteContentFromPreview() : null;
@@ -216,7 +218,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
try {
synchroniser.resetLocal();
} catch (e) {
- this.logService.error(`${synchroniser.source}: ${toErrorMessage(e)}`);
+ this.logService.error(`${synchroniser.resource}: ${toErrorMessage(e)}`);
this.logService.error(e);
}
}
@@ -291,7 +293,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
}
}
- private handleSyncError(e: Error, source: SyncSource): void {
+ private handleSyncError(e: Error, source: SyncResource): void {
if (e instanceof UserDataSyncStoreError) {
switch (e.code) {
case UserDataSyncErrorCode.TooLarge:
@@ -303,12 +305,12 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
this.logService.error(`${source}: ${toErrorMessage(e)}`);
}
- private computeConflictsSources(): SyncSource[] {
- return this.synchronisers.filter(s => s.status === SyncStatus.HasConflicts).map(s => s.source);
+ private computeConflictsSources(): SyncResource[] {
+ return this.synchronisers.filter(s => s.status === SyncStatus.HasConflicts).map(s => s.resource);
}
- getSynchroniser(source: SyncSource): IUserDataSynchroniser {
- return this.synchronisers.filter(s => s.source === source)[0];
+ getSynchroniser(source: SyncResource): IUserDataSynchroniser {
+ return this.synchronisers.filter(s => s.resource === source)[0];
}
private async checkEnablement(): Promise {
diff --git a/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts
index 091170c7abc..344fca00b30 100644
--- a/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts
+++ b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { Disposable, } from 'vs/base/common/lifecycle';
-import { IUserData, IUserDataSyncStoreService, UserDataSyncErrorCode, IUserDataSyncStore, getUserDataSyncStore, SyncSource, UserDataSyncStoreError, IUserDataSyncLogService, IUserDataManifest, ResourceKey, IResourceRefHandle } from 'vs/platform/userDataSync/common/userDataSync';
+import { IUserData, IUserDataSyncStoreService, UserDataSyncErrorCode, IUserDataSyncStore, getUserDataSyncStore, SyncResource, UserDataSyncStoreError, IUserDataSyncLogService, IUserDataManifest, IResourceRefHandle } from 'vs/platform/userDataSync/common/userDataSync';
import { IRequestService, asText, isSuccess, asJson } from 'vs/platform/request/common/request';
import { joinPath, relativePath } from 'vs/base/common/resources';
import { CancellationToken } from 'vs/base/common/cancellation';
@@ -13,12 +13,19 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { IAuthenticationTokenService } from 'vs/platform/authentication/common/authentication';
import { IProductService } from 'vs/platform/product/common/productService';
import { URI } from 'vs/base/common/uri';
+import { getServiceMachineId } from 'vs/platform/serviceMachineId/common/serviceMachineId';
+import { IEnvironmentService } from 'vs/platform/environment/common/environment';
+import { IFileService } from 'vs/platform/files/common/files';
+import { IStorageService } from 'vs/platform/storage/common/storage';
+import { assign } from 'vs/base/common/objects';
+
export class UserDataSyncStoreService extends Disposable implements IUserDataSyncStoreService {
_serviceBrand: any;
readonly userDataSyncStore: IUserDataSyncStore | undefined;
+ private readonly commonHeadersPromise: Promise<{ [key: string]: string; }>;
constructor(
@IProductService productService: IProductService,
@@ -26,17 +33,25 @@ export class UserDataSyncStoreService extends Disposable implements IUserDataSyn
@IRequestService private readonly requestService: IRequestService,
@IAuthenticationTokenService private readonly authTokenService: IAuthenticationTokenService,
@IUserDataSyncLogService private readonly logService: IUserDataSyncLogService,
+ @IEnvironmentService environmentService: IEnvironmentService,
+ @IFileService fileService: IFileService,
+ @IStorageService storageService: IStorageService,
) {
super();
this.userDataSyncStore = getUserDataSyncStore(productService, configurationService);
+ this.commonHeadersPromise = getServiceMachineId(environmentService, fileService, storageService)
+ .then(uuid => ({
+ 'X-Sync-Client-Id': productService.version,
+ 'X-Sync-Machine-Id': uuid
+ }));
}
- async getAllRefs(key: ResourceKey): Promise {
+ async getAllRefs(resource: SyncResource): Promise {
if (!this.userDataSyncStore) {
throw new Error('No settings sync store url configured.');
}
- const uri = joinPath(this.userDataSyncStore.url, 'resource', key);
+ const uri = joinPath(this.userDataSyncStore.url, 'resource', resource);
const headers: IHeaders = {};
const context = await this.request({ type: 'GET', url: uri.toString(), headers }, undefined, CancellationToken.None);
@@ -46,15 +61,15 @@ export class UserDataSyncStoreService extends Disposable implements IUserDataSyn
}
const result = await asJson<{ url: string, created: number }[]>(context) || [];
- return result.map(({ url, created }) => ({ ref: relativePath(uri, URI.parse(url))!, created: created }));
+ return result.map(({ url, created }) => ({ ref: relativePath(uri, URI.parse(url))!, created: created * 1000 /* Server returns in seconds */ }));
}
- async resolveContent(key: ResourceKey, ref: string): Promise {
+ async resolveContent(resource: SyncResource, ref: string): Promise {
if (!this.userDataSyncStore) {
throw new Error('No settings sync store url configured.');
}
- const url = joinPath(this.userDataSyncStore.url, 'resource', key, ref).toString();
+ const url = joinPath(this.userDataSyncStore.url, 'resource', resource, ref).toString();
const headers: IHeaders = {};
const context = await this.request({ type: 'GET', url, headers }, undefined, CancellationToken.None);
@@ -67,12 +82,12 @@ export class UserDataSyncStoreService extends Disposable implements IUserDataSyn
return content;
}
- async delete(key: ResourceKey): Promise {
+ async delete(resource: SyncResource): Promise {
if (!this.userDataSyncStore) {
throw new Error('No settings sync store url configured.');
}
- const url = joinPath(this.userDataSyncStore.url, 'resource', key).toString();
+ const url = joinPath(this.userDataSyncStore.url, 'resource', resource).toString();
const headers: IHeaders = {};
const context = await this.request({ type: 'DELETE', url, headers }, undefined, CancellationToken.None);
@@ -82,12 +97,12 @@ export class UserDataSyncStoreService extends Disposable implements IUserDataSyn
}
}
- async read(key: ResourceKey, oldValue: IUserData | null, source?: SyncSource): Promise {
+ async read(resource: SyncResource, oldValue: IUserData | null): Promise {
if (!this.userDataSyncStore) {
throw new Error('No settings sync store url configured.');
}
- const url = joinPath(this.userDataSyncStore.url, 'resource', key, 'latest').toString();
+ const url = joinPath(this.userDataSyncStore.url, 'resource', resource, 'latest').toString();
const headers: IHeaders = {};
// Disable caching as they are cached by synchronisers
headers['Cache-Control'] = 'no-cache';
@@ -95,7 +110,7 @@ export class UserDataSyncStoreService extends Disposable implements IUserDataSyn
headers['If-None-Match'] = oldValue.ref;
}
- const context = await this.request({ type: 'GET', url, headers }, source, CancellationToken.None);
+ const context = await this.request({ type: 'GET', url, headers }, resource, CancellationToken.None);
if (context.res.statusCode === 304) {
// There is no new value. Hence return the old value.
@@ -103,37 +118,37 @@ export class UserDataSyncStoreService extends Disposable implements IUserDataSyn
}
if (!isSuccess(context)) {
- throw new UserDataSyncStoreError('Server returned ' + context.res.statusCode, UserDataSyncErrorCode.Unknown, source);
+ throw new UserDataSyncStoreError('Server returned ' + context.res.statusCode, UserDataSyncErrorCode.Unknown, resource);
}
const ref = context.res.headers['etag'];
if (!ref) {
- throw new UserDataSyncStoreError('Server did not return the ref', UserDataSyncErrorCode.NoRef, source);
+ throw new UserDataSyncStoreError('Server did not return the ref', UserDataSyncErrorCode.NoRef, resource);
}
const content = await asText(context);
return { ref, content };
}
- async write(key: ResourceKey, data: string, ref: string | null, source?: SyncSource): Promise {
+ async write(resource: SyncResource, data: string, ref: string | null): Promise {
if (!this.userDataSyncStore) {
throw new Error('No settings sync store url configured.');
}
- const url = joinPath(this.userDataSyncStore.url, 'resource', key).toString();
+ const url = joinPath(this.userDataSyncStore.url, 'resource', resource).toString();
const headers: IHeaders = { 'Content-Type': 'text/plain' };
if (ref) {
headers['If-Match'] = ref;
}
- const context = await this.request({ type: 'POST', url, data, headers }, source, CancellationToken.None);
+ const context = await this.request({ type: 'POST', url, data, headers }, resource, CancellationToken.None);
if (!isSuccess(context)) {
- throw new UserDataSyncStoreError('Server returned ' + context.res.statusCode, UserDataSyncErrorCode.Unknown, source);
+ throw new UserDataSyncStoreError('Server returned ' + context.res.statusCode, UserDataSyncErrorCode.Unknown, resource);
}
const newRef = context.res.headers['etag'];
if (!newRef) {
- throw new UserDataSyncStoreError('Server did not return the ref', UserDataSyncErrorCode.NoRef, source);
+ throw new UserDataSyncStoreError('Server did not return the ref', UserDataSyncErrorCode.NoRef, resource);
}
return newRef;
}
@@ -169,13 +184,16 @@ export class UserDataSyncStoreService extends Disposable implements IUserDataSyn
}
}
- private async request(options: IRequestOptions, source: SyncSource | undefined, token: CancellationToken): Promise {
+ private async request(options: IRequestOptions, source: SyncResource | undefined, token: CancellationToken): Promise {
const authToken = await this.authTokenService.getToken();
if (!authToken) {
throw new UserDataSyncStoreError('No Auth Token Available', UserDataSyncErrorCode.Unauthorized, source);
}
- options.headers = options.headers || {};
- options.headers['authorization'] = `Bearer ${authToken}`;
+
+ const commonHeaders = await this.commonHeadersPromise;
+ options.headers = assign(options.headers || {}, commonHeaders, {
+ 'authorization': `Bearer ${authToken}`,
+ });
this.logService.trace('Sending request to server', { url: options.url, type: options.type, headers: { ...options.headers, ...{ authorization: undefined } } });
diff --git a/src/vs/platform/userDataSync/test/common/settingsSync.test.ts b/src/vs/platform/userDataSync/test/common/settingsSync.test.ts
index 7c40ae8d70d..c0f87712cf1 100644
--- a/src/vs/platform/userDataSync/test/common/settingsSync.test.ts
+++ b/src/vs/platform/userDataSync/test/common/settingsSync.test.ts
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
-import { IUserDataSyncStoreService, IUserDataSyncService, SyncSource, UserDataSyncError, UserDataSyncErrorCode } from 'vs/platform/userDataSync/common/userDataSync';
+import { IUserDataSyncStoreService, IUserDataSyncService, SyncResource, UserDataSyncError, UserDataSyncErrorCode } from 'vs/platform/userDataSync/common/userDataSync';
import { UserDataSyncClient, UserDataSyncTestServer } from 'vs/platform/userDataSync/test/common/userDataSyncClient';
import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle';
import { SettingsSynchroniser, ISettingsSyncContent } from 'vs/platform/userDataSync/common/settingsSync';
@@ -44,7 +44,7 @@ suite('SettingsSync', () => {
setup(async () => {
client = disposableStore.add(new UserDataSyncClient(server));
await client.setUp();
- testObject = (client.instantiationService.get(IUserDataSyncService) as UserDataSyncService).getSynchroniser(SyncSource.Settings) as SettingsSynchroniser;
+ testObject = (client.instantiationService.get(IUserDataSyncService) as UserDataSyncService).getSynchroniser(SyncResource.Settings) as SettingsSynchroniser;
disposableStore.add(toDisposable(() => client.instantiationService.get(IUserDataSyncStoreService).clear()));
});
@@ -77,7 +77,7 @@ suite('SettingsSync', () => {
await updateSettings(expected);
await testObject.sync();
- const { content } = await client.read(testObject.resourceKey);
+ const { content } = await client.read(testObject.resource);
assert.ok(content !== null);
const actual = parseSettings(content!);
assert.deepEqual(actual, expected);
@@ -101,7 +101,7 @@ suite('SettingsSync', () => {
await testObject.sync();
- const { content } = await client.read(testObject.resourceKey);
+ const { content } = await client.read(testObject.resource);
assert.ok(content !== null);
const actual = parseSettings(content!);
assert.deepEqual(actual, `{
@@ -132,7 +132,7 @@ suite('SettingsSync', () => {
await testObject.sync();
- const { content } = await client.read(testObject.resourceKey);
+ const { content } = await client.read(testObject.resource);
assert.ok(content !== null);
const actual = parseSettings(content!);
assert.deepEqual(actual, `{
@@ -163,7 +163,7 @@ suite('SettingsSync', () => {
await testObject.sync();
- const { content } = await client.read(testObject.resourceKey);
+ const { content } = await client.read(testObject.resource);
assert.ok(content !== null);
const actual = parseSettings(content!);
assert.deepEqual(actual, `{
@@ -187,7 +187,7 @@ suite('SettingsSync', () => {
await testObject.sync();
- const { content } = await client.read(testObject.resourceKey);
+ const { content } = await client.read(testObject.resource);
assert.ok(content !== null);
const actual = parseSettings(content!);
assert.deepEqual(actual, `{
@@ -205,7 +205,7 @@ suite('SettingsSync', () => {
await testObject.sync();
- const { content } = await client.read(testObject.resourceKey);
+ const { content } = await client.read(testObject.resource);
assert.ok(content !== null);
const actual = parseSettings(content!);
assert.deepEqual(actual, `{
@@ -239,7 +239,7 @@ suite('SettingsSync', () => {
await testObject.sync();
- const { content } = await client.read(testObject.resourceKey);
+ const { content } = await client.read(testObject.resource);
assert.ok(content !== null);
const actual = parseSettings(content!);
assert.deepEqual(actual, `{
@@ -287,7 +287,7 @@ suite('SettingsSync', () => {
await testObject.sync();
- const { content } = await client.read(testObject.resourceKey);
+ const { content } = await client.read(testObject.resource);
assert.ok(content !== null);
const actual = parseSettings(content!);
assert.deepEqual(actual, `{
diff --git a/src/vs/platform/userDataSync/test/common/synchronizer.test.ts b/src/vs/platform/userDataSync/test/common/synchronizer.test.ts
index b2fa135d413..d8abc061edf 100644
--- a/src/vs/platform/userDataSync/test/common/synchronizer.test.ts
+++ b/src/vs/platform/userDataSync/test/common/synchronizer.test.ts
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
-import { ResourceKey, IUserDataSyncStoreService, SyncSource, SyncStatus, IUserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync';
+import { IUserDataSyncStoreService, SyncResource, SyncStatus, IUserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync';
import { UserDataSyncClient, UserDataSyncTestServer } from 'vs/platform/userDataSync/test/common/userDataSyncClient';
import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle';
import { AbstractSynchroniser, IRemoteUserData } from 'vs/platform/userDataSync/common/abstractSynchronizer';
@@ -17,7 +17,7 @@ class TestSynchroniser extends AbstractSynchroniser {
syncResult: { status?: SyncStatus, error?: boolean } = {};
onDoSyncCall: Emitter = this._register(new Emitter());
- readonly resourceKey: ResourceKey = 'settings';
+ readonly resource: SyncResource = SyncResource.Settings;
protected readonly version: number = 1;
private cancelled: boolean = false;
@@ -40,7 +40,7 @@ class TestSynchroniser extends AbstractSynchroniser {
}
async apply(ref: string): Promise {
- ref = await this.userDataSyncStoreService.write(this.resourceKey, '', ref);
+ ref = await this.userDataSyncStoreService.write(this.resource, '', ref);
await this.updateLastSyncUserData({ ref, syncData: { content: '', version: this.version } });
}
@@ -68,7 +68,7 @@ suite('TestSynchronizer', () => {
teardown(() => disposableStore.clear());
test('status is syncing', async () => {
- const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncSource.Settings, 'settings');
+ const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings, 'settings');
const actual: SyncStatus[] = [];
disposableStore.add(testObject.onDidChangeStatus(status => actual.push(status)));
@@ -85,7 +85,7 @@ suite('TestSynchronizer', () => {
});
test('status is set correctly when sync is finished', async () => {
- const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncSource.Settings, 'settings');
+ const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings, 'settings');
testObject.syncBarrier.open();
const actual: SyncStatus[] = [];
@@ -97,7 +97,7 @@ suite('TestSynchronizer', () => {
});
test('status is set correctly when sync has conflicts', async () => {
- const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncSource.Settings, 'settings');
+ const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings, 'settings');
testObject.syncResult = { status: SyncStatus.HasConflicts };
testObject.syncBarrier.open();
@@ -110,7 +110,7 @@ suite('TestSynchronizer', () => {
});
test('status is set correctly when sync has errors', async () => {
- const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncSource.Settings, 'settings');
+ const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings, 'settings');
testObject.syncResult = { error: true };
testObject.syncBarrier.open();
@@ -127,7 +127,7 @@ suite('TestSynchronizer', () => {
});
test('sync should not run if syncing already', async () => {
- const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncSource.Settings, 'settings');
+ const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings, 'settings');
const promise = Event.toPromise(testObject.onDoSyncCall.event);
testObject.sync();
@@ -144,8 +144,8 @@ suite('TestSynchronizer', () => {
});
test('sync should not run if disabled', async () => {
- const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncSource.Settings, 'settings');
- client.instantiationService.get(IUserDataSyncEnablementService).setResourceEnablement(testObject.resourceKey, false);
+ const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings, 'settings');
+ client.instantiationService.get(IUserDataSyncEnablementService).setResourceEnablement(testObject.resource, false);
const actual: SyncStatus[] = [];
disposableStore.add(testObject.onDidChangeStatus(status => actual.push(status)));
@@ -157,7 +157,7 @@ suite('TestSynchronizer', () => {
});
test('sync should not run if there are conflicts', async () => {
- const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncSource.Settings, 'settings');
+ const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings, 'settings');
testObject.syncResult = { status: SyncStatus.HasConflicts };
testObject.syncBarrier.open();
await testObject.sync();
@@ -171,7 +171,7 @@ suite('TestSynchronizer', () => {
});
test('request latest data on precondition failure', async () => {
- const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncSource.Settings, 'settings');
+ const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings, 'settings');
// Sync once
testObject.syncBarrier.open();
await testObject.sync();
@@ -186,13 +186,13 @@ suite('TestSynchronizer', () => {
});
// Start sycing
- const { ref } = await userDataSyncStoreService.read(testObject.resourceKey, null);
+ const { ref } = await userDataSyncStoreService.read(testObject.resource, null);
await testObject.sync(ref);
assert.deepEqual(server.requests, [
- { type: 'POST', url: `${server.url}/v1/resource/${testObject.resourceKey}`, headers: { 'If-Match': ref } },
- { type: 'GET', url: `${server.url}/v1/resource/${testObject.resourceKey}/latest`, headers: {} },
- { type: 'POST', url: `${server.url}/v1/resource/${testObject.resourceKey}`, headers: { 'If-Match': `${parseInt(ref) + 1}` } },
+ { type: 'POST', url: `${server.url}/v1/resource/${testObject.resource}`, headers: { 'If-Match': ref } },
+ { type: 'GET', url: `${server.url}/v1/resource/${testObject.resource}/latest`, headers: {} },
+ { type: 'POST', url: `${server.url}/v1/resource/${testObject.resource}`, headers: { 'If-Match': `${parseInt(ref) + 1}` } },
]);
});
diff --git a/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts b/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts
index 181df5ae44f..7f6e210866e 100644
--- a/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts
+++ b/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts
@@ -6,7 +6,7 @@
import { IRequestService } from 'vs/platform/request/common/request';
import { IRequestOptions, IRequestContext, IHeaders } from 'vs/base/parts/request/common/request';
import { CancellationToken } from 'vs/base/common/cancellation';
-import { IUserData, ResourceKey, IUserDataManifest, ALL_RESOURCE_KEYS, IUserDataSyncLogService, IUserDataSyncStoreService, IUserDataSyncUtilService, IUserDataSyncEnablementService, ISettingsSyncService, IUserDataSyncService, getDefaultIgnoredSettings, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync';
+import { IUserData, IUserDataManifest, ALL_SYNC_RESOURCES, IUserDataSyncLogService, IUserDataSyncStoreService, IUserDataSyncUtilService, IUserDataSyncEnablementService, IUserDataSyncService, getDefaultIgnoredSettings, IUserDataSyncBackupStoreService, SyncResource } from 'vs/platform/userDataSync/common/userDataSync';
import { bufferToStream, VSBuffer } from 'vs/base/common/buffer';
import { generateUuid } from 'vs/base/common/uuid';
import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService';
@@ -31,7 +31,6 @@ import { GlobalExtensionEnablementService } from 'vs/platform/extensionManagemen
import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider';
import { ConfigurationService } from 'vs/platform/configuration/common/configurationService';
import { Disposable } from 'vs/base/common/lifecycle';
-import { SettingsSynchroniser } from 'vs/platform/userDataSync/common/settingsSync';
import { Emitter } from 'vs/base/common/event';
import { IAuthenticationTokenService } from 'vs/platform/authentication/common/authentication';
import product from 'vs/platform/product/common/product';
@@ -57,6 +56,7 @@ export class UserDataSyncClient extends Disposable {
keybindingsResource: joinPath(userDataDirectory, 'keybindings.json'),
keybindingsSyncPreviewResource: joinPath(userDataSyncHome, 'keybindings.json'),
argvResource: joinPath(userDataDirectory, 'argv.json'),
+ args: {}
});
const logService = new NullLogService();
@@ -105,7 +105,6 @@ export class UserDataSyncClient extends Disposable {
async getCompatibleExtension() { return null; }
});
- this.instantiationService.stub(ISettingsSyncService, this.instantiationService.createInstance(SettingsSynchroniser));
this.instantiationService.stub(IUserDataSyncService, this.instantiationService.createInstance(UserDataSyncService));
if (!empty) {
@@ -120,8 +119,8 @@ export class UserDataSyncClient extends Disposable {
return this.instantiationService.get(IUserDataSyncService).sync();
}
- read(key: ResourceKey): Promise {
- return this.instantiationService.get(IUserDataSyncStoreService).read(key, null);
+ read(resource: SyncResource): Promise {
+ return this.instantiationService.get(IUserDataSyncStoreService).read(resource, null);
}
}
@@ -132,7 +131,7 @@ export class UserDataSyncTestServer implements IRequestService {
readonly url: string = 'http://host:3000';
private session: string | null = null;
- private readonly data: Map = new Map();
+ private readonly data: Map = new Map();
private _requests: { url: string, type: string, headers?: IHeaders }[] = [];
get requests(): { url: string, type: string, headers?: IHeaders }[] { return this._requests; }
@@ -180,7 +179,7 @@ export class UserDataSyncTestServer implements IRequestService {
private async getManifest(headers?: IHeaders): Promise {
if (this.session) {
- const latest: Record = Object.create({});
+ const latest: Record = Object.create({});
const manifest: IUserDataManifest = { session: this.session, latest };
this.data.forEach((value, key) => latest[key] = value.ref);
return this.toResponse(200, { 'Content-Type': 'application/json' }, JSON.stringify(manifest));
@@ -189,7 +188,7 @@ export class UserDataSyncTestServer implements IRequestService {
}
private async getLatestData(resource: string, headers: IHeaders = {}): Promise {
- const resourceKey = ALL_RESOURCE_KEYS.find(key => key === resource);
+ const resourceKey = ALL_SYNC_RESOURCES.find(key => key === resource);
if (resourceKey) {
const data = this.data.get(resourceKey);
if (!data) {
@@ -210,7 +209,7 @@ export class UserDataSyncTestServer implements IRequestService {
if (!this.session) {
this.session = generateUuid();
}
- const resourceKey = ALL_RESOURCE_KEYS.find(key => key === resource);
+ const resourceKey = ALL_SYNC_RESOURCES.find(key => key === resource);
if (resourceKey) {
const data = this.data.get(resourceKey);
if (headers['If-Match'] !== (data ? data.ref : '0')) {
diff --git a/src/vs/platform/userDataSync/test/common/userDataSyncService.test.ts b/src/vs/platform/userDataSync/test/common/userDataSyncService.test.ts
index 86ca60f8a0a..6c5e02511da 100644
--- a/src/vs/platform/userDataSync/test/common/userDataSyncService.test.ts
+++ b/src/vs/platform/userDataSync/test/common/userDataSyncService.test.ts
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
-import { IUserDataSyncService, UserDataSyncError, UserDataSyncErrorCode, SyncStatus, SyncSource } from 'vs/platform/userDataSync/common/userDataSync';
+import { IUserDataSyncService, UserDataSyncError, UserDataSyncErrorCode, SyncStatus, SyncResource } from 'vs/platform/userDataSync/common/userDataSync';
import { UserDataSyncClient, UserDataSyncTestServer } from 'vs/platform/userDataSync/test/common/userDataSyncClient';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { IFileService } from 'vs/platform/files/common/files';
@@ -480,7 +480,7 @@ suite('UserDataSyncService', () => {
await testObject.sync();
assert.deepEqual(testObject.status, SyncStatus.HasConflicts);
- assert.deepEqual(testObject.conflictsSources, [SyncSource.Settings]);
+ assert.deepEqual(testObject.conflictsSources, [SyncResource.Settings]);
});
test('test sync will sync other non conflicted areas', async () => {
diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts
index 53826e67839..c5b6fe3f2dd 100644
--- a/src/vs/vscode.d.ts
+++ b/src/vs/vscode.d.ts
@@ -2116,6 +2116,9 @@ declare module 'vscode' {
/**
* A [command](#Command) this code action executes.
+ *
+ * If this command throws an exception, VS Code displays the exception message to users in the editor at the
+ * current cursor position.
*/
command?: Command;
@@ -2145,8 +2148,8 @@ declare module 'vscode' {
* of code action, such as refactorings.
*
* - If the user has a [keybinding](https://code.visualstudio.com/docs/editor/refactoring#_keybindings-for-code-actions)
- * that auto applies a code action and only a disabled code actions are returned, VS Code will show the user a
- * message with `reason` in the editor.
+ * that auto applies a code action and only a disabled code actions are returned, VS Code will show the user an
+ * error message with `reason` in the editor.
*/
disabled?: {
/**
@@ -7929,7 +7932,7 @@ declare module 'vscode' {
/**
* The location at which progress should show.
*/
- location: ProgressLocation;
+ location: ProgressLocation | { viewId: string };
/**
* A human-readable string which will be used to describe the
diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts
index 311be8bb15b..9d69b64b455 100644
--- a/src/vs/vscode.proposed.d.ts
+++ b/src/vs/vscode.proposed.d.ts
@@ -76,9 +76,9 @@ declare module 'vscode' {
export const onDidChangeAuthenticationProviders: Event;
/**
- * Returns whether a provider with providerId is currently registered.
+ * An array of the ids of authentication providers that are currently registered.
*/
- export function hasProvider(providerId: string): boolean;
+ export const providerIds: string[];
/**
* Get existing authentication sessions. Rejects if a provider with providerId is not
@@ -1086,6 +1086,22 @@ declare module 'vscode' {
//#endregion
+ //#region Terminal link handlers https://github.com/microsoft/vscode/issues/91606
+
+ export namespace window {
+ export function registerTerminalLinkHandler(handler: TerminalLinkHandler): Disposable;
+ }
+
+ export interface TerminalLinkHandler {
+ /**
+ * @return true when the link was handled (and should not be considered by
+ * other providers including the default), false when the link was not handled.
+ */
+ handleLink(terminal: Terminal, link: string): ProviderResult;
+ }
+
+ //#endregion
+
//#region Joh -> exclusive document filters
export interface DocumentFilter {
@@ -1222,81 +1238,71 @@ declare module 'vscode' {
// - Should we expose edits?
// - More properties from `TextDocument`?
- /**
- * Defines the capabilities of a custom webview editor.
- */
- interface CustomEditorCapabilities {
- /**
- * Defines the editing capability of a custom webview document.
- *
- * When not provided, the document is considered readonly.
- */
- readonly editing?: CustomEditorEditingCapability;
- }
-
/**
* Defines the editing capability of a custom webview editor. This allows the webview editor to hook into standard
* editor events such as `undo` or `save`.
*
* @param EditType Type of edits.
*/
- interface CustomEditorEditingCapability {
+ interface CustomEditorEditingDelegate {
/**
* Save the resource.
*
+ * @param document Document to save.
* @param cancellation Token that signals the save is no longer required (for example, if another save was triggered).
*
* @return Thenable signaling that the save has completed.
*/
- save(cancellation: CancellationToken): Thenable;
+ save(document: CustomDocument, cancellation: CancellationToken): Thenable;
/**
* Save the existing resource at a new path.
*
+ * @param document Document to save.
* @param targetResource Location to save to.
*
* @return Thenable signaling that the save has completed.
*/
- saveAs(targetResource: Uri): Thenable;
+ saveAs(document: CustomDocument, targetResource: Uri): Thenable;
/**
* Event triggered by extensions to signal to VS Code that an edit has occurred.
*/
- readonly onDidEdit: Event;
+ readonly onDidEdit: Event>;
/**
* Apply a set of edits.
*
* Note that is not invoked when `onDidEdit` is called because `onDidEdit` implies also updating the view to reflect the edit.
*
+ * @param document Document to apply edits to.
* @param edit Array of edits. Sorted from oldest to most recent.
*
* @return Thenable signaling that the change has completed.
*/
- applyEdits(edits: readonly EditType[]): Thenable;
+ applyEdits(document: CustomDocument, edits: readonly EditType[]): Thenable;
/**
* Undo a set of edits.
*
* This is triggered when a user undoes an edit.
*
+ * @param document Document to undo edits from.
* @param edit Array of edits. Sorted from most recent to oldest.
*
* @return Thenable signaling that the change has completed.
*/
- undoEdits(edits: readonly EditType[]): Thenable;
+ undoEdits(document: CustomDocument, edits: readonly EditType[]): Thenable;
/**
* Revert the file to its last saved state.
*
- * @param change Added or applied edits.
+ * @param document Document to revert.
+ * @param edits Added or applied edits.
*
* @return Thenable signaling that the change has completed.
*/
- revert(change: {
- readonly undoneEdits: readonly EditType[];
- readonly appliedEdits: readonly EditType[];
- }): Thenable;
+ revert(document: CustomDocument, edits: CustomDocumentRevert): Thenable;
/**
* Back up the resource in its current state.
@@ -1311,12 +1317,50 @@ declare module 'vscode' {
* made in quick succession, `backup` is only triggered after the last one. `backup` is not invoked when
* `auto save` is enabled (since auto save already persists resource ).
*
+ * @param document Document to revert.
* @param cancellation Token that signals the current backup since a new backup is coming in. It is up to your
* extension to decided how to respond to cancellation. If for example your extension is backing up a large file
* in an operation that takes time to complete, your extension may decide to finish the ongoing backup rather
* than cancelling it to ensure that VS Code has some valid backup.
*/
- backup(cancellation: CancellationToken): Thenable;
+ backup(document: CustomDocument, cancellation: CancellationToken): Thenable;
+ }
+
+ /**
+ * Event triggered by extensions to signal to VS Code that an edit has occurred on a CustomDocument``.
+ */
+ interface CustomDocumentEditEvent {
+ /**
+ * Document the edit is for.
+ */
+ readonly document: CustomDocument;
+
+ /**
+ * Object that describes the edit.
+ *
+ * Edit objects are passed back to your extension in `undoEdits`, `applyEdits`, and `revert`.
+ */
+ readonly edit: EditType;
+
+ /**
+ * Display name describing the edit.
+ */
+ readonly label?: string;
+ }
+
+ /**
+ * Data about a revert for a `CustomDocument`.
+ */
+ interface CustomDocumentRevert {
+ /**
+ * List of edits that were undone to get the document back to its on disk state.
+ */
+ readonly undoneEdits: readonly EditType[];
+
+ /**
+ * List of edits that were reapplied to get the document back to its on disk state.
+ */
+ readonly appliedEdits: readonly EditType[];
}
/**
@@ -1375,7 +1419,7 @@ declare module 'vscode' {
*
* @return The capabilities of the resolved document.
*/
- resolveCustomDocument(document: CustomDocument): Thenable;
+ resolveCustomDocument(document: CustomDocument): Thenable;
/**
* Resolve a webview editor for a given resource.
@@ -1393,6 +1437,13 @@ declare module 'vscode' {
* @return Thenable indicating that the webview editor has been resolved.
*/
resolveCustomEditor(document: CustomDocument, webviewPanel: WebviewPanel): Thenable;
+
+ /**
+ * Defines the editing capability of a custom webview document.
+ *
+ * When not provided, the document is considered readonly.
+ */
+ readonly editingDelegate?: CustomEditorEditingDelegate;
}
/**
@@ -1422,6 +1473,21 @@ declare module 'vscode' {
* @return Thenable indicating that the webview editor has been resolved.
*/
resolveCustomTextEditor(document: TextDocument, webviewPanel: WebviewPanel): Thenable;
+
+ /**
+ * TODO: discuss this at api sync.
+ *
+ * Handle when the underlying resource for a custom editor is renamed.
+ *
+ * This allows the webview for the editor be preserved throughout the rename. If this method is not implemented,
+ * VS Code will destory the previous custom editor and create a replacement one.
+ *
+ * @param newDocument New text document to use for the custom editor.
+ * @param existingWebviewPanel Webview panel for the custom editor.
+ *
+ * @return Thenable indicating that the webview editor has been moved.
+ */
+ moveCustomTextEditor?(newDocument: TextDocument, existingWebviewPanel: WebviewPanel): Thenable;
}
namespace window {
@@ -1438,7 +1504,7 @@ declare module 'vscode' {
export function registerCustomEditorProvider(
viewType: string,
provider: CustomEditorProvider | CustomTextEditorProvider,
- webviewOptions?: WebviewPanelOptions,
+ webviewOptions?: WebviewPanelOptions, // TODO: move this onto provider?
): Disposable;
}
@@ -1921,9 +1987,9 @@ declare module 'vscode' {
* A code that identifies this error.
*
* Possible values are names of errors, like [`FileNotFound`](#FileSystemError.FileNotFound),
- * or `undefined` for an unspecified error.
+ * or `Unknown` for an unspecified error.
*/
- readonly code?: string;
+ readonly code: string;
}
//#endregion
@@ -1943,4 +2009,5 @@ declare module 'vscode' {
}
//#endregion
+
}
diff --git a/src/vs/workbench/api/browser/mainThreadDebugService.ts b/src/vs/workbench/api/browser/mainThreadDebugService.ts
index 4e48c4bf864..9686820a36a 100644
--- a/src/vs/workbench/api/browser/mainThreadDebugService.ts
+++ b/src/vs/workbench/api/browser/mainThreadDebugService.ts
@@ -75,7 +75,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
}
runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments): Promise {
- return Promise.resolve(this._proxy.$runInTerminal(args));
+ return this._proxy.$runInTerminal(args);
}
// RPC methods (MainThreadDebugServiceShape)
diff --git a/src/vs/workbench/api/browser/mainThreadTerminalService.ts b/src/vs/workbench/api/browser/mainThreadTerminalService.ts
index 372db8a6af2..7018e5b4837 100644
--- a/src/vs/workbench/api/browser/mainThreadTerminalService.ts
+++ b/src/vs/workbench/api/browser/mainThreadTerminalService.ts
@@ -3,13 +3,13 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import { DisposableStore, Disposable } from 'vs/base/common/lifecycle';
+import { DisposableStore, Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { IShellLaunchConfig, ITerminalProcessExtHostProxy, ISpawnExtHostProcessRequest, ITerminalDimensions, EXT_HOST_CREATION_DELAY, IAvailableShellsRequest, IDefaultShellAndArgsRequest, IStartExtensionTerminalRequest } from 'vs/workbench/contrib/terminal/common/terminal';
import { ExtHostContext, ExtHostTerminalServiceShape, MainThreadTerminalServiceShape, MainContext, IExtHostContext, IShellLaunchConfigDto, TerminalLaunchConfig, ITerminalDimensionsDto } from 'vs/workbench/api/common/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { URI } from 'vs/base/common/uri';
import { StopWatch } from 'vs/base/common/stopwatch';
-import { ITerminalInstanceService, ITerminalService, ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal';
+import { ITerminalInstanceService, ITerminalService, ITerminalInstance, ITerminalBeforeHandleLinkEvent } from 'vs/workbench/contrib/terminal/browser/terminal';
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { TerminalDataBufferer } from 'vs/workbench/contrib/terminal/common/terminalDataBuffering';
@@ -23,6 +23,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
private readonly _terminalProcesses = new Map>();
private readonly _terminalProcessesReady = new Map void>();
private _dataEventTracker: TerminalDataEventTracker | undefined;
+ private _linkHandler: IDisposable | undefined;
constructor(
extHostContext: IExtHostContext,
@@ -146,6 +147,22 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
}
}
+ public $startHandlingLinks(): void {
+ this._linkHandler?.dispose();
+ this._linkHandler = this._terminalService.addLinkHandler(this._remoteAuthority || '', e => this._handleLink(e));
+ }
+
+ public $stopHandlingLinks(): void {
+ this._linkHandler?.dispose();
+ }
+
+ private async _handleLink(e: ITerminalBeforeHandleLinkEvent): Promise