diff --git a/build/.cachesalt b/build/.cachesalt
index 339d2d379fa..3f2ee542ad5 100644
--- a/build/.cachesalt
+++ b/build/.cachesalt
@@ -1 +1 @@
-2019-08-30T20:24:23.714Z
+2020-10-05T20:24:23.714Z
diff --git a/build/azure-pipelines/darwin/continuous-build-darwin.yml b/build/azure-pipelines/darwin/continuous-build-darwin.yml
index 0733d47bc72..9f66907f0a1 100644
--- a/build/azure-pipelines/darwin/continuous-build-darwin.yml
+++ b/build/azure-pipelines/darwin/continuous-build-darwin.yml
@@ -66,7 +66,8 @@ steps:
artifactName: crash-dump-macos
targetPath: .build/crashes
displayName: 'Publish Crash Reports'
- condition: succeededOrFailed()
+ continueOnError: true
+ condition: failed()
- task: PublishTestResults@2
displayName: Publish Tests Results
diff --git a/build/azure-pipelines/darwin/entitlements.plist b/build/azure-pipelines/darwin/entitlements.plist
index 6631ffa6f24..be8b7163da7 100644
--- a/build/azure-pipelines/darwin/entitlements.plist
+++ b/build/azure-pipelines/darwin/entitlements.plist
@@ -2,5 +2,13 @@
+ com.apple.security.cs.allow-jit
+
+ com.apple.security.cs.allow-unsigned-executable-memory
+
+ com.apple.security.cs.disable-library-validation
+
+ com.apple.security.cs.allow-dyld-environment-variables
+
diff --git a/build/azure-pipelines/darwin/helper-entitlements.plist b/build/azure-pipelines/darwin/helper-entitlements.plist
new file mode 100644
index 00000000000..123d12a53e9
--- /dev/null
+++ b/build/azure-pipelines/darwin/helper-entitlements.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ com.apple.security.cs.disable-library-validation
+
+
+
diff --git a/build/azure-pipelines/darwin/helper-gpu-entitlements.plist b/build/azure-pipelines/darwin/helper-gpu-entitlements.plist
index 4efe1ce508f..777b3abd95e 100644
--- a/build/azure-pipelines/darwin/helper-gpu-entitlements.plist
+++ b/build/azure-pipelines/darwin/helper-gpu-entitlements.plist
@@ -4,5 +4,7 @@
com.apple.security.cs.allow-jit
+ com.apple.security.cs.disable-library-validation
+
diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml
index 32bfd2c83db..48b8d9ed40a 100644
--- a/build/azure-pipelines/darwin/product-build-darwin.yml
+++ b/build/azure-pipelines/darwin/product-build-darwin.yml
@@ -157,7 +157,8 @@ steps:
artifactName: crash-dump-macos
targetPath: .build/crashes
displayName: 'Publish Crash Reports'
- condition: succeededOrFailed()
+ continueOnError: true
+ condition: failed()
- script: |
set -e
@@ -172,6 +173,7 @@ steps:
security import $(agent.tempdirectory)/cert.p12 -k $(agent.tempdirectory)/buildagent.keychain -P "$(macos-developer-certificate-key)" -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k pwd $(agent.tempdirectory)/buildagent.keychain
codesign -s 99FM488X57 --deep --force --options runtime --entitlements build/azure-pipelines/darwin/entitlements.plist "$APP_ROOT"/*.app
+ codesign -s 99FM488X57 --force --options runtime --entitlements build/azure-pipelines/darwin/helper-entitlements.plist "$APP_FRAMEWORK_PATH/$HELPER_APP_NAME Helper.app"
codesign -s 99FM488X57 --force --options runtime --entitlements build/azure-pipelines/darwin/helper-gpu-entitlements.plist "$APP_FRAMEWORK_PATH/$HELPER_APP_NAME Helper (GPU).app"
codesign -s 99FM488X57 --force --options runtime --entitlements build/azure-pipelines/darwin/helper-plugin-entitlements.plist "$APP_FRAMEWORK_PATH/$HELPER_APP_NAME Helper (Plugin).app"
codesign -s 99FM488X57 --force --options runtime --entitlements build/azure-pipelines/darwin/helper-renderer-entitlements.plist "$APP_FRAMEWORK_PATH/$HELPER_APP_NAME Helper (Renderer).app"
@@ -241,6 +243,13 @@ steps:
SessionTimeout: 60
displayName: Notarization
+- script: |
+ set -e
+ APP_ROOT=$(agent.builddirectory)/VSCode-darwin
+ APP_NAME="`ls $APP_ROOT | head -n 1`"
+ "$APP_ROOT/$APP_NAME/Contents/Resources/app/bin/code" --version
+ displayName: Verify start after signing
+
- script: |
set -e
VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \
@@ -251,6 +260,11 @@ steps:
./build/azure-pipelines/darwin/publish.sh
displayName: Publish
+- script: |
+ yarn gulp upload-vscode-configuration
+ displayName: Upload configuration (for Bing settings search)
+ continueOnError: true
+
- task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0
displayName: 'Component Detection'
continueOnError: true
diff --git a/build/azure-pipelines/darwin/publish.sh b/build/azure-pipelines/darwin/publish.sh
index f0375d2a3c8..fe3e9a59986 100755
--- a/build/azure-pipelines/darwin/publish.sh
+++ b/build/azure-pipelines/darwin/publish.sh
@@ -22,6 +22,3 @@ node build/azure-pipelines/common/createAsset.js \
# node build/azure-pipelines/common/symbols.js "$VSCODE_MIXIN_PASSWORD" "$VSCODE_HOCKEYAPP_TOKEN" x64 "$VSCODE_HOCKEYAPP_ID_MACOS"
# Skip hockey app because build failure.
# https://github.com/microsoft/vscode/issues/90491
-
-# upload configuration
-yarn gulp upload-vscode-configuration
diff --git a/build/azure-pipelines/linux/continuous-build-linux.yml b/build/azure-pipelines/linux/continuous-build-linux.yml
index fb0a5b2dd3e..fdd4c305cda 100644
--- a/build/azure-pipelines/linux/continuous-build-linux.yml
+++ b/build/azure-pipelines/linux/continuous-build-linux.yml
@@ -75,8 +75,9 @@ steps:
artifactName: crash-dump-linux
targetPath: .build/crashes
displayName: 'Publish Crash Reports'
- condition: succeededOrFailed()
-
+ continueOnError: true
+ condition: failed()
+
- task: PublishTestResults@2
displayName: Publish Tests Results
inputs:
diff --git a/build/azure-pipelines/linux/product-build-linux.yml b/build/azure-pipelines/linux/product-build-linux.yml
index 76428b860f2..f28c896ba83 100644
--- a/build/azure-pipelines/linux/product-build-linux.yml
+++ b/build/azure-pipelines/linux/product-build-linux.yml
@@ -145,7 +145,8 @@ steps:
artifactName: crash-dump-linux
targetPath: .build/crashes
displayName: 'Publish Crash Reports'
- condition: succeededOrFailed()
+ continueOnError: true
+ condition: failed()
- script: |
set -e
diff --git a/build/azure-pipelines/product-compile.yml b/build/azure-pipelines/product-compile.yml
index 1f665c8b3de..c3db41e80d5 100644
--- a/build/azure-pipelines/product-compile.yml
+++ b/build/azure-pipelines/product-compile.yml
@@ -76,7 +76,6 @@ steps:
set -e
yarn generate-github-config
displayName: Generate GitHub config
- condition: succeeded()
env:
OSS_GITHUB_ID: "a5d3c261b032765a78de"
OSS_GITHUB_SECRET: $(oss-github-client-secret)
@@ -94,6 +93,7 @@ steps:
VSO_DEV_GITHUB_SECRET: $(vso-dev-github-client-secret)
GITHUB_APP_ID: "Iv1.ae51e546bef24ff1"
GITHUB_APP_SECRET: $(github-app-client-secret)
+ condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true'), ne(variables['CacheRestored'], 'true'))
- script: |
set -e
diff --git a/build/azure-pipelines/win32/continuous-build-win32.yml b/build/azure-pipelines/win32/continuous-build-win32.yml
index 57385d54299..b0a81aeb8c8 100644
--- a/build/azure-pipelines/win32/continuous-build-win32.yml
+++ b/build/azure-pipelines/win32/continuous-build-win32.yml
@@ -73,7 +73,8 @@ steps:
inputs:
artifactName: crash-dump-windows
targetPath: .build\crashes
- condition: succeededOrFailed()
+ continueOnError: true
+ condition: failed()
- task: PublishTestResults@2
displayName: Publish Tests Results
diff --git a/build/azure-pipelines/win32/product-build-win32.yml b/build/azure-pipelines/win32/product-build-win32.yml
index 75dc54e3599..bcd03489df6 100644
--- a/build/azure-pipelines/win32/product-build-win32.yml
+++ b/build/azure-pipelines/win32/product-build-win32.yml
@@ -154,7 +154,8 @@ steps:
artifactName: crash-dump-windows-$(VSCODE_ARCH)
targetPath: .build\crashes
displayName: 'Publish Crash Reports'
- condition: succeededOrFailed()
+ continueOnError: true
+ condition: failed()
- task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1
inputs:
diff --git a/build/gulpfile.editor.js b/build/gulpfile.editor.js
index f6106241ced..6cb65c5a166 100644
--- a/build/gulpfile.editor.js
+++ b/build/gulpfile.editor.js
@@ -127,6 +127,7 @@ const createESMSourcesAndResourcesTask = task.define('extract-editor-esm', () =>
const compileEditorESMTask = task.define('compile-editor-esm', () => {
const KEEP_PREV_ANALYSIS = false;
+ const FAIL_ON_PURPOSE = false;
console.log(`Launching the TS compiler at ${path.join(__dirname, '../out-editor-esm')}...`);
let result;
if (process.platform === 'win32') {
@@ -142,7 +143,7 @@ const compileEditorESMTask = task.define('compile-editor-esm', () => {
console.log(result.stdout.toString());
console.log(result.stderr.toString());
- if (result.status !== 0) {
+ if (FAIL_ON_PURPOSE || result.status !== 0) {
console.log(`The TS Compilation failed, preparing analysis folder...`);
const destPath = path.join(__dirname, '../../vscode-monaco-editor-esm-analysis');
const keepPrevAnalysis = (KEEP_PREV_ANALYSIS && fs.existsSync(destPath));
diff --git a/build/lib/treeshaking.js b/build/lib/treeshaking.js
index cd366b9c524..5b1cf0591ec 100644
--- a/build/lib/treeshaking.js
+++ b/build/lib/treeshaking.js
@@ -420,7 +420,7 @@ function markNodes(languageService, options) {
// (they can be the declaration of a module import)
continue;
}
- if (options.shakeLevel === 2 /* ClassMembers */ && (ts.isClassDeclaration(declaration) || ts.isInterfaceDeclaration(declaration))) {
+ if (options.shakeLevel === 2 /* ClassMembers */ && (ts.isClassDeclaration(declaration) || ts.isInterfaceDeclaration(declaration)) && !isLocalCodeExtendingOrInheritingFromDefaultLibSymbol(program, checker, declaration)) {
enqueue_black(declaration.name);
for (let j = 0; j < declaration.members.length; j++) {
const member = declaration.members[j];
@@ -614,6 +614,34 @@ function generateResult(languageService, shakeLevel) {
}
//#endregion
//#region Utils
+function isLocalCodeExtendingOrInheritingFromDefaultLibSymbol(program, checker, declaration) {
+ if (!program.isSourceFileDefaultLibrary(declaration.getSourceFile()) && declaration.heritageClauses) {
+ for (const heritageClause of declaration.heritageClauses) {
+ for (const type of heritageClause.types) {
+ const symbol = findSymbolFromHeritageType(checker, type);
+ if (symbol) {
+ const decl = symbol.valueDeclaration || (symbol.declarations && symbol.declarations[0]);
+ if (decl && program.isSourceFileDefaultLibrary(decl.getSourceFile())) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+function findSymbolFromHeritageType(checker, type) {
+ if (ts.isExpressionWithTypeArguments(type)) {
+ return findSymbolFromHeritageType(checker, type.expression);
+ }
+ if (ts.isIdentifier(type)) {
+ return getRealNodeSymbol(checker, type)[0];
+ }
+ if (ts.isPropertyAccessExpression(type)) {
+ return findSymbolFromHeritageType(checker, type.name);
+ }
+ return null;
+}
/**
* Returns the node's symbol and the `import` node (if the symbol resolved from a different module)
*/
diff --git a/build/lib/treeshaking.ts b/build/lib/treeshaking.ts
index b65475299f5..405336bfcbb 100644
--- a/build/lib/treeshaking.ts
+++ b/build/lib/treeshaking.ts
@@ -536,7 +536,7 @@ function markNodes(languageService: ts.LanguageService, options: ITreeShakingOpt
continue;
}
- if (options.shakeLevel === ShakeLevel.ClassMembers && (ts.isClassDeclaration(declaration) || ts.isInterfaceDeclaration(declaration))) {
+ if (options.shakeLevel === ShakeLevel.ClassMembers && (ts.isClassDeclaration(declaration) || ts.isInterfaceDeclaration(declaration)) && !isLocalCodeExtendingOrInheritingFromDefaultLibSymbol(program, checker, declaration)) {
enqueue_black(declaration.name!);
for (let j = 0; j < declaration.members.length; j++) {
@@ -752,6 +752,36 @@ function generateResult(languageService: ts.LanguageService, shakeLevel: ShakeLe
//#region Utils
+function isLocalCodeExtendingOrInheritingFromDefaultLibSymbol(program: ts.Program, checker: ts.TypeChecker, declaration: ts.ClassDeclaration | ts.InterfaceDeclaration): boolean {
+ if (!program.isSourceFileDefaultLibrary(declaration.getSourceFile()) && declaration.heritageClauses) {
+ for (const heritageClause of declaration.heritageClauses) {
+ for (const type of heritageClause.types) {
+ const symbol = findSymbolFromHeritageType(checker, type);
+ if (symbol) {
+ const decl = symbol.valueDeclaration || (symbol.declarations && symbol.declarations[0]);
+ if (decl && program.isSourceFileDefaultLibrary(decl.getSourceFile())) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
+function findSymbolFromHeritageType(checker: ts.TypeChecker, type: ts.ExpressionWithTypeArguments | ts.Expression | ts.PrivateIdentifier): ts.Symbol | null {
+ if (ts.isExpressionWithTypeArguments(type)) {
+ return findSymbolFromHeritageType(checker, type.expression);
+ }
+ if (ts.isIdentifier(type)) {
+ return getRealNodeSymbol(checker, type)[0];
+ }
+ if (ts.isPropertyAccessExpression(type)) {
+ return findSymbolFromHeritageType(checker, type.name);
+ }
+ return null;
+}
+
/**
* Returns the node's symbol and the `import` node (if the symbol resolved from a different module)
*/
diff --git a/extensions/github-authentication/src/extension.ts b/extensions/github-authentication/src/extension.ts
index 159f6c72234..3980ba7c7fd 100644
--- a/extensions/github-authentication/src/extension.ts
+++ b/extensions/github-authentication/src/extension.ts
@@ -25,6 +25,7 @@ export async function activate(context: vscode.ExtensionContext) {
vscode.authentication.registerAuthenticationProvider({
id: 'github',
displayName: 'GitHub',
+ supportsMultipleAccounts: false,
onDidChangeSessions: onDidChangeSessions.event,
getSessions: () => Promise.resolve(loginService.sessions),
login: async (scopeList: string[]) => {
diff --git a/extensions/image-preview/src/preview.ts b/extensions/image-preview/src/preview.ts
index 25d3ac86c22..f4b589e0fb8 100644
--- a/extensions/image-preview/src/preview.ts
+++ b/extensions/image-preview/src/preview.ts
@@ -179,7 +179,7 @@ class Preview extends Disposable {
private async render() {
if (this._previewState !== PreviewState.Disposed) {
- this.webviewEditor.webview.html = await this.getWebiewContents();
+ this.webviewEditor.webview.html = await this.getWebviewContents();
}
}
@@ -203,7 +203,7 @@ class Preview extends Disposable {
}
}
- private async getWebiewContents(): Promise {
+ private async getWebviewContents(): Promise {
const version = Date.now().toString();
const settings = {
isMac: process.platform === 'darwin',
@@ -249,9 +249,9 @@ class Preview extends Disposable {
// Avoid adding cache busting if there is already a query string
if (resource.query) {
- return webviewEditor.webview.asWebviewUri(resource).toString(true);
+ return webviewEditor.webview.asWebviewUri(resource).toString();
}
- return webviewEditor.webview.asWebviewUri(resource).with({ query: `version=${version}` }).toString(true);
+ return webviewEditor.webview.asWebviewUri(resource).with({ query: `version=${version}` }).toString();
}
private extensionResource(path: string) {
diff --git a/extensions/markdown-language-features/media/markdown.css b/extensions/markdown-language-features/media/markdown.css
index 5c06f2b082f..bff2bcdb48d 100644
--- a/extensions/markdown-language-features/media/markdown.css
+++ b/extensions/markdown-language-features/media/markdown.css
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
html, body {
- font-family: var(--vscode-markdown-font-family, -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif);
+ font-family: var(--vscode-markdown-font-family, system-ui, -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif);
font-size: var(--vscode-markdown-font-size, 14px);
padding: 0 26px;
line-height: var(--vscode-markdown-line-height, 22px);
@@ -157,7 +157,7 @@ blockquote {
}
code {
- font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Courier New", monospace, "Droid Sans Fallback";
+ font-family: var(--vscode-editor-font-family, Menlo, Monaco, Consolas, "Droid Sans Mono", "Courier New", monospace, "Droid Sans Fallback");
font-size: 1em;
line-height: 1.357em;
}
diff --git a/extensions/markdown-language-features/package.json b/extensions/markdown-language-features/package.json
index 541cd4e327a..fcefaef0a56 100644
--- a/extensions/markdown-language-features/package.json
+++ b/extensions/markdown-language-features/package.json
@@ -210,7 +210,7 @@
},
"markdown.preview.fontFamily": {
"type": "string",
- "default": "-apple-system, BlinkMacSystemFont, 'Segoe WPC', 'Segoe UI', 'Ubuntu', 'Droid Sans', sans-serif",
+ "default": "system-ui, -apple-system, BlinkMacSystemFont, 'Segoe WPC', 'Segoe UI', 'Ubuntu', 'Droid Sans', sans-serif",
"description": "%markdown.preview.fontFamily.desc%",
"scope": "resource"
},
diff --git a/extensions/markdown-language-features/package.nls.json b/extensions/markdown-language-features/package.nls.json
index fbbab999519..6cf645a4103 100644
--- a/extensions/markdown-language-features/package.nls.json
+++ b/extensions/markdown-language-features/package.nls.json
@@ -1,7 +1,7 @@
{
"displayName": "Markdown Language Features",
"description": "Provides rich language support for Markdown.",
- "markdown.preview.breaks.desc": "Sets how line-breaks are rendered in the markdown preview. Setting it to 'true' creates a
for every newline.",
+ "markdown.preview.breaks.desc": "Sets how line-breaks are rendered in the markdown preview. Setting it to 'true' creates a
for newlines inside paragraphs.",
"markdown.preview.linkify": "Enable or disable conversion of URL-like text to links in the markdown preview.",
"markdown.preview.doubleClickToSwitchToEditor.desc": "Double click in the markdown preview to switch to the editor.",
"markdown.preview.fontFamily.desc": "Controls the font family used in the markdown preview.",
diff --git a/extensions/npm/src/tasks.ts b/extensions/npm/src/tasks.ts
index 661796a3756..2f4947de447 100644
--- a/extensions/npm/src/tasks.ts
+++ b/extensions/npm/src/tasks.ts
@@ -29,6 +29,8 @@ type AutoDetect = 'on' | 'off';
let cachedTasks: Task[] | undefined = undefined;
+const INSTALL_SCRIPT = 'install';
+
export class NpmTaskProvider implements TaskProvider {
constructor() {
@@ -52,7 +54,7 @@ export class NpmTaskProvider implements TaskProvider {
} else {
packageJsonUri = _task.scope.uri.with({ path: _task.scope.uri.path + '/package.json' });
}
- return createTask(kind, `run ${kind.script}`, _task.scope, packageJsonUri);
+ return createTask(kind, `${kind.script === INSTALL_SCRIPT ? '' : 'run '}${kind.script}`, _task.scope, packageJsonUri);
}
return undefined;
}
@@ -253,7 +255,7 @@ async function provideNpmScriptsForFolder(packageJsonUri: Uri): Promise
result.push(task);
});
// always add npm install (without a problem matcher)
- result.push(createTask('install', 'install', folder, packageJsonUri, 'install dependencies from package', []));
+ result.push(createTask(INSTALL_SCRIPT, INSTALL_SCRIPT, folder, packageJsonUri, 'install dependencies from package', []));
return result;
}
diff --git a/extensions/package.json b/extensions/package.json
index 343e3053756..8744f4dff90 100644
--- a/extensions/package.json
+++ b/extensions/package.json
@@ -3,7 +3,7 @@
"version": "0.0.1",
"description": "Dependencies shared by all extensions",
"dependencies": {
- "typescript": "3.9.1-rc"
+ "typescript": "^3.9.2-insiders.20200509"
},
"scripts": {
"postinstall": "node ./postinstall"
diff --git a/extensions/perl/cgmanifest.json b/extensions/perl/cgmanifest.json
index b7c850dd1aa..83d91107671 100644
--- a/extensions/perl/cgmanifest.json
+++ b/extensions/perl/cgmanifest.json
@@ -6,7 +6,7 @@
"git": {
"name": "textmate/perl.tmbundle",
"repositoryUrl": "https://github.com/textmate/perl.tmbundle",
- "commitHash": "d9841a0878239fa43f88c640f8d458590f97e8f5"
+ "commitHash": "80826abe75250286c2a1a07958e50e8551d3f50c"
}
},
"licenseDetail": [
diff --git a/extensions/python/cgmanifest.json b/extensions/python/cgmanifest.json
index 97a9d1d1118..37a21b2de54 100644
--- a/extensions/python/cgmanifest.json
+++ b/extensions/python/cgmanifest.json
@@ -6,7 +6,7 @@
"git": {
"name": "MagicStack/MagicPython",
"repositoryUrl": "https://github.com/MagicStack/MagicPython",
- "commitHash": "0b09c1fca238d22e15ac5712d03f9bf6da626f9c"
+ "commitHash": "c9b3409deb69acec31bbf7913830e93a046b30cc"
}
},
"license": "MIT",
diff --git a/extensions/python/syntaxes/MagicPython.tmLanguage.json b/extensions/python/syntaxes/MagicPython.tmLanguage.json
index a6920a06305..eb3c96990b9 100644
--- a/extensions/python/syntaxes/MagicPython.tmLanguage.json
+++ b/extensions/python/syntaxes/MagicPython.tmLanguage.json
@@ -4,7 +4,7 @@
"If you want to provide a fix or improvement, please create a pull request against the original repository.",
"Once accepted there, we are happy to receive an update request."
],
- "version": "https://github.com/MagicStack/MagicPython/commit/0b09c1fca238d22e15ac5712d03f9bf6da626f9c",
+ "version": "https://github.com/MagicStack/MagicPython/commit/2ca894f270f92e2bc8f09a2ebdcd482fbb3b1074",
"name": "MagicPython",
"scopeName": "source.python",
"patterns": [
@@ -31,6 +31,9 @@
{
"include": "#function-declaration"
},
+ {
+ "include": "#generator"
+ },
{
"include": "#statement-keyword"
},
@@ -291,6 +294,9 @@
{
"include": "#lambda"
},
+ {
+ "include": "#generator"
+ },
{
"include": "#illegal-operator"
},
@@ -306,6 +312,9 @@
{
"include": "#list"
},
+ {
+ "include": "#odd-function-call"
+ },
{
"include": "#round-braces"
},
@@ -388,6 +397,9 @@
},
{
"include": "#member-access-base"
+ },
+ {
+ "include": "#member-access-attribute"
}
]
},
@@ -413,6 +425,11 @@
}
]
},
+ "member-access-attribute": {
+ "comment": "Highlight attribute access in otherwise non-specialized cases.",
+ "name": "meta.attribute.python",
+ "match": "(?x)\n \\b ([[:alpha:]_]\\w*) \\b\n"
+ },
"special-names": {
"name": "constant.other.caps.python",
"match": "(?x)\n \\b\n # we want to see \"enough\", meaning 2 or more upper-case\n # letters in the beginning of the constant\n #\n # for more details refer to:\n # https://github.com/MagicStack/MagicPython/issues/42\n (\n _* [[:upper:]] [_\\d]* [[:upper:]]\n )\n [[:upper:]\\d]* (_\\w*)?\n \\b\n"
@@ -459,6 +476,21 @@
}
]
},
+ "odd-function-call": {
+ "comment": "A bit obscured function call where there may have been an\narbitrary number of other operations to get the function.\nE.g. \"arr[idx](args)\"\n",
+ "begin": "(?x)\n (?<= \\] | \\) ) \\s*\n (?=\\()\n",
+ "end": "(\\))",
+ "endCaptures": {
+ "1": {
+ "name": "punctuation.definition.arguments.end.python"
+ }
+ },
+ "patterns": [
+ {
+ "include": "#function-arguments"
+ }
+ ]
+ },
"round-braces": {
"begin": "\\(",
"end": "\\)",
@@ -1195,6 +1227,26 @@
}
]
},
+ "generator": {
+ "comment": "Match \"for ... in\" construct used in generators and for loops to\ncorrectly identify the \"in\" as a control flow keyword.\n",
+ "begin": "for",
+ "beginCaptures": {
+ "0": {
+ "name": "keyword.control.flow.python"
+ }
+ },
+ "end": "in",
+ "endCaptures": {
+ "0": {
+ "name": "keyword.control.flow.python"
+ }
+ },
+ "patterns": [
+ {
+ "include": "#expression"
+ }
+ ]
+ },
"function-declaration": {
"name": "meta.function.python",
"begin": "(?x)\n \\s*\n (?:\\b(async) \\s+)? \\b(def)\\s+\n (?=\n [[:alpha:]_][[:word:]]* \\s* \\(\n )\n",
@@ -1407,6 +1459,7 @@
"include": "#special-names"
},
{
+ "name": "meta.indexed-name.python",
"match": "(?x)\n \\b ([[:alpha:]_]\\w*) \\b\n"
}
]
@@ -1524,6 +1577,7 @@
},
"function-call": {
"name": "meta.function-call.python",
+ "comment": "Regular function call of the type \"name(args)\"",
"begin": "(?x)\n \\b(?=\n ([[:alpha:]_]\\w*) \\s* (\\()\n )\n",
"end": "(\\))",
"endCaptures": {
diff --git a/extensions/python/test/colorize-results/test-freeze-56377_py.json b/extensions/python/test/colorize-results/test-freeze-56377_py.json
index 63889e26fee..5038c289809 100644
--- a/extensions/python/test/colorize-results/test-freeze-56377_py.json
+++ b/extensions/python/test/colorize-results/test-freeze-56377_py.json
@@ -254,13 +254,13 @@
},
{
"c": "in",
- "t": "source.python keyword.operator.logical.python",
+ "t": "source.python keyword.control.flow.python",
"r": {
- "dark_plus": "keyword.operator.logical.python: #569CD6",
- "light_plus": "keyword.operator.logical.python: #0000FF",
- "dark_vs": "keyword.operator.logical.python: #569CD6",
- "light_vs": "keyword.operator.logical.python: #0000FF",
- "hc_black": "keyword.operator.logical.python: #569CD6"
+ "dark_plus": "keyword.control: #C586C0",
+ "light_plus": "keyword.control: #AF00DB",
+ "dark_vs": "keyword.control: #569CD6",
+ "light_vs": "keyword.control: #0000FF",
+ "hc_black": "keyword.control: #C586C0"
}
},
{
@@ -298,7 +298,7 @@
},
{
"c": "request",
- "t": "source.python meta.member.access.python",
+ "t": "source.python meta.member.access.python meta.attribute.python",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
diff --git a/extensions/python/test/colorize-results/test_py.json b/extensions/python/test/colorize-results/test_py.json
index c61d2db7611..c0a14453646 100644
--- a/extensions/python/test/colorize-results/test_py.json
+++ b/extensions/python/test/colorize-results/test_py.json
@@ -430,7 +430,7 @@
},
{
"c": "size",
- "t": "source.python meta.member.access.python",
+ "t": "source.python meta.member.access.python meta.attribute.python",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
@@ -914,13 +914,13 @@
},
{
"c": "in",
- "t": "source.python meta.function.python meta.function.parameters.python keyword.operator.logical.python",
+ "t": "source.python meta.function.python meta.function.parameters.python keyword.control.flow.python",
"r": {
- "dark_plus": "keyword.operator.logical.python: #569CD6",
- "light_plus": "keyword.operator.logical.python: #0000FF",
- "dark_vs": "keyword.operator.logical.python: #569CD6",
- "light_vs": "keyword.operator.logical.python: #0000FF",
- "hc_black": "keyword.operator.logical.python: #569CD6"
+ "dark_plus": "keyword.control: #C586C0",
+ "light_plus": "keyword.control: #AF00DB",
+ "dark_vs": "keyword.control: #569CD6",
+ "light_vs": "keyword.control: #0000FF",
+ "hc_black": "keyword.control: #C586C0"
}
},
{
@@ -2113,13 +2113,13 @@
},
{
"c": "in",
- "t": "source.python keyword.operator.logical.python",
+ "t": "source.python keyword.control.flow.python",
"r": {
- "dark_plus": "keyword.operator.logical.python: #569CD6",
- "light_plus": "keyword.operator.logical.python: #0000FF",
- "dark_vs": "keyword.operator.logical.python: #569CD6",
- "light_vs": "keyword.operator.logical.python: #0000FF",
- "hc_black": "keyword.operator.logical.python: #569CD6"
+ "dark_plus": "keyword.control: #C586C0",
+ "light_plus": "keyword.control: #AF00DB",
+ "dark_vs": "keyword.control: #569CD6",
+ "light_vs": "keyword.control: #0000FF",
+ "hc_black": "keyword.control: #C586C0"
}
},
{
@@ -4544,7 +4544,7 @@
},
{
"c": "fn",
- "t": "source.python meta.member.access.python",
+ "t": "source.python meta.member.access.python meta.attribute.python",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
@@ -4621,7 +4621,7 @@
},
{
"c": "memo",
- "t": "source.python meta.member.access.python",
+ "t": "source.python meta.member.access.python meta.attribute.python",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
@@ -4918,7 +4918,7 @@
},
{
"c": "memo",
- "t": "source.python meta.member.access.python",
+ "t": "source.python meta.member.access.python meta.attribute.python",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
@@ -4973,7 +4973,7 @@
},
{
"c": "memo",
- "t": "source.python meta.member.access.python meta.item-access.python",
+ "t": "source.python meta.member.access.python meta.item-access.python meta.indexed-name.python",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
@@ -5182,7 +5182,7 @@
},
{
"c": "memo",
- "t": "source.python meta.member.access.python meta.item-access.python",
+ "t": "source.python meta.member.access.python meta.item-access.python meta.indexed-name.python",
"r": {
"dark_plus": "default: #D4D4D4",
"light_plus": "default: #000000",
@@ -6797,4 +6797,4 @@
"hc_black": "string: #CE9178"
}
}
-]
+]
\ No newline at end of file
diff --git a/extensions/r/cgmanifest.json b/extensions/r/cgmanifest.json
index 7cf785d7140..62f2751e6c9 100644
--- a/extensions/r/cgmanifest.json
+++ b/extensions/r/cgmanifest.json
@@ -6,11 +6,11 @@
"git": {
"name": "Ikuyadeu/vscode-R",
"repositoryUrl": "https://github.com/Ikuyadeu/vscode-R",
- "commitHash": "bc79e9245682ee09b4f0b742b927a37702d91b82"
+ "commitHash": "e03ba9cb9b19412f48c73ea73deb6746d50bbf23"
}
},
"license": "MIT",
- "version": "1.1.8"
+ "version": "1.3.0"
}
],
"version": 1
diff --git a/extensions/r/syntaxes/r.tmLanguage.json b/extensions/r/syntaxes/r.tmLanguage.json
index 2b6c4368da9..ad947f62849 100644
--- a/extensions/r/syntaxes/r.tmLanguage.json
+++ b/extensions/r/syntaxes/r.tmLanguage.json
@@ -4,7 +4,7 @@
"If you want to provide a fix or improvement, please create a pull request against the original repository.",
"Once accepted there, we are happy to receive an update request."
],
- "version": "https://github.com/Ikuyadeu/vscode-R/commit/bc79e9245682ee09b4f0b742b927a37702d91b82",
+ "version": "https://github.com/Ikuyadeu/vscode-R/commit/e03ba9cb9b19412f48c73ea73deb6746d50bbf23",
"name": "R",
"scopeName": "source.r",
"patterns": [
@@ -168,20 +168,12 @@
"match": "(\\-|\\+|\\*|\\/|%\\/%|%%|%\\*%|%o%|%x%|\\^)",
"name": "keyword.operator.arithmetic.r"
},
- {
- "match": "<=|>=",
- "name": "keyword.operator.comparison.r"
- },
- {
- "match": "==",
- "name": "keyword.operator.comarison.r"
- },
{
"match": "(:=|<-|<<-|->|->>)",
"name": "keyword.operator.assignment.r"
},
{
- "match": "(!=|<>|<|>|%in%)",
+ "match": "(==|<=|>=|!=|<>|<|>|%in%)",
"name": "keyword.operator.comparison.r"
},
{
diff --git a/extensions/search-result/package.json b/extensions/search-result/package.json
index 6a55968bda1..ffb5321ae4d 100644
--- a/extensions/search-result/package.json
+++ b/extensions/search-result/package.json
@@ -43,8 +43,5 @@
"path": "./syntaxes/searchResult.tmLanguage.json"
}
]
- },
- "devDependencies": {
- "vscode": "^1.1.36"
}
}
diff --git a/extensions/search-result/yarn.lock b/extensions/search-result/yarn.lock
index 4ff83ab98bb..fb57ccd13af 100644
--- a/extensions/search-result/yarn.lock
+++ b/extensions/search-result/yarn.lock
@@ -2,601 +2,3 @@
# yarn lockfile v1
-agent-base@4, agent-base@^4.3.0:
- version "4.3.0"
- resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee"
- integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==
- dependencies:
- es6-promisify "^5.0.0"
-
-ajv@^6.5.5:
- version "6.10.2"
- resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52"
- integrity sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==
- dependencies:
- fast-deep-equal "^2.0.1"
- fast-json-stable-stringify "^2.0.0"
- json-schema-traverse "^0.4.1"
- uri-js "^4.2.2"
-
-asn1@~0.2.3:
- version "0.2.4"
- resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136"
- integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==
- dependencies:
- safer-buffer "~2.1.0"
-
-assert-plus@1.0.0, assert-plus@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
- integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=
-
-asynckit@^0.4.0:
- version "0.4.0"
- resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
- integrity sha1-x57Zf380y48robyXkLzDZkdLS3k=
-
-aws-sign2@~0.7.0:
- version "0.7.0"
- resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
- integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=
-
-aws4@^1.8.0:
- version "1.8.0"
- resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f"
- integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==
-
-balanced-match@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
- integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
-
-bcrypt-pbkdf@^1.0.0:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"
- integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=
- dependencies:
- tweetnacl "^0.14.3"
-
-brace-expansion@^1.1.7:
- version "1.1.11"
- resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
- integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
- dependencies:
- balanced-match "^1.0.0"
- concat-map "0.0.1"
-
-browser-stdout@1.3.1:
- version "1.3.1"
- resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60"
- integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==
-
-buffer-from@^1.0.0:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
- integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
-
-caseless@~0.12.0:
- version "0.12.0"
- resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
- integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=
-
-combined-stream@^1.0.6, combined-stream@~1.0.6:
- version "1.0.8"
- resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
- integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
- dependencies:
- delayed-stream "~1.0.0"
-
-commander@2.15.1:
- version "2.15.1"
- resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f"
- integrity sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==
-
-concat-map@0.0.1:
- version "0.0.1"
- resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
- integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
-
-core-util-is@1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
- integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
-
-dashdash@^1.12.0:
- version "1.14.1"
- resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
- integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=
- dependencies:
- assert-plus "^1.0.0"
-
-debug@3.1.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
- integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==
- dependencies:
- ms "2.0.0"
-
-debug@^3.1.0:
- version "3.2.6"
- resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
- integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
- dependencies:
- ms "^2.1.1"
-
-delayed-stream@~1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
- integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
-
-diff@3.5.0:
- version "3.5.0"
- resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
- integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==
-
-ecc-jsbn@~0.1.1:
- version "0.1.2"
- resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9"
- integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=
- dependencies:
- jsbn "~0.1.0"
- safer-buffer "^2.1.0"
-
-es6-promise@^4.0.3:
- version "4.2.8"
- resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a"
- integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==
-
-es6-promisify@^5.0.0:
- version "5.0.0"
- resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203"
- integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=
- dependencies:
- es6-promise "^4.0.3"
-
-escape-string-regexp@1.0.5:
- version "1.0.5"
- resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
- integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
-
-extend@~3.0.2:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
- integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
-
-extsprintf@1.3.0:
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05"
- integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=
-
-extsprintf@^1.2.0:
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"
- integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=
-
-fast-deep-equal@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49"
- integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=
-
-fast-json-stable-stringify@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2"
- integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I=
-
-forever-agent@~0.6.1:
- version "0.6.1"
- resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
- integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=
-
-form-data@~2.3.2:
- version "2.3.3"
- resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"
- integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==
- dependencies:
- asynckit "^0.4.0"
- combined-stream "^1.0.6"
- mime-types "^2.1.12"
-
-fs.realpath@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
- integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
-
-getpass@^0.1.1:
- version "0.1.7"
- resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa"
- integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=
- dependencies:
- assert-plus "^1.0.0"
-
-glob@7.1.2:
- version "7.1.2"
- resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15"
- integrity sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==
- dependencies:
- fs.realpath "^1.0.0"
- inflight "^1.0.4"
- inherits "2"
- minimatch "^3.0.4"
- once "^1.3.0"
- path-is-absolute "^1.0.0"
-
-glob@^7.1.2:
- version "7.1.6"
- resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
- integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
- dependencies:
- fs.realpath "^1.0.0"
- inflight "^1.0.4"
- inherits "2"
- minimatch "^3.0.4"
- once "^1.3.0"
- path-is-absolute "^1.0.0"
-
-growl@1.10.5:
- version "1.10.5"
- resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e"
- integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==
-
-har-schema@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
- integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=
-
-har-validator@~5.1.0:
- version "5.1.3"
- resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080"
- integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==
- dependencies:
- ajv "^6.5.5"
- har-schema "^2.0.0"
-
-has-flag@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
- integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
-
-he@1.1.1:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd"
- integrity sha1-k0EP0hsAlzUVH4howvJx80J+I/0=
-
-http-proxy-agent@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405"
- integrity sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==
- dependencies:
- agent-base "4"
- debug "3.1.0"
-
-http-signature@~1.2.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
- integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=
- dependencies:
- assert-plus "^1.0.0"
- jsprim "^1.2.2"
- sshpk "^1.7.0"
-
-https-proxy-agent@^2.2.1:
- version "2.2.4"
- resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b"
- integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==
- dependencies:
- agent-base "^4.3.0"
- debug "^3.1.0"
-
-inflight@^1.0.4:
- version "1.0.6"
- resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
- integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
- dependencies:
- once "^1.3.0"
- wrappy "1"
-
-inherits@2:
- version "2.0.4"
- resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
- integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
-
-is-typedarray@~1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
- integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=
-
-isstream@~0.1.2:
- version "0.1.2"
- resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
- integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=
-
-jsbn@~0.1.0:
- version "0.1.1"
- resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
- integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM=
-
-json-schema-traverse@^0.4.1:
- version "0.4.1"
- resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
- integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
-
-json-schema@0.2.3:
- version "0.2.3"
- resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
- integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=
-
-json-stringify-safe@~5.0.1:
- version "5.0.1"
- resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
- integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
-
-jsprim@^1.2.2:
- version "1.4.1"
- resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2"
- integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=
- dependencies:
- assert-plus "1.0.0"
- extsprintf "1.3.0"
- json-schema "0.2.3"
- verror "1.10.0"
-
-mime-db@1.40.0:
- version "1.40.0"
- resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.40.0.tgz#a65057e998db090f732a68f6c276d387d4126c32"
- integrity sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==
-
-mime-types@^2.1.12, mime-types@~2.1.19:
- version "2.1.24"
- resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.24.tgz#b6f8d0b3e951efb77dedeca194cff6d16f676f81"
- integrity sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==
- dependencies:
- mime-db "1.40.0"
-
-minimatch@3.0.4, minimatch@^3.0.4:
- version "3.0.4"
- resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
- integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
- dependencies:
- brace-expansion "^1.1.7"
-
-minimist@0.0.8:
- version "0.0.8"
- resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
- integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=
-
-mkdirp@0.5.1:
- version "0.5.1"
- resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
- integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=
- dependencies:
- minimist "0.0.8"
-
-mocha@^5.2.0:
- version "5.2.0"
- resolved "https://registry.yarnpkg.com/mocha/-/mocha-5.2.0.tgz#6d8ae508f59167f940f2b5b3c4a612ae50c90ae6"
- integrity sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==
- dependencies:
- browser-stdout "1.3.1"
- commander "2.15.1"
- debug "3.1.0"
- diff "3.5.0"
- escape-string-regexp "1.0.5"
- glob "7.1.2"
- growl "1.10.5"
- he "1.1.1"
- minimatch "3.0.4"
- mkdirp "0.5.1"
- supports-color "5.4.0"
-
-ms@2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
- integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
-
-ms@^2.1.1:
- version "2.1.2"
- resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
- integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
-
-oauth-sign@~0.9.0:
- version "0.9.0"
- resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
- integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==
-
-once@^1.3.0:
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
- integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
- dependencies:
- wrappy "1"
-
-path-is-absolute@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
- integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
-
-performance-now@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
- integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
-
-psl@^1.1.24:
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/psl/-/psl-1.4.0.tgz#5dd26156cdb69fa1fdb8ab1991667d3f80ced7c2"
- integrity sha512-HZzqCGPecFLyoRj5HLfuDSKYTJkAfB5thKBIkRHtGjWwY7p1dAyveIbXIq4tO0KYfDF2tHqPUgY9SDnGm00uFw==
-
-punycode@^1.4.1:
- version "1.4.1"
- resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
- integrity sha1-wNWmOycYgArY4esPpSachN1BhF4=
-
-punycode@^2.1.0:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
- integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
-
-qs@~6.5.2:
- version "6.5.2"
- resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
- integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
-
-querystringify@^2.1.1:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e"
- integrity sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==
-
-request@^2.88.0:
- version "2.88.0"
- resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef"
- integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==
- dependencies:
- aws-sign2 "~0.7.0"
- aws4 "^1.8.0"
- caseless "~0.12.0"
- combined-stream "~1.0.6"
- extend "~3.0.2"
- forever-agent "~0.6.1"
- form-data "~2.3.2"
- har-validator "~5.1.0"
- http-signature "~1.2.0"
- is-typedarray "~1.0.0"
- isstream "~0.1.2"
- json-stringify-safe "~5.0.1"
- mime-types "~2.1.19"
- oauth-sign "~0.9.0"
- performance-now "^2.1.0"
- qs "~6.5.2"
- safe-buffer "^5.1.2"
- tough-cookie "~2.4.3"
- tunnel-agent "^0.6.0"
- uuid "^3.3.2"
-
-requires-port@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
- integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=
-
-safe-buffer@^5.0.1, safe-buffer@^5.1.2:
- version "5.2.0"
- resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519"
- integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==
-
-safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
- version "2.1.2"
- resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
- integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
-
-semver@^5.4.1:
- version "5.7.1"
- resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
- integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
-
-source-map-support@^0.5.0:
- version "0.5.16"
- resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042"
- integrity sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==
- dependencies:
- buffer-from "^1.0.0"
- source-map "^0.6.0"
-
-source-map@^0.6.0:
- version "0.6.1"
- resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
- integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
-
-sshpk@^1.7.0:
- version "1.16.1"
- resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877"
- integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==
- dependencies:
- asn1 "~0.2.3"
- assert-plus "^1.0.0"
- bcrypt-pbkdf "^1.0.0"
- dashdash "^1.12.0"
- ecc-jsbn "~0.1.1"
- getpass "^0.1.1"
- jsbn "~0.1.0"
- safer-buffer "^2.0.2"
- tweetnacl "~0.14.0"
-
-supports-color@5.4.0:
- version "5.4.0"
- resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54"
- integrity sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==
- dependencies:
- has-flag "^3.0.0"
-
-tough-cookie@~2.4.3:
- version "2.4.3"
- resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781"
- integrity sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==
- dependencies:
- psl "^1.1.24"
- punycode "^1.4.1"
-
-tunnel-agent@^0.6.0:
- version "0.6.0"
- resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
- integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=
- dependencies:
- safe-buffer "^5.0.1"
-
-tweetnacl@^0.14.3, tweetnacl@~0.14.0:
- version "0.14.5"
- resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
- integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=
-
-uri-js@^4.2.2:
- version "4.2.2"
- resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0"
- integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==
- dependencies:
- punycode "^2.1.0"
-
-url-parse@^1.4.4:
- version "1.4.7"
- resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278"
- integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==
- dependencies:
- querystringify "^2.1.1"
- requires-port "^1.0.0"
-
-uuid@^3.3.2:
- version "3.3.3"
- resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.3.tgz#4568f0216e78760ee1dbf3a4d2cf53e224112866"
- integrity sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==
-
-verror@1.10.0:
- version "1.10.0"
- resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400"
- integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=
- dependencies:
- assert-plus "^1.0.0"
- core-util-is "1.0.2"
- extsprintf "^1.2.0"
-
-vscode-test@^0.4.1:
- version "0.4.3"
- resolved "https://registry.yarnpkg.com/vscode-test/-/vscode-test-0.4.3.tgz#461ebf25fc4bc93d77d982aed556658a2e2b90b8"
- integrity sha512-EkMGqBSefZH2MgW65nY05rdRSko15uvzq4VAPM5jVmwYuFQKE7eikKXNJDRxL+OITXHB6pI+a3XqqD32Y3KC5w==
- dependencies:
- http-proxy-agent "^2.1.0"
- https-proxy-agent "^2.2.1"
-
-vscode@^1.1.36:
- version "1.1.36"
- resolved "https://registry.yarnpkg.com/vscode/-/vscode-1.1.36.tgz#5e1a0d1bf4977d0c7bc5159a9a13d5b104d4b1b6"
- integrity sha512-cGFh9jmGLcTapCpPCKvn8aG/j9zVQ+0x5hzYJq5h5YyUXVGa1iamOaB2M2PZXoumQPES4qeAP1FwkI0b6tL4bQ==
- dependencies:
- glob "^7.1.2"
- mocha "^5.2.0"
- request "^2.88.0"
- semver "^5.4.1"
- source-map-support "^0.5.0"
- url-parse "^1.4.4"
- vscode-test "^0.4.1"
-
-wrappy@1:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
- integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
diff --git a/extensions/sql/cgmanifest.json b/extensions/sql/cgmanifest.json
index 859d6782240..56b576cdc79 100644
--- a/extensions/sql/cgmanifest.json
+++ b/extensions/sql/cgmanifest.json
@@ -6,7 +6,7 @@
"git": {
"name": "Microsoft/vscode-mssql",
"repositoryUrl": "https://github.com/Microsoft/vscode-mssql",
- "commitHash": "a542fe96780e6b274adb281810d419a512fb5bb4"
+ "commitHash": "37a22725186b5b481b2882a78c7b9fe024c13946"
}
},
"license": "MIT",
diff --git a/extensions/sql/syntaxes/sql.tmLanguage.json b/extensions/sql/syntaxes/sql.tmLanguage.json
index d4173da5b23..778c35071fa 100644
--- a/extensions/sql/syntaxes/sql.tmLanguage.json
+++ b/extensions/sql/syntaxes/sql.tmLanguage.json
@@ -4,7 +4,7 @@
"If you want to provide a fix or improvement, please create a pull request against the original repository.",
"Once accepted there, we are happy to receive an update request."
],
- "version": "https://github.com/Microsoft/vscode-mssql/commit/a542fe96780e6b274adb281810d419a512fb5bb4",
+ "version": "https://github.com/Microsoft/vscode-mssql/commit/37a22725186b5b481b2882a78c7b9fe024c13946",
"name": "SQL",
"scopeName": "source.sql",
"patterns": [
diff --git a/extensions/typescript-language-features/src/features/completions.ts b/extensions/typescript-language-features/src/features/completions.ts
index df7f7ea9a5b..97051395de9 100644
--- a/extensions/typescript-language-features/src/features/completions.ts
+++ b/extensions/typescript-language-features/src/features/completions.ts
@@ -380,7 +380,6 @@ interface CompletionConfiguration {
readonly nameSuggestions: boolean;
readonly pathSuggestions: boolean;
readonly autoImportSuggestions: boolean;
- readonly includeAutomaticOptionalChainCompletions: boolean;
}
namespace CompletionConfiguration {
@@ -388,7 +387,6 @@ namespace CompletionConfiguration {
export const nameSuggestions = 'suggest.names';
export const pathSuggestions = 'suggest.paths';
export const autoImportSuggestions = 'suggest.autoImports';
- export const includeAutomaticOptionalChainCompletions = 'suggest.includeAutomaticOptionalChainCompletions';
export function getConfigurationForResource(
modeId: string,
@@ -400,12 +398,11 @@ namespace CompletionConfiguration {
pathSuggestions: config.get(CompletionConfiguration.pathSuggestions, true),
autoImportSuggestions: config.get(CompletionConfiguration.autoImportSuggestions, true),
nameSuggestions: config.get(CompletionConfiguration.nameSuggestions, true),
- includeAutomaticOptionalChainCompletions: config.get(CompletionConfiguration.includeAutomaticOptionalChainCompletions, true),
};
}
}
-class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider {
+class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider {
public static readonly triggerCharacters = ['.', '"', '\'', '`', '/', '@', '<', '#'];
@@ -428,9 +425,9 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider
position: vscode.Position,
token: vscode.CancellationToken,
context: vscode.CompletionContext
- ): Promise {
+ ): Promise | null> {
if (this.typingsStatus.isAcquiringTypings) {
- return Promise.reject({
+ return Promise.reject>({
label: localize(
{ key: 'acquiringTypingsLabel', comment: ['Typings refers to the *.d.ts typings files that power our IntelliSense. It should not be localized'] },
'Acquiring typings...'),
@@ -456,12 +453,11 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider
await this.client.interruptGetErr(() => this.fileConfigurationManager.ensureConfigurationForDocument(document, token));
- const args: Proto.CompletionsRequestArgs & { includeAutomaticOptionalChainCompletions?: boolean } = {
+ const args: Proto.CompletionsRequestArgs = {
...typeConverters.Position.toFileLocationRequestArgs(file, position),
includeExternalModuleExports: completionConfiguration.autoImportSuggestions,
includeInsertTextCompletions: true,
triggerCharacter: this.getTsTriggerCharacter(context),
- includeAutomaticOptionalChainCompletions: completionConfiguration.includeAutomaticOptionalChainCompletions,
};
let isNewIdentifierLocation = true;
@@ -535,7 +531,7 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider
useFuzzyWordRangeLogic: this.client.apiVersion.lt(API.v390),
};
- const items: vscode.CompletionItem[] = [];
+ const items: MyCompletionItem[] = [];
for (let entry of entries) {
if (!shouldExcludeCompletionEntry(entry, completionConfiguration)) {
items.push(new MyCompletionItem(position, document, entry, completionContext, metadata));
@@ -565,13 +561,9 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider
}
public async resolveCompletionItem(
- item: vscode.CompletionItem,
+ item: MyCompletionItem,
token: vscode.CancellationToken
- ): Promise {
- if (!(item instanceof MyCompletionItem)) {
- return undefined;
- }
-
+ ): Promise {
const filepath = this.client.toOpenedFilePath(item.document);
if (!filepath) {
return undefined;
diff --git a/extensions/typescript-language-features/src/features/fileConfigurationManager.ts b/extensions/typescript-language-features/src/features/fileConfigurationManager.ts
index 686c35e18ed..0d54ccea22a 100644
--- a/extensions/typescript-language-features/src/features/fileConfigurationManager.ts
+++ b/extensions/typescript-language-features/src/features/fileConfigurationManager.ts
@@ -10,29 +10,16 @@ import API from '../utils/api';
import { Disposable } from '../utils/dispose';
import * as fileSchemes from '../utils/fileSchemes';
import { isTypeScriptDocument } from '../utils/languageModeIds';
+import { equals } from '../utils/objects';
import { ResourceMap } from '../utils/resourceMap';
-
-function objsAreEqual(a: T, b: T): boolean {
- let keys = Object.keys(a);
- for (const key of keys) {
- if ((a as any)[key] !== (b as any)[key]) {
- return false;
- }
- }
- return true;
-}
-
interface FileConfiguration {
readonly formatOptions: Proto.FormatCodeSettings;
readonly preferences: Proto.UserPreferences;
}
function areFileConfigurationsEqual(a: FileConfiguration, b: FileConfiguration): boolean {
- return (
- objsAreEqual(a.formatOptions, b.formatOptions)
- && objsAreEqual(a.preferences, b.preferences)
- );
+ return equals(a, b);
}
export default class FileConfigurationManager extends Disposable {
@@ -176,16 +163,21 @@ export default class FileConfigurationManager extends Disposable {
}
const config = vscode.workspace.getConfiguration(
+ isTypeScriptDocument(document) ? 'typescript' : 'javascript',
+ document.uri);
+
+ const preferencesConfig = vscode.workspace.getConfiguration(
isTypeScriptDocument(document) ? 'typescript.preferences' : 'javascript.preferences',
document.uri);
const preferences: Proto.UserPreferences = {
- quotePreference: this.getQuoteStylePreference(config),
- importModuleSpecifierPreference: getImportModuleSpecifierPreference(config),
- importModuleSpecifierEnding: getImportModuleSpecifierEndingPreference(config),
+ quotePreference: this.getQuoteStylePreference(preferencesConfig),
+ importModuleSpecifierPreference: getImportModuleSpecifierPreference(preferencesConfig),
+ importModuleSpecifierEnding: getImportModuleSpecifierEndingPreference(preferencesConfig),
allowTextChangesInNewFiles: document.uri.scheme === fileSchemes.file,
- providePrefixAndSuffixTextForRename: config.get('renameShorthandProperties', true) === false ? false : config.get('useAliasesForRenames', true),
+ providePrefixAndSuffixTextForRename: preferencesConfig.get('renameShorthandProperties', true) === false ? false : preferencesConfig.get('useAliasesForRenames', true),
allowRenameOfImportPath: true,
+ includeAutomaticOptionalChainCompletions: config.get('suggest.includeAutomaticOptionalChainCompletions', true),
};
return preferences;
diff --git a/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts b/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts
index 1e0d59f1057..0be49c3113b 100644
--- a/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts
+++ b/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts
@@ -18,6 +18,7 @@ import TypeScriptServiceClient from './typescriptServiceClient';
import { coalesce, flatten } from './utils/arrays';
import { CommandManager } from './utils/commandManager';
import { Disposable } from './utils/dispose';
+import * as errorCodes from './utils/errorCodes';
import { DiagnosticLanguage, LanguageDescription } from './utils/languageDescription';
import LogDirectoryProvider from './utils/logDirectoryProvider';
import { PluginManager } from './utils/plugins';
@@ -27,13 +28,13 @@ import VersionStatus from './utils/versionStatus';
// Style check diagnostics that can be reported as warnings
const styleCheckDiagnostics = [
- 6133, // variable is declared but never used
- 6138, // property is declared but its value is never read
- 6192, // All imports are unused
- 7027, // unreachable code detected
- 7028, // unused label
- 7029, // fall through case in switch
- 7030 // not all code paths return a value
+ errorCodes.variableDeclaredButNeverUsed,
+ errorCodes.propertyDeclaretedButNeverUsed,
+ errorCodes.allImportsAreUnused,
+ errorCodes.unreachableCode,
+ errorCodes.unusedLabel,
+ errorCodes.fallThroughCaseInSwitch,
+ errorCodes.notAllCodePathsReturnAValue,
];
export default class TypeScriptServiceClientHost extends Disposable {
@@ -97,23 +98,31 @@ export default class TypeScriptServiceClientHost extends Disposable {
this.client.onReady(() => {
const languages = new Set();
for (const plugin of pluginManager.plugins) {
- for (const language of plugin.languages) {
- languages.add(language);
+ if (plugin.configNamespace && plugin.languages.length) {
+ this.registerExtensionLanguageProvider({
+ id: plugin.configNamespace,
+ modeIds: Array.from(plugin.languages),
+ diagnosticSource: 'ts-plugin',
+ diagnosticLanguage: DiagnosticLanguage.TypeScript,
+ diagnosticOwner: 'typescript',
+ isExternal: true
+ }, onCompletionAccepted);
+ } else {
+ for (const language of plugin.languages) {
+ languages.add(language);
+ }
}
}
+
if (languages.size) {
- const description: LanguageDescription = {
+ this.registerExtensionLanguageProvider({
id: 'typescript-plugins',
modeIds: Array.from(languages.values()),
diagnosticSource: 'ts-plugin',
diagnosticLanguage: DiagnosticLanguage.TypeScript,
diagnosticOwner: 'typescript',
isExternal: true
- };
- const manager = new LanguageProvider(this.client, description, this.commandManager, this.client.telemetryReporter, this.typingsStatus, this.fileConfigurationManager, onCompletionAccepted);
- this.languages.push(manager);
- this._register(manager);
- this.languagePerId.set(description.id, manager);
+ }, onCompletionAccepted);
}
});
@@ -125,6 +134,13 @@ export default class TypeScriptServiceClientHost extends Disposable {
this.configurationChanged();
}
+ private registerExtensionLanguageProvider(description: LanguageDescription, onCompletionAccepted: (item: vscode.CompletionItem) => void) {
+ const manager = new LanguageProvider(this.client, description, this.commandManager, this.client.telemetryReporter, this.typingsStatus, this.fileConfigurationManager, onCompletionAccepted);
+ this.languages.push(manager);
+ this._register(manager);
+ this.languagePerId.set(description.id, manager);
+ }
+
private getAllModeIds(descriptions: LanguageDescription[], pluginManager: PluginManager) {
const allModeIds = flatten([
...descriptions.map(x => x.modeIds),
diff --git a/extensions/typescript-language-features/src/utils/errorCodes.ts b/extensions/typescript-language-features/src/utils/errorCodes.ts
new file mode 100644
index 00000000000..4407b3a054b
--- /dev/null
+++ b/extensions/typescript-language-features/src/utils/errorCodes.ts
@@ -0,0 +1,12 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+export const variableDeclaredButNeverUsed = 6133;
+export const propertyDeclaretedButNeverUsed = 6138;
+export const allImportsAreUnused = 6192;
+export const unreachableCode = 7027;
+export const unusedLabel = 7028;
+export const fallThroughCaseInSwitch = 7029;
+export const notAllCodePathsReturnAValue = 7030;
diff --git a/extensions/typescript-language-features/src/utils/plugins.ts b/extensions/typescript-language-features/src/utils/plugins.ts
index 71ce2430cb8..6d0708b15db 100644
--- a/extensions/typescript-language-features/src/utils/plugins.ts
+++ b/extensions/typescript-language-features/src/utils/plugins.ts
@@ -12,6 +12,7 @@ export interface TypeScriptServerPlugin {
readonly name: string;
readonly enableForWorkspaceTypeScriptVersions: boolean;
readonly languages: ReadonlyArray;
+ readonly configNamespace?: string
}
namespace TypeScriptServerPlugin {
@@ -77,6 +78,7 @@ export class PluginManager extends Disposable {
enableForWorkspaceTypeScriptVersions: !!plugin.enableForWorkspaceTypeScriptVersions,
path: extension.extensionPath,
languages: Array.isArray(plugin.languages) ? plugin.languages : [],
+ configNamespace: plugin.configNamespace,
});
}
if (plugins.length) {
@@ -86,4 +88,4 @@ export class PluginManager extends Disposable {
}
return pluginMap;
}
-}
\ No newline at end of file
+}
diff --git a/extensions/vscode-account/package.json b/extensions/vscode-account/package.json
index 0deacbea46b..6466cc59663 100644
--- a/extensions/vscode-account/package.json
+++ b/extensions/vscode-account/package.json
@@ -16,21 +16,6 @@
],
"aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217",
"main": "./out/extension.js",
- "contributes": {
- "configuration": {
- "title": "Microsoft Account",
- "properties": {
- "microsoftAccount.logLevel": {
- "type": "string",
- "enum": [
- "info",
- "trace"
- ],
- "default": "info"
- }
- }
- }
- },
"scripts": {
"vscode:prepublish": "npm run compile",
"compile": "gulp compile-extension:vscode-account",
diff --git a/extensions/vscode-account/src/extension.ts b/extensions/vscode-account/src/extension.ts
index 0f0b33b96ec..cc2954e05a6 100644
--- a/extensions/vscode-account/src/extension.ts
+++ b/extensions/vscode-account/src/extension.ts
@@ -20,6 +20,7 @@ export async function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(vscode.authentication.registerAuthenticationProvider({
id: 'microsoft',
displayName: 'Microsoft',
+ supportsMultipleAccounts: true,
onDidChangeSessions: onDidChangeSessions.event,
getSessions: () => Promise.resolve(loginService.sessions),
login: async (scopes: string[]) => {
diff --git a/extensions/vscode-account/src/keychain.ts b/extensions/vscode-account/src/keychain.ts
index eb9f54acab3..afda7035521 100644
--- a/extensions/vscode-account/src/keychain.ts
+++ b/extensions/vscode-account/src/keychain.ts
@@ -62,7 +62,6 @@ export class Keychain {
async setToken(token: string): Promise {
try {
- Logger.trace('Writing to keychain', token);
return await this.keytar.setPassword(SERVICE_ID, ACCOUNT_ID, token);
} catch (e) {
Logger.error(`Setting token failed: ${e}`);
@@ -85,9 +84,7 @@ export class Keychain {
async getToken(): Promise {
try {
- const result = await this.keytar.getPassword(SERVICE_ID, ACCOUNT_ID);
- Logger.trace('Reading from keychain', result);
- return result;
+ return await this.keytar.getPassword(SERVICE_ID, ACCOUNT_ID);
} catch (e) {
// Ignore
Logger.error(`Getting token failed: ${e}`);
diff --git a/extensions/vscode-account/src/logger.ts b/extensions/vscode-account/src/logger.ts
index d982ee69f0c..b9ed27bd993 100644
--- a/extensions/vscode-account/src/logger.ts
+++ b/extensions/vscode-account/src/logger.ts
@@ -5,25 +5,13 @@
import * as vscode from 'vscode';
-type LogLevel = 'Trace' | 'Info' | 'Error';
-
-enum Level {
- Trace = 'trace',
- Info = 'Info'
-}
+type LogLevel = 'Info' | 'Error';
class Log {
private output: vscode.OutputChannel;
- private level: Level;
constructor() {
this.output = vscode.window.createOutputChannel('Microsoft Authentication');
- this.level = vscode.workspace.getConfiguration('microsoftAccount').get('logLevel') || Level.Info;
- vscode.workspace.onDidChangeConfiguration(e => {
- if (e.affectsConfiguration('microsoftAccount.logLevel')) {
- this.level = vscode.workspace.getConfiguration('microsoftAccount').get('logLevel') || Level.Info;
- }
- });
}
private data2String(data: any): string {
@@ -44,12 +32,6 @@ class Log {
this.logLevel('Error', message, data);
}
- public trace(message: string, data?: any): void {
- if (this.level === Level.Trace) {
- this.logLevel('Trace', message, data);
- }
- }
-
public logLevel(level: LogLevel, message: string, data?: any): void {
this.output.appendLine(`[${level} - ${this.now()}] ${message}`);
if (data) {
diff --git a/extensions/vscode-notebook-tests/src/notebook.test.ts b/extensions/vscode-notebook-tests/src/notebook.test.ts
index e4eba9aefb3..0de2917cf3f 100644
--- a/extensions/vscode-notebook-tests/src/notebook.test.ts
+++ b/extensions/vscode-notebook-tests/src/notebook.test.ts
@@ -212,7 +212,7 @@ suite('notebook dirty state', () => {
await vscode.commands.executeCommand('default:type', { text: 'var abc = 0;' });
- // await vscode.commands.executeCommand('workbench.action.files.save');
+ await vscode.commands.executeCommand('workbench.action.files.newUntitledFile');
await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
@@ -272,3 +272,51 @@ suite('notebook undo redo', () => {
await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
});
});
+
+suite('notebook working copy', () => {
+ test('notebook revert on close', async function () {
+ const resource = vscode.Uri.parse(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
+ await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
+
+ await waitFor(500);
+ await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
+ assert.equal(vscode.notebook.activeNotebookEditor!.selection?.source, '');
+
+ await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove');
+ await vscode.commands.executeCommand('default:type', { text: 'var abc = 0;' });
+
+ // close active editor from command will revert the file
+ await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
+ await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
+
+ await waitFor(500);
+ assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true);
+ assert.equal(vscode.notebook.activeNotebookEditor?.selection !== undefined, true);
+ assert.deepEqual(vscode.notebook.activeNotebookEditor?.document.cells[0], vscode.notebook.activeNotebookEditor?.selection);
+ assert.equal(vscode.notebook.activeNotebookEditor?.selection?.source, 'test');
+
+ await vscode.commands.executeCommand('workbench.action.files.save');
+ await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
+ });
+
+ test('notebook revert', async function () {
+ const resource = vscode.Uri.parse(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
+ await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
+
+ await waitFor(500);
+ await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
+ assert.equal(vscode.notebook.activeNotebookEditor!.selection?.source, '');
+
+ await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove');
+ await vscode.commands.executeCommand('default:type', { text: 'var abc = 0;' });
+ await vscode.commands.executeCommand('workbench.action.files.revert');
+
+ assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true);
+ assert.equal(vscode.notebook.activeNotebookEditor?.selection !== undefined, true);
+ assert.deepEqual(vscode.notebook.activeNotebookEditor?.document.cells[0], vscode.notebook.activeNotebookEditor?.selection);
+ assert.deepEqual(vscode.notebook.activeNotebookEditor?.document.cells.length, 1);
+ assert.equal(vscode.notebook.activeNotebookEditor?.selection?.source, 'test');
+
+ await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
+ });
+});
diff --git a/extensions/vscode-notebook-tests/src/notebookTestMain.ts b/extensions/vscode-notebook-tests/src/notebookTestMain.ts
index b27f6760475..4281ba002fd 100644
--- a/extensions/vscode-notebook-tests/src/notebookTestMain.ts
+++ b/extensions/vscode-notebook-tests/src/notebookTestMain.ts
@@ -6,12 +6,22 @@
import * as vscode from 'vscode';
export function activate(context: vscode.ExtensionContext): any {
- context.subscriptions.push(vscode.notebook.registerNotebookProvider('notebookCoreTest', {
- resolveNotebook: async (editor: vscode.NotebookEditor) => {
- await editor.edit(eb => {
- eb.insert(0, 'test', 'typescript', vscode.CellKind.Code, [], {});
- });
- return;
+ context.subscriptions.push(vscode.notebook.registerNotebookContentProvider('notebookCoreTest', {
+ onDidChangeNotebook: new vscode.EventEmitter().event,
+ openNotebook: async (_resource: vscode.Uri) => {
+ return {
+ languages: ['typescript'],
+ metadata: {},
+ cells: [
+ {
+ source: 'test',
+ language: 'typescript',
+ cellKind: vscode.CellKind.Code,
+ outputs: [],
+ metadata: {}
+ }
+ ]
+ };
},
executeCell: async (_document: vscode.NotebookDocument, _cell: vscode.NotebookCell | undefined, _token: vscode.CancellationToken) => {
if (!_cell) {
@@ -26,8 +36,11 @@ export function activate(context: vscode.ExtensionContext): any {
}];
return;
},
- save: async (_document: vscode.NotebookDocument) => {
- return true;
+ saveNotebook: async (_document: vscode.NotebookDocument, _cancellation: vscode.CancellationToken) => {
+ return;
+ },
+ saveNotebookAs: async (_targetResource: vscode.Uri, _document: vscode.NotebookDocument, _cancellation: vscode.CancellationToken) => {
+ return;
}
}));
}
diff --git a/extensions/yarn.lock b/extensions/yarn.lock
index 0a5236436a5..4e0ca82acd1 100644
--- a/extensions/yarn.lock
+++ b/extensions/yarn.lock
@@ -2,7 +2,7 @@
# yarn lockfile v1
-typescript@3.9.1-rc:
- version "3.9.1-rc"
- resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.1-rc.tgz#81d5a5a0a597e224b6e2af8dffb46524b2eaf5f3"
- integrity sha512-+cPv8L2Vd4KidCotqi2wjegBZ5n47CDRUu/QiLVu2YbeXAz78hIfcai9ziBiNI6JTGTVwUqXRug2UZxDcxhvFw==
+typescript@^3.9.2-insiders.20200509:
+ version "3.9.2-insiders.20200509"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.2-insiders.20200509.tgz#8c90ed86a91f9692f10f5ac9c1fd6cb241419e6c"
+ integrity sha512-AAbhs55BZMbyHGfJd0pNfO3+B6jjPpa38zgaIb9MRExkRGLkIUpbUetoh+HgmM5LAtg128sHGiwhLc49pOcgFw==
diff --git a/package.json b/package.json
index 4b4ea6e65d7..61e48f8384f 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "code-oss-dev",
"version": "1.46.0",
- "distro": "ce946b3269a7188e873f31dd642911089c3e03e7",
+ "distro": "6ec59b3610ac45e47ab420d15a0588817178a043",
"author": {
"name": "Microsoft Corporation"
},
@@ -147,7 +147,7 @@
"opn": "^6.0.0",
"optimist": "0.3.5",
"p-all": "^1.0.0",
- "playwright": "0.15.0",
+ "playwright": "1.0.1",
"pump": "^1.0.1",
"queue": "3.0.6",
"rcedit": "^1.1.0",
diff --git a/src/main.js b/src/main.js
index 9309c12a747..23915126b8f 100644
--- a/src/main.js
+++ b/src/main.js
@@ -35,6 +35,9 @@ app.setPath('userData', userDataPath);
// Set temp directory based on crash-reporter-directory CLI argument
// The crash reporter will store crashes in temp folder so we need
// to change that location accordingly.
+
+// If a crash-reporter-directory is specified we setup the crash reporter
+// right from the beginning as early as possible to monitor all processes.
let crashReporterDirectory = args['crash-reporter-directory'];
if (crashReporterDirectory) {
crashReporterDirectory = path.normalize(crashReporterDirectory);
@@ -47,8 +50,20 @@ if (crashReporterDirectory) {
app.exit(1);
}
}
+
+ // Crashes are stored in the temp directory by default, so we
+ // need to change that directory to the provided one
console.log(`Found --crash-reporter-directory argument. Setting temp directory to be '${crashReporterDirectory}'`);
app.setPath('temp', crashReporterDirectory);
+
+ // Start crash reporter
+ const { crashReporter } = require('electron');
+ crashReporter.start({
+ companyName: 'Microsoft',
+ productName: product.nameShort,
+ submitURL: '',
+ uploadToServer: false
+ });
}
// Set logs path before app 'ready' event if running portable
diff --git a/src/vs/base/browser/ui/countBadge/countBadge.css b/src/vs/base/browser/ui/countBadge/countBadge.css
index db90e7ae552..ce26af5e6a1 100644
--- a/src/vs/base/browser/ui/countBadge/countBadge.css
+++ b/src/vs/base/browser/ui/countBadge/countBadge.css
@@ -15,3 +15,8 @@
display: inline-block;
box-sizing: border-box;
}
+
+.monaco-count-badge.long {
+ padding: 2px 3px;
+ border-radius: 2px;
+}
diff --git a/src/vs/base/browser/ui/dropdown/dropdown.ts b/src/vs/base/browser/ui/dropdown/dropdown.ts
index 5f17647363c..99c11cdd6e2 100644
--- a/src/vs/base/browser/ui/dropdown/dropdown.ts
+++ b/src/vs/base/browser/ui/dropdown/dropdown.ts
@@ -14,6 +14,7 @@ import { ResolvedKeybinding, KeyCode } from 'vs/base/common/keyCodes';
import { EventHelper, EventType, removeClass, addClass, append, $, addDisposableListener, addClasses } from 'vs/base/browser/dom';
import { IContextMenuDelegate } from 'vs/base/browser/contextmenu';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
+import { Emitter } from 'vs/base/common/event';
export interface ILabelRenderer {
(container: HTMLElement): IDisposable | null;
@@ -29,7 +30,10 @@ export class BaseDropdown extends ActionRunner {
private boxContainer?: HTMLElement;
private _label?: HTMLElement;
private contents?: HTMLElement;
+
private visible: boolean | undefined;
+ private _onDidChangeVisibility = new Emitter();
+ readonly onDidChangeVisibility = this._onDidChangeVisibility.event;
constructor(container: HTMLElement, options: IBaseDropdownOptions) {
super();
@@ -101,11 +105,17 @@ export class BaseDropdown extends ActionRunner {
}
show(): void {
- this.visible = true;
+ if (!this.visible) {
+ this.visible = true;
+ this._onDidChangeVisibility.fire(true);
+ }
}
hide(): void {
- this.visible = false;
+ if (this.visible) {
+ this.visible = false;
+ this._onDidChangeVisibility.fire(false);
+ }
}
isVisible(): boolean {
@@ -303,6 +313,7 @@ export class DropdownMenuActionViewItem extends BaseActionViewItem {
this.element.tabIndex = 0;
this.element.setAttribute('role', 'button');
this.element.setAttribute('aria-haspopup', 'true');
+ this.element.setAttribute('aria-expanded', 'false');
this.element.title = this._action.label || '';
return null;
@@ -321,6 +332,7 @@ export class DropdownMenuActionViewItem extends BaseActionViewItem {
}
this.dropdownMenu = this._register(new DropdownMenu(container, options));
+ this._register(this.dropdownMenu.onDidChangeVisibility(visible => this.element?.setAttribute('aria-expanded', `${visible}`)));
this.dropdownMenu.menuOptions = {
actionViewItemProvider: this.actionViewItemProvider,
diff --git a/src/vs/base/browser/ui/list/listPaging.ts b/src/vs/base/browser/ui/list/listPaging.ts
index d0e5097f9e6..508fb3dcabd 100644
--- a/src/vs/base/browser/ui/list/listPaging.ts
+++ b/src/vs/base/browser/ui/list/listPaging.ts
@@ -165,7 +165,7 @@ export class PagedList implements IDisposable {
}
get onDidChangeFocus(): Event> {
- return Event.map(this.list.onDidChangeFocus, ({ elements, indexes }) => ({ elements: elements.map(e => this._model.get(e)), indexes }));
+ return Event.map(this.list.onDidChangeFocus, ({ elements, indexes, browserEvent }) => ({ elements: elements.map(e => this._model.get(e)), indexes, browserEvent }));
}
get onDidOpen(): Event> {
@@ -173,11 +173,11 @@ export class PagedList implements IDisposable {
}
get onDidChangeSelection(): Event> {
- return Event.map(this.list.onDidChangeSelection, ({ elements, indexes }) => ({ elements: elements.map(e => this._model.get(e)), indexes }));
+ return Event.map(this.list.onDidChangeSelection, ({ elements, indexes, browserEvent }) => ({ elements: elements.map(e => this._model.get(e)), indexes, browserEvent }));
}
get onPin(): Event> {
- return Event.map(this.list.onDidPin, ({ elements, indexes }) => ({ elements: elements.map(e => this._model.get(e)), indexes }));
+ return Event.map(this.list.onDidPin, ({ elements, indexes, browserEvent }) => ({ elements: elements.map(e => this._model.get(e)), indexes, browserEvent }));
}
get onContextMenu(): Event> {
diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts
index 8c9b875936e..b271eea461b 100644
--- a/src/vs/base/browser/ui/list/listWidget.ts
+++ b/src/vs/base/browser/ui/list/listWidget.ts
@@ -26,6 +26,7 @@ import { CombinedSpliceable } from 'vs/base/browser/ui/list/splice';
import { clamp } from 'vs/base/common/numbers';
import { matchesPrefix } from 'vs/base/common/filters';
import { IDragAndDropData } from 'vs/base/browser/dnd';
+import { alert } from 'vs/base/browser/ui/aria/aria';
interface ITraitChangeEvent {
indexes: number[];
@@ -344,6 +345,7 @@ class TypeLabelController implements IDisposable {
private automaticKeyboardNavigation = true;
private triggered = false;
+ private previouslyFocused = -1;
private readonly enabledDisposables = new DisposableStore();
private readonly disposables = new DisposableStore();
@@ -393,6 +395,7 @@ class TypeLabelController implements IDisposable {
const onInput = Event.reduce(Event.any(onChar, onClear), (r, i) => i === null ? null : ((r || '') + i));
onInput(this.onInput, this, this.enabledDisposables);
+ onClear(this.onClear, this, this.enabledDisposables);
this.enabled = true;
this.triggered = false;
@@ -408,6 +411,19 @@ class TypeLabelController implements IDisposable {
this.triggered = false;
}
+ private onClear(): void {
+ const focus = this.list.getFocus();
+ if (focus.length > 0 && focus[0] === this.previouslyFocused) {
+ // List: re-anounce element on typing end since typed keys will interupt aria label of focused element
+ // Do not announce if there was a focus change at the end to prevent duplication https://github.com/microsoft/vscode/issues/95961
+ const ariaLabel = this.list.options.accessibilityProvider?.getAriaLabel(this.list.element(focus[0]));
+ if (ariaLabel) {
+ alert(ariaLabel);
+ }
+ }
+ this.previouslyFocused = -1;
+ }
+
private onInput(word: string | null): void {
if (!word) {
this.state = TypeLabelControllerState.Idle;
@@ -426,6 +442,7 @@ class TypeLabelController implements IDisposable {
const labelStr = label && label.toString();
if (typeof labelStr === 'undefined' || matchesPrefix(word, labelStr)) {
+ this.previouslyFocused = start;
this.list.setFocus([index]);
this.list.reveal(index);
return;
diff --git a/src/vs/base/browser/ui/splitview/paneview.css b/src/vs/base/browser/ui/splitview/paneview.css
index 679bf211e94..2694eb4b9d2 100644
--- a/src/vs/base/browser/ui/splitview/paneview.css
+++ b/src/vs/base/browser/ui/splitview/paneview.css
@@ -16,6 +16,10 @@
flex-direction: column;
}
+.monaco-pane-view .pane.horizontal:not(.expanded) {
+ flex-direction: row;
+}
+
.monaco-pane-view .pane > .pane-header {
font-size: 11px;
font-weight: bold;
@@ -26,6 +30,11 @@
align-items: center;
}
+.monaco-pane-view .pane.horizontal:not(.expanded) > .pane-header {
+ flex-direction: column;
+ width: 22px;
+}
+
.monaco-pane-view .pane > .pane-header > .twisties {
width: 20px;
display: flex;
@@ -36,6 +45,11 @@
flex-shrink: 0;
}
+.monaco-pane-view .pane.horizontal:not(.expanded) > .pane-header > .twisties {
+ margin-top: 2px;
+ margin-bottom: 2px;
+}
+
.monaco-pane-view .pane > .pane-header.expanded > .twisties::before {
transform: rotate(90deg);
}
@@ -132,6 +146,7 @@
width: 100%;
height: 100%;
min-height: 22px;
+ min-width: 19px;
pointer-events: none; /* very important to not take events away from the parent */
transition: opacity 150ms ease-out;
diff --git a/src/vs/base/browser/ui/splitview/paneview.ts b/src/vs/base/browser/ui/splitview/paneview.ts
index e4c62b298b9..1a7c66cc8a9 100644
--- a/src/vs/base/browser/ui/splitview/paneview.ts
+++ b/src/vs/base/browser/ui/splitview/paneview.ts
@@ -106,7 +106,7 @@ export abstract class Pane extends Disposable implements IView {
get minimumSize(): number {
const headerSize = this.headerSize;
const expanded = !this.headerVisible || this.isExpanded();
- const minimumBodySize = expanded ? this.minimumBodySize : this._orientation === Orientation.HORIZONTAL ? 50 : 0;
+ const minimumBodySize = expanded ? this.minimumBodySize : 0;
return headerSize + minimumBodySize;
}
@@ -114,7 +114,7 @@ export abstract class Pane extends Disposable implements IView {
get maximumSize(): number {
const headerSize = this.headerSize;
const expanded = !this.headerVisible || this.isExpanded();
- const maximumBodySize = expanded ? this.maximumBodySize : this._orientation === Orientation.HORIZONTAL ? 50 : 0;
+ const maximumBodySize = expanded ? this.maximumBodySize : 0;
return headerSize + maximumBodySize;
}
@@ -141,6 +141,10 @@ export abstract class Pane extends Disposable implements IView {
return false;
}
+ if (this.element) {
+ toggleClass(this.element, 'expanded', expanded);
+ }
+
this._expanded = !!expanded;
this.updateHeader();
@@ -185,12 +189,21 @@ export abstract class Pane extends Disposable implements IView {
this._orientation = orientation;
+ if (this.element) {
+ toggleClass(this.element, 'horizontal', this.orientation === Orientation.HORIZONTAL);
+ toggleClass(this.element, 'vertical', this.orientation === Orientation.VERTICAL);
+ }
+
if (this.header) {
this.updateHeader();
}
}
render(): void {
+ toggleClass(this.element, 'expanded', this.isExpanded());
+ toggleClass(this.element, 'horizontal', this.orientation === Orientation.HORIZONTAL);
+ toggleClass(this.element, 'vertical', this.orientation === Orientation.VERTICAL);
+
this.header = $('.pane-header');
append(this.element, this.header);
this.header.setAttribute('tabindex', '0');
@@ -262,7 +275,6 @@ export abstract class Pane extends Disposable implements IView {
protected updateHeader(): void {
const expanded = !this.headerVisible || this.isExpanded();
- this.header.style.height = `${this.headerSize}px`;
this.header.style.lineHeight = `${this.headerSize}px`;
toggleClass(this.header, 'hidden', !this.headerVisible);
toggleClass(this.header, 'expanded', expanded);
diff --git a/src/vs/base/browser/ui/splitview/splitview.ts b/src/vs/base/browser/ui/splitview/splitview.ts
index 8224c7be20e..028e4f839f6 100644
--- a/src/vs/base/browser/ui/splitview/splitview.ts
+++ b/src/vs/base/browser/ui/splitview/splitview.ts
@@ -951,7 +951,7 @@ export class SplitView extends Disposable {
position += this.viewItems[i].size;
if (this.sashItems[i].sash === sash) {
- return Math.min(position, this.contentSize - 2);
+ return position;
}
}
diff --git a/src/vs/base/browser/ui/tree/treeDefaults.ts b/src/vs/base/browser/ui/tree/treeDefaults.ts
index 944efecd207..834ab449fdd 100644
--- a/src/vs/base/browser/ui/tree/treeDefaults.ts
+++ b/src/vs/base/browser/ui/tree/treeDefaults.ts
@@ -13,7 +13,7 @@ export class CollapseAllAction extends Action {
super('vs.tree.collapse', nls.localize('collapse all', "Collapse All"), 'collapse-all', enabled);
}
- async run(context?: any): Promise {
+ async run(): Promise {
this.viewer.collapseAll();
this.viewer.setSelection([]);
this.viewer.setFocus([]);
diff --git a/src/vs/base/common/async.ts b/src/vs/base/common/async.ts
index 9951ca26b7e..6925d74b686 100644
--- a/src/vs/base/common/async.ts
+++ b/src/vs/base/common/async.ts
@@ -764,7 +764,7 @@ export class IdleValue {
this._handle.dispose();
}
- getValue(): T {
+ get value(): T {
if (!this._didRun) {
this._handle.dispose();
this._executor();
diff --git a/src/vs/base/common/comparers.ts b/src/vs/base/common/comparers.ts
index a713092dbda..a25e2bb9c16 100644
--- a/src/vs/base/common/comparers.ts
+++ b/src/vs/base/common/comparers.ts
@@ -6,7 +6,12 @@
import { sep } from 'vs/base/common/path';
import { IdleValue } from 'vs/base/common/async';
-const intlFileNameCollator: IdleValue<{ collator: Intl.Collator, collatorIsNumeric: boolean }> = new IdleValue(() => {
+// When comparing large numbers of strings, such as in sorting large arrays, is better for
+// performance to create an Intl.Collator object and use the function provided by its compare
+// property than it is to use String.prototype.localeCompare()
+
+// A collator with numeric sorting enabled, and no sensitivity to case or to accents
+const intlFileNameCollatorBaseNumeric: IdleValue<{ collator: Intl.Collator, collatorIsNumeric: boolean }> = new IdleValue(() => {
const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' });
return {
collator: collator,
@@ -14,20 +19,64 @@ const intlFileNameCollator: IdleValue<{ collator: Intl.Collator, collatorIsNumer
};
});
+// A collator with numeric sorting enabled.
+const intlFileNameCollatorNumeric: IdleValue<{ collator: Intl.Collator }> = new IdleValue(() => {
+ const collator = new Intl.Collator(undefined, { numeric: true });
+ return {
+ collator: collator
+ };
+});
+
+// A collator with numeric sorting enabled, and sensitivity to accents and diacritics but not case.
+const intlFileNameCollatorNumericCaseInsenstive: IdleValue<{ collator: Intl.Collator }> = new IdleValue(() => {
+ const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'accent' });
+ return {
+ collator: collator
+ };
+});
+
export function compareFileNames(one: string | null, other: string | null, caseSensitive = false): number {
const a = one || '';
const b = other || '';
- const result = intlFileNameCollator.getValue().collator.compare(a, b);
+ const result = intlFileNameCollatorBaseNumeric.value.collator.compare(a, b);
// Using the numeric option in the collator will
// make compare(`foo1`, `foo01`) === 0. We must disambiguate.
- if (intlFileNameCollator.getValue().collatorIsNumeric && result === 0 && a !== b) {
+ if (intlFileNameCollatorBaseNumeric.value.collatorIsNumeric && result === 0 && a !== b) {
return a < b ? -1 : 1;
}
return result;
}
+/** Compares filenames by name then extension, sorting numbers numerically instead of alphabetically. */
+export function compareFileNamesNumeric(one: string | null, other: string | null): number {
+ const [oneName, oneExtension] = extractNameAndExtension(one, true);
+ const [otherName, otherExtension] = extractNameAndExtension(other, true);
+ const collatorNumeric = intlFileNameCollatorNumeric.value.collator;
+ const collatorNumericCaseInsensitive = intlFileNameCollatorNumericCaseInsenstive.value.collator;
+ let result;
+
+ // Check for name differences, comparing numbers numerically instead of alphabetically.
+ result = compareAndDisambiguateByLength(collatorNumeric, oneName, otherName);
+ if (result !== 0) {
+ return result;
+ }
+
+ // Check for case insensitive extension differences, comparing numbers numerically instead of alphabetically.
+ result = compareAndDisambiguateByLength(collatorNumericCaseInsensitive, oneExtension, otherExtension);
+ if (result !== 0) {
+ return result;
+ }
+
+ // Disambiguate the extension case if needed.
+ if (oneExtension !== otherExtension) {
+ return collatorNumeric.compare(oneExtension, otherExtension);
+ }
+
+ return 0;
+}
+
const FileNameMatch = /^(.*?)(\.([^.]*))?$/;
export function noIntlCompareFileNames(one: string | null, other: string | null, caseSensitive = false): number {
@@ -54,19 +103,19 @@ export function compareFileExtensions(one: string | null, other: string | null):
const [oneName, oneExtension] = extractNameAndExtension(one);
const [otherName, otherExtension] = extractNameAndExtension(other);
- let result = intlFileNameCollator.getValue().collator.compare(oneExtension, otherExtension);
+ let result = intlFileNameCollatorBaseNumeric.value.collator.compare(oneExtension, otherExtension);
if (result === 0) {
// Using the numeric option in the collator will
// make compare(`foo1`, `foo01`) === 0. We must disambiguate.
- if (intlFileNameCollator.getValue().collatorIsNumeric && oneExtension !== otherExtension) {
+ if (intlFileNameCollatorBaseNumeric.value.collatorIsNumeric && oneExtension !== otherExtension) {
return oneExtension < otherExtension ? -1 : 1;
}
// Extensions are equal, compare filenames
- result = intlFileNameCollator.getValue().collator.compare(oneName, otherName);
+ result = intlFileNameCollatorBaseNumeric.value.collator.compare(oneName, otherName);
- if (intlFileNameCollator.getValue().collatorIsNumeric && result === 0 && oneName !== otherName) {
+ if (intlFileNameCollatorBaseNumeric.value.collatorIsNumeric && result === 0 && oneName !== otherName) {
return oneName < otherName ? -1 : 1;
}
}
@@ -74,10 +123,63 @@ export function compareFileExtensions(one: string | null, other: string | null):
return result;
}
-function extractNameAndExtension(str?: string | null): [string, string] {
+/** Compares filenames by extenson, then by name. Sorts numbers numerically, not alphabetically. */
+export function compareFileExtensionsNumeric(one: string | null, other: string | null): number {
+ const [oneName, oneExtension] = extractNameAndExtension(one, true);
+ const [otherName, otherExtension] = extractNameAndExtension(other, true);
+ const collatorNumeric = intlFileNameCollatorNumeric.value.collator;
+ const collatorNumericCaseInsensitive = intlFileNameCollatorNumericCaseInsenstive.value.collator;
+ let result;
+
+ // Check for extension differences, ignoring differences in case and comparing numbers numerically.
+ result = compareAndDisambiguateByLength(collatorNumericCaseInsensitive, oneExtension, otherExtension);
+ if (result !== 0) {
+ return result;
+ }
+
+ // Compare names.
+ result = compareAndDisambiguateByLength(collatorNumeric, oneName, otherName);
+ if (result !== 0) {
+ return result;
+ }
+
+ // Disambiguate extension case if needed.
+ if (oneExtension !== otherExtension) {
+ return collatorNumeric.compare(oneExtension, otherExtension);
+ }
+
+ return 0;
+}
+
+/** Extracts the name and extension from a full filename, with optional special handling for dotfiles */
+function extractNameAndExtension(str?: string | null, dotfilesAsNames = false): [string, string] {
const match = str ? FileNameMatch.exec(str) as Array : ([] as Array);
- return [(match && match[1]) || '', (match && match[3]) || ''];
+ let result: [string, string] = [(match && match[1]) || '', (match && match[3]) || ''];
+
+ // if the dotfilesAsNames option is selected, treat an empty filename with an extension,
+ // or a filename that starts with a dot, as a dotfile name
+ if (dotfilesAsNames && (!result[0] && result[1] || result[0] && result[0].charAt(0) === '.')) {
+ result = [result[0] + '.' + result[1], ''];
+ }
+
+ return result;
+}
+
+function compareAndDisambiguateByLength(collator: Intl.Collator, one: string, other: string) {
+ // Check for differences
+ let result = collator.compare(one, other);
+ if (result !== 0) {
+ return result;
+ }
+
+ // In a numeric comparison, `foo1` and `foo01` will compare as equivalent.
+ // Disambiguate by sorting the shorter string first.
+ if (one.length !== other.length) {
+ return one.length < other.length ? -1 : 1;
+ }
+
+ return 0;
}
function comparePathComponents(one: string, other: string, caseSensitive = false): number {
diff --git a/src/vs/base/common/map.ts b/src/vs/base/common/map.ts
index 5d79ad368a7..8df6ef68bea 100644
--- a/src/vs/base/common/map.ts
+++ b/src/vs/base/common/map.ts
@@ -580,18 +580,23 @@ export const enum Touch {
AsNew = 2
}
-export class LinkedMap {
+export class LinkedMap implements Map {
+
+ readonly [Symbol.toStringTag] = 'LinkedMap';
private _map: Map>;
private _head: Item | undefined;
private _tail: Item | undefined;
private _size: number;
+ private _state: number;
+
constructor() {
this._map = new Map>();
this._head = undefined;
this._tail = undefined;
this._size = 0;
+ this._state = 0;
}
clear(): void {
@@ -599,6 +604,7 @@ export class LinkedMap {
this._head = undefined;
this._tail = undefined;
this._size = 0;
+ this._state++;
}
isEmpty(): boolean {
@@ -632,7 +638,7 @@ export class LinkedMap {
return item.value;
}
- set(key: K, value: V, touch: Touch = Touch.None): void {
+ set(key: K, value: V, touch: Touch = Touch.None): this {
let item = this._map.get(key);
if (item) {
item.value = value;
@@ -658,6 +664,7 @@ export class LinkedMap {
this._map.set(key, item);
this._size++;
}
+ return this;
}
delete(key: K): boolean {
@@ -690,6 +697,7 @@ export class LinkedMap {
}
forEach(callbackfn: (value: V, key: K, map: LinkedMap) => void, thisArg?: any): void {
+ const state = this._state;
let current = this._head;
while (current) {
if (thisArg) {
@@ -697,38 +705,25 @@ export class LinkedMap {
} else {
callbackfn(current.value, current.key, this);
}
+ if (this._state !== state) {
+ throw new Error(`LinkedMap got modified during iteration.`);
+ }
current = current.next;
}
}
- values(): V[] {
- const result: V[] = [];
- let current = this._head;
- while (current) {
- result.push(current.value);
- current = current.next;
- }
- return result;
- }
-
- keys(): K[] {
- const result: K[] = [];
- let current = this._head;
- while (current) {
- result.push(current.key);
- current = current.next;
- }
- return result;
- }
-
- /* VS Code / Monaco editor runs on es5 which has no Symbol.iterator
keys(): IterableIterator {
- const current = this._head;
+ const map = this;
+ const state = this._state;
+ let current = this._head;
const iterator: IterableIterator = {
[Symbol.iterator]() {
return iterator;
},
- next():IteratorResult {
+ next(): IteratorResult {
+ if (map._state !== state) {
+ throw new Error(`LinkedMap got modified during iteration.`);
+ }
if (current) {
const result = { value: current.key, done: false };
current = current.next;
@@ -742,12 +737,17 @@ export class LinkedMap {
}
values(): IterableIterator {
- const current = this._head;
+ const map = this;
+ const state = this._state;
+ let current = this._head;
const iterator: IterableIterator = {
[Symbol.iterator]() {
return iterator;
},
- next():IteratorResult {
+ next(): IteratorResult {
+ if (map._state !== state) {
+ throw new Error(`LinkedMap got modified during iteration.`);
+ }
if (current) {
const result = { value: current.value, done: false };
current = current.next;
@@ -759,7 +759,34 @@ export class LinkedMap {
};
return iterator;
}
- */
+
+ entries(): IterableIterator<[K, V]> {
+ const map = this;
+ const state = this._state;
+ let current = this._head;
+ const iterator: IterableIterator<[K, V]> = {
+ [Symbol.iterator]() {
+ return iterator;
+ },
+ next(): IteratorResult<[K, V]> {
+ if (map._state !== state) {
+ throw new Error(`LinkedMap got modified during iteration.`);
+ }
+ if (current) {
+ const result: IteratorResult<[K, V]> = { value: [current.key, current.value], done: false };
+ current = current.next;
+ return result;
+ } else {
+ return { value: undefined, done: true };
+ }
+ }
+ };
+ return iterator;
+ }
+
+ [Symbol.iterator](): IterableIterator<[K, V]> {
+ return this.entries();
+ }
protected trimOld(newSize: number) {
if (newSize >= this.size) {
@@ -781,6 +808,7 @@ export class LinkedMap {
if (current) {
current.previous = undefined;
}
+ this._state++;
}
private addItemFirst(item: Item): void {
@@ -794,6 +822,7 @@ export class LinkedMap {
this._head.previous = item;
}
this._head = item;
+ this._state++;
}
private addItemLast(item: Item): void {
@@ -807,6 +836,7 @@ export class LinkedMap {
this._tail.next = item;
}
this._tail = item;
+ this._state++;
}
private removeItem(item: Item): void {
@@ -843,6 +873,7 @@ export class LinkedMap {
}
item.next = undefined;
item.previous = undefined;
+ this._state++;
}
private touch(item: Item, touch: Touch): void {
@@ -879,6 +910,7 @@ export class LinkedMap {
item.next = this._head;
this._head.previous = item;
this._head = item;
+ this._state++;
} else if (touch === Touch.AsNew) {
if (item === this._tail) {
return;
@@ -902,6 +934,7 @@ export class LinkedMap {
item.previous = this._tail;
this._tail.next = item;
this._tail = item;
+ this._state++;
}
}
@@ -953,17 +986,18 @@ export class LRUCache extends LinkedMap {
this.checkTrim();
}
- get(key: K): V | undefined {
- return super.get(key, Touch.AsNew);
+ get(key: K, touch: Touch = Touch.AsNew): V | undefined {
+ return super.get(key, touch);
}
peek(key: K): V | undefined {
return super.get(key, Touch.None);
}
- set(key: K, value: V): void {
+ set(key: K, value: V): this {
super.set(key, value, Touch.AsNew);
this.checkTrim();
+ return this;
}
private checkTrim() {
diff --git a/src/vs/base/parts/quickinput/browser/media/quickInput.css b/src/vs/base/parts/quickinput/browser/media/quickInput.css
index d45840ee38c..c086b587c14 100644
--- a/src/vs/base/parts/quickinput/browser/media/quickInput.css
+++ b/src/vs/base/parts/quickinput/browser/media/quickInput.css
@@ -103,6 +103,8 @@
.quick-input-count .monaco-count-badge {
vertical-align: middle;
+ padding: 2px 4px;
+ border-radius: 2px;
}
.quick-input-action {
diff --git a/src/vs/base/test/browser/comparers.test.ts b/src/vs/base/test/browser/comparers.test.ts
index 369f160803b..90dff8c2835 100644
--- a/src/vs/base/test/browser/comparers.test.ts
+++ b/src/vs/base/test/browser/comparers.test.ts
@@ -3,48 +3,294 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import { compareFileNames, compareFileExtensions } from 'vs/base/common/comparers';
+import { compareFileNames, compareFileExtensions, compareFileNamesNumeric, compareFileExtensionsNumeric } from 'vs/base/common/comparers';
import * as assert from 'assert';
+const compareLocale = (a: string, b: string) => a.localeCompare(b);
+const compareLocaleNumeric = (a: string, b: string) => a.localeCompare(b, undefined, { numeric: true });
+
+
suite('Comparers', () => {
test('compareFileNames', () => {
+ //
+ // Comparisons with the same results as compareFileNamesNumeric
+ //
+
+ // name-only comparisons
assert(compareFileNames(null, null) === 0, 'null should be equal');
assert(compareFileNames(null, 'abc') < 0, 'null should be come before real values');
assert(compareFileNames('', '') === 0, 'empty should be equal');
assert(compareFileNames('abc', 'abc') === 0, 'equal names should be equal');
- assert(compareFileNames('.abc', '.abc') === 0, 'equal full names should be equal');
- assert(compareFileNames('.env', '.env.example') < 0, 'filenames with extensions should come after those without');
- assert(compareFileNames('.env.example', '.gitattributes') < 0, 'filenames starting with dots and with extensions should still sort properly');
+ assert(compareFileNames('z', 'A') > 0, 'z comes is after A regardless of case');
+ assert(compareFileNames('Z', 'a') > 0, 'Z comes after a regardless of case');
+
+ // name plus extension comparisons
+ assert(compareFileNames('bbb.aaa', 'aaa.bbb') > 0, 'files with extensions are compared first by filename');
+
+ // dotfile comparisons
+ assert(compareFileNames('.abc', '.abc') === 0, 'equal dotfile names should be equal');
+ assert(compareFileNames('.env.', '.gitattributes') < 0, 'filenames starting with dots and with extensions should still sort properly');
+ assert(compareFileNames('.env', '.aaa.env') > 0, 'dotfiles sort alphabetically when they contain multiple dots');
+ assert(compareFileNames('.env', '.env.aaa') < 0, 'dotfiles with the same root sort shortest first');
+ assert(compareFileNames('.aaa_env', '.aaa.env') < 0, 'and underscore in a dotfile name will sort before a dot');
+
+ // dotfile vs non-dotfile comparisons
+ assert(compareFileNames(null, '.abc') < 0, 'null should come before dotfiles');
+ assert(compareFileNames('.env', 'aaa') < 0, 'dotfiles come before filenames without extensions');
+ assert(compareFileNames('.env', 'aaa.env') < 0, 'dotfiles come before filenames with extensions');
+ assert(compareFileNames('.md', 'A.MD') < 0, 'dotfiles sort before uppercase files');
+ assert(compareFileNames('.MD', 'a.md') < 0, 'dotfiles sort before lowercase files');
+
+ // numeric comparisons
assert(compareFileNames('1', '1') === 0, 'numerically equal full names should be equal');
assert(compareFileNames('abc1.txt', 'abc1.txt') === 0, 'equal filenames with numbers should be equal');
assert(compareFileNames('abc1.txt', 'abc2.txt') < 0, 'filenames with numbers should be in numerical order, not alphabetical order');
assert(compareFileNames('abc2.txt', 'abc10.txt') < 0, 'filenames with numbers should be in numerical order even when they are multiple digits long');
+ assert(compareFileNames('abc02.txt', 'abc010.txt') < 0, 'filenames with numbers that have leading zeros sort numerically');
+ assert(compareFileNames('abc1.10.txt', 'abc1.2.txt') > 0, 'numbers with dots between them are treated as two separate numbers, not one decimal number');
+
+ //
+ // Comparisons with different results than compareFileNamesNumeric
+ //
+
+ // name-only comparisons
+ assert(compareFileNames('a', 'A') !== compareLocale('a', 'A'), 'the same letter does not sort by locale');
+ assert(compareFileNames('â', 'Â') !== compareLocale('â', 'Â'), 'the same accented letter does not sort by locale');
+ assert.notDeepEqual(['artichoke', 'Artichoke', 'art', 'Art'].sort(compareFileNames), ['artichoke', 'Artichoke', 'art', 'Art'].sort(compareLocale), 'words with the same root and different cases do not sort in locale order');
+ assert.notDeepEqual(['email', 'Email', 'émail', 'Émail'].sort(compareFileNames), ['email', 'Email', 'émail', 'Émail'].sort(compareLocale), 'the same base characters with different case or accents do not sort in locale order');
+
+ // name plus extension comparisons
+ assert(compareFileNames('aggregate.go', 'aggregate_repo.go') > 0, 'compares the whole name all at once by locale');
+
+ // numeric comparisons
+ assert(compareFileNames('abc02.txt', 'abc002.txt') > 0, 'filenames with equivalent numbers and leading zeros sort in unicode order');
+ assert(compareFileNames('abc.txt1', 'abc.txt01') > 0, 'same name plus extensions with equal numbers sort in unicode order');
+ assert(compareFileNames('art01', 'Art01') !== 'art01'.localeCompare('Art01', undefined, { numeric: true }),
+ 'a numerically equivalent word of a different case does not compare numerically based on locale');
+
});
test('compareFileExtensions', () => {
+ //
+ // Comparisons with the same results as compareFileExtensionsNumeric
+ //
+
+ // name-only comparisons
assert(compareFileExtensions(null, null) === 0, 'null should be equal');
- assert(compareFileExtensions(null, '.abc') < 0, 'null should come before real files');
assert(compareFileExtensions(null, 'abc') < 0, 'null should come before real files without extension');
assert(compareFileExtensions('', '') === 0, 'empty should be equal');
assert(compareFileExtensions('abc', 'abc') === 0, 'equal names should be equal');
- assert(compareFileExtensions('.abc', '.abc') === 0, 'equal full names should be equal');
+ assert(compareFileExtensions('z', 'A') > 0, 'z comes after A');
+ assert(compareFileExtensions('Z', 'a') > 0, 'Z comes after a');
+
+ // name plus extension comparisons
assert(compareFileExtensions('file.ext', 'file.ext') === 0, 'equal full names should be equal');
assert(compareFileExtensions('a.ext', 'b.ext') < 0, 'if equal extensions, filenames should be compared');
- assert(compareFileExtensions('.ext', 'a.ext') < 0, 'if equal extensions, filenames should be compared, empty filename should come before others');
- assert(compareFileExtensions('file.aaa', 'file.bbb') < 0, 'files should be compared by extensions');
+ assert(compareFileExtensions('file.aaa', 'file.bbb') < 0, 'files with equal names should be compared by extensions');
assert(compareFileExtensions('bbb.aaa', 'aaa.bbb') < 0, 'files should be compared by extensions even if filenames compare differently');
+ assert(compareFileExtensions('agg.go', 'aggrepo.go') < 0, 'shorter names sort before longer names');
+ assert(compareFileExtensions('agg.go', 'agg_repo.go') < 0, 'shorter names short before longer names even when the longer name contains an underscore');
+ assert(compareFileExtensions('a.MD', 'b.md') < 0, 'when extensions are the same except for case, the files sort by name');
+
+ // dotfile comparisons
+ assert(compareFileExtensions('.abc', '.abc') === 0, 'equal dotfiles should be equal');
+ assert(compareFileExtensions('.md', '.Gitattributes') > 0, 'dotfiles sort alphabetically regardless of case');
+
+ // dotfile vs non-dotfile comparisons
+ assert(compareFileExtensions(null, '.abc') < 0, 'null should come before dotfiles');
+ assert(compareFileExtensions('.env', 'aaa.env') < 0, 'if equal extensions, filenames should be compared, empty filename should come before others');
+ assert(compareFileExtensions('.MD', 'a.md') < 0, 'if extensions differ in case, files sort by extension in unicode order');
+
+ // numeric comparisons
assert(compareFileExtensions('1', '1') === 0, 'numerically equal full names should be equal');
assert(compareFileExtensions('abc1.txt', 'abc1.txt') === 0, 'equal filenames with numbers should be equal');
assert(compareFileExtensions('abc1.txt', 'abc2.txt') < 0, 'filenames with numbers should be in numerical order, not alphabetical order');
assert(compareFileExtensions('abc2.txt', 'abc10.txt') < 0, 'filenames with numbers should be in numerical order even when they are multiple digits long');
+ assert(compareFileExtensions('abc02.txt', 'abc010.txt') < 0, 'filenames with numbers that have leading zeros sort numerically');
+ assert(compareFileExtensions('abc1.10.txt', 'abc1.2.txt') > 0, 'numbers with dots between them are treated as two separate numbers, not one decimal number');
+ assert(compareFileExtensions('abc2.txt2', 'abc1.txt10') < 0, 'extensions with numbers should be in numerical order, not alphabetical order');
assert(compareFileExtensions('txt.abc1', 'txt.abc1') === 0, 'equal extensions with numbers should be equal');
assert(compareFileExtensions('txt.abc1', 'txt.abc2') < 0, 'extensions with numbers should be in numerical order, not alphabetical order');
assert(compareFileExtensions('txt.abc2', 'txt.abc10') < 0, 'extensions with numbers should be in numerical order even when they are multiple digits long');
assert(compareFileExtensions('a.ext1', 'b.ext1') < 0, 'if equal extensions with numbers, filenames should be compared');
- assert(compareFileExtensions('file2.ext2', 'file1.ext10') < 0, 'extensions with numbers should be in numerical order, not alphabetical order');
- assert(compareFileExtensions('file.ext01', 'file.ext1') < 0, 'extensions with equal numbers should be in alphabetical order');
+ assert(compareFileExtensions('a10.txt', 'A2.txt') > 0, 'filenames with number and case differences compare numerically');
+
+ // Same extension comparison that has the same result as compareFileExtensionsNumeric, but a different result than compareFileNames
+ // This is an edge case caused by compareFileNames comparing the whole name all at once instead of the name and then the extension.
+ assert(compareFileExtensions('aggregate.go', 'aggregate_repo.go') < 0, 'when extensions are equal, names sort in dictionary order');
+
+ //
+ // Comparisons with different results from compareFileExtensionsNumeric
+ //
+
+ // name-only comparisions
+ assert(compareFileExtensions('a', 'A') !== compareLocale('a', 'A'), 'the same letter of different case does not sort by locale');
+ assert(compareFileExtensions('â', 'Â') !== compareLocale('â', 'Â'), 'the same accented letter of different case does not sort by locale');
+ assert.notDeepEqual(['artichoke', 'Artichoke', 'art', 'Art'].sort(compareFileExtensions), ['artichoke', 'Artichoke', 'art', 'Art'].sort(compareLocale), 'words with the same root and different cases do not sort in locale order');
+ assert.notDeepEqual(['email', 'Email', 'émail', 'Émail'].sort(compareFileExtensions), ['email', 'Email', 'émail', 'Émail'].sort((a, b) => a.localeCompare(b)), 'the same base characters with different case or accents do not sort in locale order');
+
+ // name plus extension comparisons
+ assert(compareFileExtensions('a.MD', 'a.md') !== compareLocale('MD', 'md'), 'case differences in extensions do not sort by locale');
+ assert(compareFileExtensions('a.md', 'A.md') !== compareLocale('a', 'A'), 'case differences in names do not sort by locale');
+
+ // dotfile comparisons
+ assert(compareFileExtensions('.env', '.aaa.env') < 0, 'a dotfile with an extension is treated as a name plus an extension - equal extensions');
+ assert(compareFileExtensions('.env', '.env.aaa') > 0, 'a dotfile with an extension is treated as a name plus an extension - unequal extensions');
+
+ // dotfile vs non-dotfile comparisons
+ assert(compareFileExtensions('.env', 'aaa') > 0, 'filenames without extensions come before dotfiles');
+ assert(compareFileExtensions('.md', 'A.MD') > 0, 'a file with an uppercase extension sorts before a dotfile of the same lowercase extension');
+
+ // numeric comparisons
+ assert(compareFileExtensions('abc.txt01', 'abc.txt1') < 0, 'extensions with equal numbers sort in unicode order');
+ assert(compareFileExtensions('art01', 'Art01') !== compareLocaleNumeric('art01', 'Art01'), 'a numerically equivalent word of a different case does not compare by locale');
+ assert(compareFileExtensions('abc02.txt', 'abc002.txt') > 0, 'filenames with equivalent numbers and leading zeros sort in unicode order');
+ assert(compareFileExtensions('txt.abc01', 'txt.abc1') < 0, 'extensions with equivalent numbers sort in unicode order');
+
+ });
+
+ test('compareFileNamesNumeric', () => {
+
+ //
+ // Comparisons with the same results as compareFileNames
+ //
+
+ // name-only comparisons
+ assert(compareFileNamesNumeric(null, null) === 0, 'null should be equal');
+ assert(compareFileNamesNumeric(null, 'abc') < 0, 'null should be come before real values');
+ assert(compareFileNamesNumeric('', '') === 0, 'empty should be equal');
+ assert(compareFileNamesNumeric('abc', 'abc') === 0, 'equal names should be equal');
+ assert(compareFileNamesNumeric('z', 'A') > 0, 'z comes is after A regardless of case');
+ assert(compareFileNamesNumeric('Z', 'a') > 0, 'Z comes after a regardless of case');
+
+ // name plus extension comparisons
+ assert(compareFileNamesNumeric('file.ext', 'file.ext') === 0, 'equal full names should be equal');
+ assert(compareFileNamesNumeric('a.ext', 'b.ext') < 0, 'if equal extensions, filenames should be compared');
+ assert(compareFileNamesNumeric('file.aaa', 'file.bbb') < 0, 'files with equal names should be compared by extensions');
+ assert(compareFileNamesNumeric('bbb.aaa', 'aaa.bbb') > 0, 'files should be compared by names even if extensions compare differently');
+
+ // dotfile comparisons
+ assert(compareFileNamesNumeric('.abc', '.abc') === 0, 'equal dotfile names should be equal');
+ assert(compareFileNamesNumeric('.env.', '.gitattributes') < 0, 'filenames starting with dots and with extensions should still sort properly');
+ assert(compareFileNamesNumeric('.env', '.aaa.env') > 0, 'dotfiles sort alphabetically when they contain multiple dots');
+ assert(compareFileNamesNumeric('.env', '.env.aaa') < 0, 'dotfiles with the same root sort shortest first');
+ assert(compareFileNamesNumeric('.aaa_env', '.aaa.env') < 0, 'and underscore in a dotfile name will sort before a dot');
+
+ // dotfile vs non-dotfile comparisons
+ assert(compareFileNamesNumeric(null, '.abc') < 0, 'null should come before dotfiles');
+ assert(compareFileNamesNumeric('.env', 'aaa') < 0, 'dotfiles come before filenames without extensions');
+ assert(compareFileNamesNumeric('.env', 'aaa.env') < 0, 'dotfiles come before filenames with extensions');
+ assert(compareFileNamesNumeric('.md', 'A.MD') < 0, 'dotfiles sort before uppercase files');
+ assert(compareFileNamesNumeric('.MD', 'a.md') < 0, 'dotfiles sort before lowercase files');
+
+ // numeric comparisons
+ assert(compareFileNamesNumeric('1', '1') === 0, 'numerically equal full names should be equal');
+ assert(compareFileNamesNumeric('abc1.txt', 'abc1.txt') === 0, 'equal filenames with numbers should be equal');
+ assert(compareFileNamesNumeric('abc1.txt', 'abc2.txt') < 0, 'filenames with numbers should be in numerical order, not alphabetical order');
+ assert(compareFileNamesNumeric('abc2.txt', 'abc10.txt') < 0, 'filenames with numbers should be in numerical order even when they are multiple digits long');
+ assert(compareFileNamesNumeric('abc02.txt', 'abc010.txt') < 0, 'filenames with numbers that have leading zeros sort numerically');
+ assert(compareFileNamesNumeric('abc1.10.txt', 'abc1.2.txt') > 0, 'numbers with dots between them are treated as two separate numbers, not one decimal number');
+
+ //
+ // Comparisons with different results than compareFileNames
+ //
+
+ // name-only comparisons
+ assert(compareFileNamesNumeric('a', 'A') === compareLocale('a', 'A'), 'the same letter sorts by locale');
+ assert(compareFileNamesNumeric('â', 'Â') === compareLocale('â', 'Â'), 'the same accented letter sorts by locale');
+ assert.deepEqual(['artichoke', 'Artichoke', 'art', 'Art'].sort(compareFileNamesNumeric), ['artichoke', 'Artichoke', 'art', 'Art'].sort(compareLocale), 'words with the same root and different cases sort in locale order');
+ assert.deepEqual(['email', 'Email', 'émail', 'Émail'].sort(compareFileNamesNumeric), ['email', 'Email', 'émail', 'Émail'].sort(compareLocale), 'the same base characters with different case or accents sort in locale order');
+
+ // name plus extensions comparisons
+ assert(compareFileNamesNumeric('aggregate.go', 'aggregate_repo.go') < 0, 'compares the name first, then the extension');
+
+ // numeric comparisons
+ assert(compareFileNamesNumeric('abc02.txt', 'abc002.txt') < 0, 'filenames with equivalent numbers and leading zeros sort shortest number first');
+ assert(compareFileNamesNumeric('abc.txt1', 'abc.txt01') < 0, 'same name plus extensions with equal numbers sort shortest number first');
+ assert(compareFileNamesNumeric('art01', 'Art01') === compareLocaleNumeric('art01', 'Art01'), 'a numerically equivalent word of a different case compares numerically based on locale');
+
+ });
+
+ test('compareFileExtensionsNumeric', () => {
+
+ //
+ // Comparisons with the same result as compareFileExtensions
+ //
+
+ // name-only comparisons
+ assert(compareFileExtensionsNumeric(null, null) === 0, 'null should be equal');
+ assert(compareFileExtensionsNumeric(null, 'abc') < 0, 'null should come before real files without extensions');
+ assert(compareFileExtensionsNumeric('', '') === 0, 'empty should be equal');
+ assert(compareFileExtensionsNumeric('abc', 'abc') === 0, 'equal names should be equal');
+ assert(compareFileExtensionsNumeric('z', 'A') > 0, 'z comes after A');
+ assert(compareFileExtensionsNumeric('Z', 'a') > 0, 'Z comes after a');
+
+ // name plus extension comparisons
+ assert(compareFileExtensionsNumeric('file.ext', 'file.ext') === 0, 'equal full filenames should be equal');
+ assert(compareFileExtensionsNumeric('a.ext', 'b.ext') < 0, 'if equal extensions, filenames should be compared');
+ assert(compareFileExtensionsNumeric('file.aaa', 'file.bbb') < 0, 'files with equal names should be compared by extensions');
+ assert(compareFileExtensionsNumeric('bbb.aaa', 'aaa.bbb') < 0, 'files should be compared by extension first');
+ assert(compareFileExtensionsNumeric('agg.go', 'aggrepo.go') < 0, 'shorter names sort before longer names');
+ assert(compareFileExtensionsNumeric('agg.go', 'agg_repo.go') < 0, 'shorter names short before longer names even when the longer name contains an underscore');
+ assert(compareFileExtensionsNumeric('a.MD', 'b.md') < 0, 'when extensions are the same except for case, the files sort by name');
+
+ // dotfile comparisons
+ assert(compareFileExtensionsNumeric('.abc', '.abc') === 0, 'equal dotfiles should be equal');
+ assert(compareFileExtensionsNumeric('.md', '.Gitattributes') > 0, 'dotfiles sort alphabetically regardless of case');
+
+ // dotfile vs non-dotfile comparisons
+ assert(compareFileExtensionsNumeric(null, '.abc') < 0, 'null should come before dotfiles');
+ assert(compareFileExtensionsNumeric('.env', 'aaa.env') < 0, 'dotfiles come before filenames with extensions');
+ assert(compareFileExtensionsNumeric('.MD', 'a.md') < 0, 'dotfiles sort before lowercase files');
+
+ // numeric comparisons
+ assert(compareFileExtensionsNumeric('1', '1') === 0, 'numerically equal full names should be equal');
+ assert(compareFileExtensionsNumeric('abc1.txt', 'abc1.txt') === 0, 'equal filenames with numbers should be equal');
+ assert(compareFileExtensionsNumeric('abc1.txt', 'abc2.txt') < 0, 'filenames with numbers should be in numerical order, not alphabetical order');
+ assert(compareFileExtensionsNumeric('abc2.txt', 'abc10.txt') < 0, 'filenames with numbers should be in numerical order');
+ assert(compareFileExtensionsNumeric('abc02.txt', 'abc010.txt') < 0, 'filenames with numbers that have leading zeros sort numerically');
+ assert(compareFileExtensionsNumeric('abc1.10.txt', 'abc1.2.txt') > 0, 'numbers with dots between them are treated as two separate numbers, not one decimal number');
+ assert(compareFileExtensionsNumeric('abc2.txt2', 'abc1.txt10') < 0, 'extensions with numbers should be in numerical order, not alphabetical order');
+ assert(compareFileExtensionsNumeric('txt.abc1', 'txt.abc1') === 0, 'equal extensions with numbers should be equal');
+ assert(compareFileExtensionsNumeric('txt.abc1', 'txt.abc2') < 0, 'extensions with numbers should be in numerical order, not alphabetical order');
+ assert(compareFileExtensionsNumeric('txt.abc2', 'txt.abc10') < 0, 'extensions with numbers should be in numerical order even when they are multiple digits long');
+ assert(compareFileExtensionsNumeric('a.ext1', 'b.ext1') < 0, 'if equal extensions with numbers, filenames should be compared');
+ assert(compareFileExtensionsNumeric('a10.txt', 'A2.txt') > 0, 'filenames with number and case differences compare numerically');
+
+ // Same extension comparison that has the same result as compareFileExtensions, but a different result than compareFileNames
+ // This is an edge case caused by compareFileNames comparing the whole name all at once instead of the name and then the extension.
+ assert(compareFileExtensionsNumeric('aggregate.go', 'aggregate_repo.go') < 0, 'when extensions are equal, names sort in dictionary order');
+
+ //
+ // Comparisons with different results than compareFileExtensions
+ //
+
+ // name-only comparisons
+ assert(compareFileExtensionsNumeric('a', 'A') === compareLocale('a', 'A'), 'the same letter of different case sorts by locale');
+ assert(compareFileExtensionsNumeric('â', 'Â') === compareLocale('â', 'Â'), 'the same accented letter of different case sorts by locale');
+ assert.deepEqual(['artichoke', 'Artichoke', 'art', 'Art'].sort(compareFileExtensionsNumeric), ['artichoke', 'Artichoke', 'art', 'Art'].sort(compareLocale), 'words with the same root and different cases sort in locale order');
+ assert.deepEqual(['email', 'Email', 'émail', 'Émail'].sort(compareFileExtensionsNumeric), ['email', 'Email', 'émail', 'Émail'].sort((a, b) => a.localeCompare(b)), 'the same base characters with different case or accents sort in locale order');
+
+ // name plus extension comparisons
+ assert(compareFileExtensionsNumeric('a.MD', 'a.md') === compareLocale('MD', 'md'), 'case differences in extensions sort by locale');
+ assert(compareFileExtensionsNumeric('a.md', 'A.md') === compareLocale('a', 'A'), 'case differences in names sort by locale');
+
+ // dotfile comparisons
+ assert(compareFileExtensionsNumeric('.env', '.aaa.env') > 0, 'dotfiles sort alphabetically when they contain multiple dots');
+ assert(compareFileExtensionsNumeric('.env', '.env.aaa') < 0, 'dotfiles with the same root sort shortest first');
+
+ // dotfile vs non-dotfile comparisons
+ assert(compareFileExtensionsNumeric('.env', 'aaa') < 0, 'dotfiles come before filenames without extensions');
+ assert(compareFileExtensionsNumeric('.md', 'A.MD') < 0, 'dotfiles sort before uppercase files');
+
+ // numeric comparisons
+ assert(compareFileExtensionsNumeric('abc.txt01', 'abc.txt1') > 0, 'extensions with equal numbers should be in shortest-first order');
+ assert(compareFileExtensionsNumeric('art01', 'Art01') === compareLocaleNumeric('art01', 'Art01'), 'a numerically equivalent word of a different case compares numerically based on locale');
+ assert(compareFileExtensionsNumeric('abc02.txt', 'abc002.txt') < 0, 'filenames with equivalent numbers and leading zeros sort shortest string first');
+ assert(compareFileExtensionsNumeric('txt.abc01', 'txt.abc1') > 0, 'extensions with equivalent numbers sort shortest extension first');
+
});
});
diff --git a/src/vs/base/test/common/map.test.ts b/src/vs/base/test/common/map.test.ts
index c9a99858854..7d7f768e3bc 100644
--- a/src/vs/base/test/common/map.test.ts
+++ b/src/vs/base/test/common/map.test.ts
@@ -13,8 +13,8 @@ suite('Map', () => {
let map = new LinkedMap();
map.set('ak', 'av');
map.set('bk', 'bv');
- assert.deepStrictEqual(map.keys(), ['ak', 'bk']);
- assert.deepStrictEqual(map.values(), ['av', 'bv']);
+ assert.deepStrictEqual([...map.keys()], ['ak', 'bk']);
+ assert.deepStrictEqual([...map.values()], ['av', 'bv']);
assert.equal(map.first, 'av');
assert.equal(map.last, 'bv');
});
@@ -23,16 +23,16 @@ suite('Map', () => {
let map = new LinkedMap();
map.set('ak', 'av');
map.set('ak', 'av', Touch.AsOld);
- assert.deepStrictEqual(map.keys(), ['ak']);
- assert.deepStrictEqual(map.values(), ['av']);
+ assert.deepStrictEqual([...map.keys()], ['ak']);
+ assert.deepStrictEqual([...map.values()], ['av']);
});
test('LinkedMap - Touch New one', () => {
let map = new LinkedMap();
map.set('ak', 'av');
map.set('ak', 'av', Touch.AsNew);
- assert.deepStrictEqual(map.keys(), ['ak']);
- assert.deepStrictEqual(map.values(), ['av']);
+ assert.deepStrictEqual([...map.keys()], ['ak']);
+ assert.deepStrictEqual([...map.values()], ['av']);
});
test('LinkedMap - Touch Old two', () => {
@@ -40,8 +40,8 @@ suite('Map', () => {
map.set('ak', 'av');
map.set('bk', 'bv');
map.set('bk', 'bv', Touch.AsOld);
- assert.deepStrictEqual(map.keys(), ['bk', 'ak']);
- assert.deepStrictEqual(map.values(), ['bv', 'av']);
+ assert.deepStrictEqual([...map.keys()], ['bk', 'ak']);
+ assert.deepStrictEqual([...map.values()], ['bv', 'av']);
});
test('LinkedMap - Touch New two', () => {
@@ -49,8 +49,8 @@ suite('Map', () => {
map.set('ak', 'av');
map.set('bk', 'bv');
map.set('ak', 'av', Touch.AsNew);
- assert.deepStrictEqual(map.keys(), ['bk', 'ak']);
- assert.deepStrictEqual(map.values(), ['bv', 'av']);
+ assert.deepStrictEqual([...map.keys()], ['bk', 'ak']);
+ assert.deepStrictEqual([...map.values()], ['bv', 'av']);
});
test('LinkedMap - Touch Old from middle', () => {
@@ -59,8 +59,8 @@ suite('Map', () => {
map.set('bk', 'bv');
map.set('ck', 'cv');
map.set('bk', 'bv', Touch.AsOld);
- assert.deepStrictEqual(map.keys(), ['bk', 'ak', 'ck']);
- assert.deepStrictEqual(map.values(), ['bv', 'av', 'cv']);
+ assert.deepStrictEqual([...map.keys()], ['bk', 'ak', 'ck']);
+ assert.deepStrictEqual([...map.values()], ['bv', 'av', 'cv']);
});
test('LinkedMap - Touch New from middle', () => {
@@ -69,8 +69,8 @@ suite('Map', () => {
map.set('bk', 'bv');
map.set('ck', 'cv');
map.set('bk', 'bv', Touch.AsNew);
- assert.deepStrictEqual(map.keys(), ['ak', 'ck', 'bk']);
- assert.deepStrictEqual(map.values(), ['av', 'cv', 'bv']);
+ assert.deepStrictEqual([...map.keys()], ['ak', 'ck', 'bk']);
+ assert.deepStrictEqual([...map.values()], ['av', 'cv', 'bv']);
});
test('LinkedMap - basics', function () {
@@ -129,6 +129,61 @@ suite('Map', () => {
assert.ok(!map.has('1'));
});
+ test('LinkedMap - Iterators', () => {
+ const map = new LinkedMap();
+ map.set(1, 1);
+ map.set(2, 2);
+ map.set(3, 3);
+
+ for (const elem of map.keys()) {
+ assert.ok(elem);
+ }
+
+ for (const elem of map.values()) {
+ assert.ok(elem);
+ }
+
+ for (const elem of map.entries()) {
+ assert.ok(elem);
+ }
+
+ {
+ const keys = map.keys();
+ const values = map.values();
+ const entries = map.entries();
+ map.get(1);
+ keys.next();
+ values.next();
+ entries.next();
+ }
+
+ {
+ const keys = map.keys();
+ const values = map.values();
+ const entries = map.entries();
+ map.get(1, Touch.AsNew);
+
+ let exceptions: number = 0;
+ try {
+ keys.next();
+ } catch (err) {
+ exceptions++;
+ }
+ try {
+ values.next();
+ } catch (err) {
+ exceptions++;
+ }
+ try {
+ entries.next();
+ } catch (err) {
+ exceptions++;
+ }
+
+ assert.strictEqual(exceptions, 3);
+ }
+ });
+
test('LinkedMap - LRU Cache simple', () => {
const cache = new LRUCache(5);
@@ -136,10 +191,10 @@ suite('Map', () => {
assert.strictEqual(cache.size, 5);
cache.set(6, 6);
assert.strictEqual(cache.size, 5);
- assert.deepStrictEqual(cache.keys(), [2, 3, 4, 5, 6]);
+ assert.deepStrictEqual([...cache.keys()], [2, 3, 4, 5, 6]);
cache.set(7, 7);
assert.strictEqual(cache.size, 5);
- assert.deepStrictEqual(cache.keys(), [3, 4, 5, 6, 7]);
+ assert.deepStrictEqual([...cache.keys()], [3, 4, 5, 6, 7]);
let values: number[] = [];
[3, 4, 5, 6, 7].forEach(key => values.push(cache.get(key)!));
assert.deepStrictEqual(values, [3, 4, 5, 6, 7]);
@@ -150,11 +205,11 @@ suite('Map', () => {
[1, 2, 3, 4, 5].forEach(value => cache.set(value, value));
assert.strictEqual(cache.size, 5);
- assert.deepStrictEqual(cache.keys(), [1, 2, 3, 4, 5]);
+ assert.deepStrictEqual([...cache.keys()], [1, 2, 3, 4, 5]);
cache.get(3);
- assert.deepStrictEqual(cache.keys(), [1, 2, 4, 5, 3]);
+ assert.deepStrictEqual([...cache.keys()], [1, 2, 4, 5, 3]);
cache.peek(4);
- assert.deepStrictEqual(cache.keys(), [1, 2, 4, 5, 3]);
+ assert.deepStrictEqual([...cache.keys()], [1, 2, 4, 5, 3]);
let values: number[] = [];
[1, 2, 3, 4, 5].forEach(key => values.push(cache.get(key)!));
assert.deepStrictEqual(values, [1, 2, 3, 4, 5]);
@@ -169,7 +224,7 @@ suite('Map', () => {
assert.strictEqual(cache.size, 10);
cache.limit = 5;
assert.strictEqual(cache.size, 5);
- assert.deepStrictEqual(cache.keys(), [6, 7, 8, 9, 10]);
+ assert.deepStrictEqual([...cache.keys()], [6, 7, 8, 9, 10]);
cache.limit = 20;
assert.strictEqual(cache.size, 5);
for (let i = 11; i <= 20; i++) {
@@ -181,7 +236,7 @@ suite('Map', () => {
values.push(cache.get(i)!);
assert.strictEqual(cache.get(i), i);
}
- assert.deepStrictEqual(cache.values(), values);
+ assert.deepStrictEqual([...cache.values()], values);
});
test('LinkedMap - LRU Cache limit with ratio', () => {
@@ -193,11 +248,11 @@ suite('Map', () => {
assert.strictEqual(cache.size, 10);
cache.set(11, 11);
assert.strictEqual(cache.size, 5);
- assert.deepStrictEqual(cache.keys(), [7, 8, 9, 10, 11]);
+ assert.deepStrictEqual([...cache.keys()], [7, 8, 9, 10, 11]);
let values: number[] = [];
- cache.keys().forEach(key => values.push(cache.get(key)!));
+ [...cache.keys()].forEach(key => values.push(cache.get(key)!));
assert.deepStrictEqual(values, [7, 8, 9, 10, 11]);
- assert.deepStrictEqual(cache.values(), values);
+ assert.deepStrictEqual([...cache.values()], values);
});
test('LinkedMap - toJSON / fromJSON', () => {
@@ -222,7 +277,6 @@ suite('Map', () => {
assert.equal(key, 'ck');
assert.equal(value, 'cv');
}
-
i++;
});
});
@@ -237,7 +291,7 @@ suite('Map', () => {
map.delete('1');
assert.equal(map.get('1'), undefined);
assert.equal(map.size, 0);
- assert.equal(map.keys().length, 0);
+ assert.equal([...map.keys()].length, 0);
});
test('LinkedMap - delete Head', function () {
@@ -251,8 +305,8 @@ suite('Map', () => {
map.delete('1');
assert.equal(map.get('2'), 2);
assert.equal(map.size, 1);
- assert.equal(map.keys().length, 1);
- assert.equal(map.keys()[0], 2);
+ assert.equal([...map.keys()].length, 1);
+ assert.equal([...map.keys()][0], 2);
});
test('LinkedMap - delete Tail', function () {
@@ -266,8 +320,8 @@ suite('Map', () => {
map.delete('2');
assert.equal(map.get('1'), 1);
assert.equal(map.size, 1);
- assert.equal(map.keys().length, 1);
- assert.equal(map.keys()[0], 1);
+ assert.equal([...map.keys()].length, 1);
+ assert.equal([...map.keys()][0], 1);
});
diff --git a/src/vs/code/electron-browser/issue/media/issueReporter.css b/src/vs/code/electron-browser/issue/media/issueReporter.css
index eb4f53747a8..ad29591ada0 100644
--- a/src/vs/code/electron-browser/issue/media/issueReporter.css
+++ b/src/vs/code/electron-browser/issue/media/issueReporter.css
@@ -95,25 +95,25 @@ textarea, input, select {
}
html {
- font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif;
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif;
color: #CCCCCC;
height: 100%;
}
html:lang(zh-Hans) {
- font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Noto Sans", "Microsoft YaHei", "PingFang SC", "Hiragino Sans GB", "Source Han Sans SC", "Source Han Sans CN", "Source Han Sans", sans-serif;
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Noto Sans", "Microsoft YaHei", "PingFang SC", "Hiragino Sans GB", "Source Han Sans SC", "Source Han Sans CN", "Source Han Sans", sans-serif;
}
html:lang(zh-Hant) {
- font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Noto Sans", "Microsoft Jhenghei", "PingFang TC", "Source Han Sans TC", "Source Han Sans", "Source Han Sans TW", sans-serif;
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Noto Sans", "Microsoft Jhenghei", "PingFang TC", "Source Han Sans TC", "Source Han Sans", "Source Han Sans TW", sans-serif;
}
html:lang(ja) {
- font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Noto Sans", "Yu Gothic UI", "Meiryo UI", "Hiragino Kaku Gothic Pro", "Source Han Sans J", "Source Han Sans JP", "Source Han Sans", "Sazanami Gothic", "IPA Gothic", sans-serif;
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Noto Sans", "Yu Gothic UI", "Meiryo UI", "Hiragino Kaku Gothic Pro", "Source Han Sans J", "Source Han Sans JP", "Source Han Sans", "Sazanami Gothic", "IPA Gothic", sans-serif;
}
html:lang(ko) {
- font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Noto Sans", "Malgun Gothic", "Nanum Gothic", "Dotom", "Apple SD Gothic Neo", "AppleGothic", "Source Han Sans K", "Source Han Sans JR", "Source Han Sans", "UnDotum", "FBaekmuk Gulim", sans-serif;
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Noto Sans", "Malgun Gothic", "Nanum Gothic", "Dotom", "Apple SD Gothic Neo", "AppleGothic", "Source Han Sans K", "Source Han Sans JR", "Source Han Sans", "UnDotum", "FBaekmuk Gulim", sans-serif;
}
body {
diff --git a/src/vs/code/electron-browser/processExplorer/media/processExplorer.css b/src/vs/code/electron-browser/processExplorer/media/processExplorer.css
index 69b0b9a35c9..606ec4c84a8 100644
--- a/src/vs/code/electron-browser/processExplorer/media/processExplorer.css
+++ b/src/vs/code/electron-browser/processExplorer/media/processExplorer.css
@@ -4,24 +4,24 @@
*--------------------------------------------------------------------------------------------*/
html {
- font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif;
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif;
font-size: 13px;
}
html:lang(zh-Hans) {
- font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Noto Sans", "Microsoft YaHei", "PingFang SC", "Hiragino Sans GB", "Source Han Sans SC", "Source Han Sans CN", "Source Han Sans", sans-serif;
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Noto Sans", "Microsoft YaHei", "PingFang SC", "Hiragino Sans GB", "Source Han Sans SC", "Source Han Sans CN", "Source Han Sans", sans-serif;
}
html:lang(zh-Hant) {
- font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Noto Sans", "Microsoft Jhenghei", "PingFang TC", "Source Han Sans TC", "Source Han Sans", "Source Han Sans TW", sans-serif;
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Noto Sans", "Microsoft Jhenghei", "PingFang TC", "Source Han Sans TC", "Source Han Sans", "Source Han Sans TW", sans-serif;
}
html:lang(ja) {
- font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Noto Sans", "Yu Gothic UI", "Meiryo UI", "Hiragino Kaku Gothic Pro", "Source Han Sans J", "Source Han Sans JP", "Source Han Sans", "Sazanami Gothic", "IPA Gothic", sans-serif;
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Noto Sans", "Yu Gothic UI", "Meiryo UI", "Hiragino Kaku Gothic Pro", "Source Han Sans J", "Source Han Sans JP", "Source Han Sans", "Sazanami Gothic", "IPA Gothic", sans-serif;
}
html:lang(ko) {
- font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Noto Sans", "Malgun Gothic", "Nanum Gothic", "Dotom", "Apple SD Gothic Neo", "AppleGothic", "Source Han Sans K", "Source Han Sans JR", "Source Han Sans", "UnDotum", "FBaekmuk Gulim", sans-serif;
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Noto Sans", "Malgun Gothic", "Nanum Gothic", "Dotom", "Apple SD Gothic Neo", "AppleGothic", "Source Han Sans K", "Source Han Sans JR", "Source Han Sans", "UnDotum", "FBaekmuk Gulim", sans-serif;
}
body {
diff --git a/src/vs/code/electron-browser/proxy/auth.html b/src/vs/code/electron-browser/proxy/auth.html
index 5ef195878ca..f0fc7231e34 100644
--- a/src/vs/code/electron-browser/proxy/auth.html
+++ b/src/vs/code/electron-browser/proxy/auth.html
@@ -5,7 +5,7 @@
+ content="default-src 'none'; img-src 'self' https: data:; media-src 'none'; child-src 'self'; object-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; connect-src 'self' https:; font-src 'self' https:;">
diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts
index 3a20695a397..fd287c25045 100644
--- a/src/vs/code/electron-main/app.ts
+++ b/src/vs/code/electron-main/app.ts
@@ -125,7 +125,7 @@ export class CodeApplication extends Disposable {
// Mac only event: open new window when we get activated
if (!hasVisibleWindows && this.windowsMainService) {
- this.windowsMainService.openEmptyWindow(OpenContext.DOCK);
+ this.windowsMainService.openEmptyWindow({ context: OpenContext.DOCK });
}
});
@@ -258,7 +258,7 @@ export class CodeApplication extends Disposable {
app.on('new-window-for-tab', () => {
if (this.windowsMainService) {
- this.windowsMainService.openEmptyWindow(OpenContext.DESKTOP); //macOS native tab "+" button
+ this.windowsMainService.openEmptyWindow({ context: OpenContext.DESKTOP }); //macOS native tab "+" button
}
});
diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts
index 19c04a25e65..4cd972ef3c3 100644
--- a/src/vs/editor/common/config/editorOptions.ts
+++ b/src/vs/editor/common/config/editorOptions.ts
@@ -2872,8 +2872,8 @@ class EditorScrollbar extends BaseEditorOption {
} else if (a instanceof OutlineElement && b instanceof OutlineElement) {
if (this.type === OutlineSortOrder.ByKind) {
- return a.symbol.kind - b.symbol.kind || this._collator.getValue().compare(a.symbol.name, b.symbol.name);
+ return a.symbol.kind - b.symbol.kind || this._collator.value.compare(a.symbol.name, b.symbol.name);
} else if (this.type === OutlineSortOrder.ByName) {
- return this._collator.getValue().compare(a.symbol.name, b.symbol.name) || Range.compareRangesUsingStarts(a.symbol.range, b.symbol.range);
+ return this._collator.value.compare(a.symbol.name, b.symbol.name) || Range.compareRangesUsingStarts(a.symbol.range, b.symbol.range);
} else if (this.type === OutlineSortOrder.ByPosition) {
- return Range.compareRangesUsingStarts(a.symbol.range, b.symbol.range) || this._collator.getValue().compare(a.symbol.name, b.symbol.name);
+ return Range.compareRangesUsingStarts(a.symbol.range, b.symbol.range) || this._collator.value.compare(a.symbol.name, b.symbol.name);
}
}
return 0;
diff --git a/src/vs/editor/contrib/find/findWidget.css b/src/vs/editor/contrib/find/findWidget.css
index 4882113bec5..3805801379b 100644
--- a/src/vs/editor/contrib/find/findWidget.css
+++ b/src/vs/editor/contrib/find/findWidget.css
@@ -126,10 +126,6 @@
justify-content: center;
}
-.monaco-editor .find-widget .button:not(.disabled):hover {
- background-color: rgba(0, 0, 0, 0.1);
-}
-
.monaco-editor .find-widget .button.left {
margin-left: 0;
margin-right: 3px;
@@ -211,11 +207,6 @@
margin-left: -4px;
}
-.monaco-editor.hc-black .find-widget .button:not(.disabled):hover,
-.monaco-editor.vs-dark .find-widget .button:not(.disabled):hover {
- background-color: rgba(255, 255, 255, 0.1);
-}
-
.monaco-editor.hc-black .find-widget .button:before {
position: relative;
top: 1px;
diff --git a/src/vs/editor/contrib/folding/folding.ts b/src/vs/editor/contrib/folding/folding.ts
index 6d444d41e65..2372e889922 100644
--- a/src/vs/editor/contrib/folding/folding.ts
+++ b/src/vs/editor/contrib/folding/folding.ts
@@ -15,7 +15,7 @@ import { ITextModel } from 'vs/editor/common/model';
import { registerEditorAction, registerEditorContribution, ServicesAccessor, EditorAction, registerInstantiatedEditorAction } from 'vs/editor/browser/editorExtensions';
import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser';
import { FoldingModel, setCollapseStateAtLevel, CollapseMemento, setCollapseStateLevelsDown, setCollapseStateLevelsUp, setCollapseStateForMatchingLines, setCollapseStateForType, toggleCollapseState, setCollapseStateUp } from 'vs/editor/contrib/folding/foldingModel';
-import { FoldingDecorationProvider } from './foldingDecorations';
+import { FoldingDecorationProvider, foldingCollapsedIcon, foldingExpandedIcon } from './foldingDecorations';
import { FoldingRegions, FoldingRegion } from './foldingRanges';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config/editorOptions';
@@ -909,8 +909,8 @@ registerThemingParticipant((theme, collector) => {
const editorFoldColor = theme.getColor(editorFoldForeground);
if (editorFoldColor) {
collector.addRule(`
- .monaco-editor .cldr.codicon-chevron-right,
- .monaco-editor .cldr.codicon-chevron-down {
+ .monaco-editor .cldr${foldingExpandedIcon.cssSelector},
+ .monaco-editor .cldr${foldingCollapsedIcon.cssSelector} {
color: ${editorFoldColor} !important;
}
`);
diff --git a/src/vs/editor/contrib/folding/foldingDecorations.ts b/src/vs/editor/contrib/folding/foldingDecorations.ts
index 1c7861f8ac3..c34e2c2121c 100644
--- a/src/vs/editor/contrib/folding/foldingDecorations.ts
+++ b/src/vs/editor/contrib/folding/foldingDecorations.ts
@@ -9,8 +9,8 @@ import { IDecorationProvider } from 'vs/editor/contrib/folding/foldingModel';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { Codicon, registerIcon } from 'vs/base/common/codicons';
-const foldingExpandedIcon = registerIcon('folding-expanded', Codicon.chevronDown);
-const foldingCollapsedIcon = registerIcon('folding-collapsed', Codicon.chevronRight);
+export const foldingExpandedIcon = registerIcon('folding-expanded', Codicon.chevronDown);
+export const foldingCollapsedIcon = registerIcon('folding-collapsed', Codicon.chevronRight);
export class FoldingDecorationProvider implements IDecorationProvider {
diff --git a/src/vs/editor/contrib/gotoError/gotoError.ts b/src/vs/editor/contrib/gotoError/gotoError.ts
index c785054b50d..001ed3f273b 100644
--- a/src/vs/editor/contrib/gotoError/gotoError.ts
+++ b/src/vs/editor/contrib/gotoError/gotoError.ts
@@ -4,12 +4,11 @@
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
-import { Emitter } from 'vs/base/common/event';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
import { RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
-import { IMarker, IMarkerService, MarkerSeverity } from 'vs/platform/markers/common/markers';
+import { IMarker } from 'vs/platform/markers/common/markers';
import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { IEditorContribution } from 'vs/editor/common/editorCommon';
@@ -18,194 +17,32 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { MarkerNavigationWidget } from './gotoErrorWidget';
-import { compare } from 'vs/base/common/strings';
-import { binarySearch, find } from 'vs/base/common/arrays';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
-import { onUnexpectedError } from 'vs/base/common/errors';
-import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
-import { isEqual } from 'vs/base/common/resources';
+import { MenuId } from 'vs/platform/actions/common/actions';
import { TextEditorSelectionRevealType } from 'vs/platform/editor/common/editor';
import { Codicon, registerIcon } from 'vs/base/common/codicons';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
-
-class MarkerModel {
-
- private readonly _editor: ICodeEditor;
- private _markers: IMarker[];
- private _nextIdx: number;
- private readonly _toUnbind = new DisposableStore();
- private _ignoreSelectionChange: boolean;
- private readonly _onCurrentMarkerChanged: Emitter;
- private readonly _onMarkerSetChanged: Emitter;
-
- constructor(editor: ICodeEditor, markers: IMarker[]) {
- this._editor = editor;
- this._markers = [];
- this._nextIdx = -1;
- this._ignoreSelectionChange = false;
- this._onCurrentMarkerChanged = new Emitter();
- this._onMarkerSetChanged = new Emitter();
- this.setMarkers(markers);
-
- // listen on editor
- this._toUnbind.add(this._editor.onDidDispose(() => this.dispose()));
- this._toUnbind.add(this._editor.onDidChangeCursorPosition(() => {
- if (this._ignoreSelectionChange) {
- return;
- }
- if (this.currentMarker && this._editor.getPosition() && Range.containsPosition(this.currentMarker, this._editor.getPosition()!)) {
- return;
- }
- this._nextIdx = -1;
- }));
- }
-
- public get onCurrentMarkerChanged() {
- return this._onCurrentMarkerChanged.event;
- }
-
- public get onMarkerSetChanged() {
- return this._onMarkerSetChanged.event;
- }
-
- public setMarkers(markers: IMarker[]): void {
-
- let oldMarker = this._nextIdx >= 0 ? this._markers[this._nextIdx] : undefined;
- this._markers = markers || [];
- this._markers.sort(MarkerNavigationAction.compareMarker);
- if (!oldMarker) {
- this._nextIdx = -1;
- } else {
- this._nextIdx = Math.max(-1, binarySearch(this._markers, oldMarker, MarkerNavigationAction.compareMarker));
- }
- this._onMarkerSetChanged.fire(this);
- }
-
- public withoutWatchingEditorPosition(callback: () => void): void {
- this._ignoreSelectionChange = true;
- try {
- callback();
- } finally {
- this._ignoreSelectionChange = false;
- }
- }
-
- private _initIdx(fwd: boolean): void {
- let found = false;
- const position = this._editor.getPosition();
- for (let i = 0; i < this._markers.length; i++) {
- let range = Range.lift(this._markers[i]);
-
- if (range.isEmpty() && this._editor.getModel()) {
- const word = this._editor.getModel()!.getWordAtPosition(range.getStartPosition());
- if (word) {
- range = new Range(range.startLineNumber, word.startColumn, range.startLineNumber, word.endColumn);
- }
- }
-
- if (position && (range.containsPosition(position) || position.isBeforeOrEqual(range.getStartPosition()))) {
- this._nextIdx = i;
- found = true;
- break;
- }
- }
- if (!found) {
- // after the last change
- this._nextIdx = fwd ? 0 : this._markers.length - 1;
- }
- if (this._nextIdx < 0) {
- this._nextIdx = this._markers.length - 1;
- }
- }
-
- get currentMarker(): IMarker | undefined {
- return this.canNavigate() ? this._markers[this._nextIdx] : undefined;
- }
-
- set currentMarker(marker: IMarker | undefined) {
- const idx = this._nextIdx;
- this._nextIdx = -1;
- if (marker) {
- this._nextIdx = this.indexOf(marker);
- }
- if (this._nextIdx !== idx) {
- this._onCurrentMarkerChanged.fire(marker);
- }
- }
-
- public move(fwd: boolean, inCircles: boolean): boolean {
- if (!this.canNavigate()) {
- this._onCurrentMarkerChanged.fire(undefined);
- return !inCircles;
- }
-
- let oldIdx = this._nextIdx;
- let atEdge = false;
-
- if (this._nextIdx === -1) {
- this._initIdx(fwd);
-
- } else if (fwd) {
- if (inCircles || this._nextIdx + 1 < this._markers.length) {
- this._nextIdx = (this._nextIdx + 1) % this._markers.length;
- } else {
- atEdge = true;
- }
-
- } else if (!fwd) {
- if (inCircles || this._nextIdx > 0) {
- this._nextIdx = (this._nextIdx - 1 + this._markers.length) % this._markers.length;
- } else {
- atEdge = true;
- }
- }
-
- if (oldIdx !== this._nextIdx) {
- const marker = this._markers[this._nextIdx];
- this._onCurrentMarkerChanged.fire(marker);
- }
-
- return atEdge;
- }
-
- public canNavigate(): boolean {
- return this._markers.length > 0;
- }
-
- public findMarkerAtPosition(pos: Position): IMarker | undefined {
- return find(this._markers, marker => Range.containsPosition(marker, pos));
- }
-
- public get total() {
- return this._markers.length;
- }
-
- public indexOf(marker: IMarker): number {
- return 1 + this._markers.indexOf(marker);
- }
-
- public dispose(): void {
- this._toUnbind.dispose();
- }
-}
+import { IMarkerNavigationService, MarkerList } from 'vs/editor/contrib/gotoError/markerNavigationService';
export class MarkerController implements IEditorContribution {
- public static readonly ID = 'editor.contrib.markerController';
+ static readonly ID = 'editor.contrib.markerController';
- public static get(editor: ICodeEditor): MarkerController {
+ static get(editor: ICodeEditor): MarkerController {
return editor.getContribution(MarkerController.ID);
}
private readonly _editor: ICodeEditor;
- private _model: MarkerModel | null = null;
- private _widget: MarkerNavigationWidget | null = null;
+
private readonly _widgetVisible: IContextKey;
- private readonly _disposeOnClose = new DisposableStore();
+ private readonly _sessionDispoables = new DisposableStore();
+
+ private _model?: MarkerList;
+ private _widget?: MarkerNavigationWidget;
constructor(
editor: ICodeEditor,
- @IMarkerService private readonly _markerService: IMarkerService,
+ @IMarkerNavigationService private readonly _markerNavigationService: IMarkerNavigationService,
@IContextKeyService private readonly _contextKeyService: IContextKeyService,
@ICodeEditorService private readonly _editorService: ICodeEditorService,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@@ -214,195 +51,134 @@ export class MarkerController implements IEditorContribution {
this._widgetVisible = CONTEXT_MARKERS_NAVIGATION_VISIBLE.bindTo(this._contextKeyService);
}
- public dispose(): void {
+ dispose(): void {
this._cleanUp();
- this._disposeOnClose.dispose();
+ this._sessionDispoables.dispose();
}
private _cleanUp(): void {
this._widgetVisible.reset();
- this._disposeOnClose.clear();
- this._widget = null;
- this._model = null;
+ this._sessionDispoables.clear();
+ this._widget = undefined;
+ this._model = undefined;
}
- public getOrCreateModel(): MarkerModel {
+ private _getOrCreateModel(uri: URI | undefined): MarkerList {
- if (this._model) {
+ if (this._model && this._model.matches(uri)) {
return this._model;
}
+ let reusePosition = false;
+ if (this._model) {
+ reusePosition = true;
+ this._cleanUp();
+ }
- const markers = this._getMarkers();
- this._model = new MarkerModel(this._editor, markers);
- this._markerService.onMarkerChanged(this._onMarkerChanged, this, this._disposeOnClose);
+ this._model = this._markerNavigationService.getMarkerList(uri);
+ if (reusePosition) {
+ this._model.move(true, this._editor.getModel()!, this._editor.getPosition()!);
+ }
this._widget = this._instantiationService.createInstance(MarkerNavigationWidget, this._editor);
+ this._widget.onDidClose(() => this.close(), this, this._sessionDispoables);
this._widgetVisible.set(true);
- this._widget.onDidClose(() => this.closeMarkersNavigation(), this, this._disposeOnClose);
- this._disposeOnClose.add(this._model);
- this._disposeOnClose.add(this._widget);
+ this._sessionDispoables.add(this._model);
+ this._sessionDispoables.add(this._widget);
- this._disposeOnClose.add(this._widget.onDidSelectRelatedInformation(related => {
- this._editorService.openCodeEditor({
- resource: related.resource,
- options: { pinned: true, revealIfOpened: true, selection: Range.lift(related).collapseToStart() }
- }, this._editor).then(undefined, onUnexpectedError);
- this.closeMarkersNavigation(false);
- }));
- this._disposeOnClose.add(this._editor.onDidChangeModel(() => this._cleanUp()));
-
- this._disposeOnClose.add(this._model.onCurrentMarkerChanged(marker => {
- if (!marker || !this._model) {
- this._cleanUp();
- } else {
- this._model.withoutWatchingEditorPosition(() => {
- if (!this._widget || !this._model) {
- return;
- }
- this._widget.showAtMarker(marker, this._model.indexOf(marker), this._model.total);
- });
+ // follow cursor
+ this._sessionDispoables.add(this._editor.onDidChangeCursorPosition(e => {
+ if (!this._model?.selected || !Range.containsPosition(this._model?.selected.marker, e.position)) {
+ this._model?.resetIndex();
}
}));
- this._disposeOnClose.add(this._model.onMarkerSetChanged(() => {
+
+ // update markers
+ this._sessionDispoables.add(this._model.onDidChange(() => {
if (!this._widget || !this._widget.position || !this._model) {
return;
}
-
- const marker = this._model.findMarkerAtPosition(this._widget.position);
- if (marker) {
- this._widget.updateMarker(marker);
+ const info = this._model.find(this._editor.getModel()!.uri, this._widget!.position!);
+ if (info) {
+ this._widget.updateMarker(info.marker);
} else {
this._widget.showStale();
}
}));
+ // open related
+ this._sessionDispoables.add(this._widget.onDidSelectRelatedInformation(related => {
+ this._editorService.openCodeEditor({
+ resource: related.resource,
+ options: { pinned: true, revealIfOpened: true, selection: Range.lift(related).collapseToStart() }
+ }, this._editor);
+ this.close(false);
+ }));
+ this._sessionDispoables.add(this._editor.onDidChangeModel(() => this._cleanUp()));
+
return this._model;
}
- public closeMarkersNavigation(focusEditor: boolean = true): void {
+ close(focusEditor: boolean = true): void {
this._cleanUp();
if (focusEditor) {
this._editor.focus();
}
}
- public show(marker: IMarker): void {
- const model = this.getOrCreateModel();
- model.currentMarker = marker;
+ showAtMarker(marker: IMarker): void {
+ if (this._editor.hasModel()) {
+ const model = this._getOrCreateModel(this._editor.getModel().uri);
+ model.resetIndex();
+ model.move(true, this._editor.getModel(), new Position(marker.startLineNumber, marker.startColumn));
+ if (model.selected) {
+ this._widget!.showAtMarker(model.selected.marker, model.selected.index, model.selected.total);
+ }
+ }
}
- private _onMarkerChanged(changedResources: readonly URI[]): void {
- const editorModel = this._editor.getModel();
- if (!editorModel) {
- return;
- }
+ async nagivate(next: boolean, multiFile: boolean) {
+ if (this._editor.hasModel()) {
+ const model = this._getOrCreateModel(multiFile ? undefined : this._editor.getModel().uri);
+ model.move(next, this._editor.getModel(), this._editor.getPosition());
+ if (!model.selected) {
+ return;
+ }
+ if (model.selected.marker.resource.toString() !== this._editor.getModel().uri.toString()) {
+ // show in different editor
+ this._cleanUp();
+ const otherEditor = await this._editorService.openCodeEditor({
+ resource: model.selected.marker.resource,
+ options: { pinned: false, revealIfOpened: true, selectionRevealType: TextEditorSelectionRevealType.NearTop, selection: model.selected.marker }
+ }, this._editor);
- if (!this._model) {
- return;
- }
+ if (otherEditor) {
+ MarkerController.get(otherEditor).close();
+ MarkerController.get(otherEditor).nagivate(next, multiFile);
+ }
- if (!changedResources.some(r => isEqual(editorModel.uri, r))) {
- return;
+ } else {
+ // show in this editor
+ this._widget!.showAtMarker(model.selected.marker, model.selected.index, model.selected.total);
+ }
}
- this._model.setMarkers(this._getMarkers());
- }
-
- private _getMarkers(): IMarker[] {
- let model = this._editor.getModel();
- if (!model) {
- return [];
- }
-
- return this._markerService.read({
- resource: model.uri,
- severities: MarkerSeverity.Error | MarkerSeverity.Warning | MarkerSeverity.Info
- });
}
}
class MarkerNavigationAction extends EditorAction {
- private readonly _isNext: boolean;
-
- private readonly _multiFile: boolean;
-
- constructor(next: boolean, multiFile: boolean, opts: IActionOptions) {
+ constructor(
+ private readonly _next: boolean,
+ private readonly _multiFile: boolean,
+ opts: IActionOptions
+ ) {
super(opts);
- this._isNext = next;
- this._multiFile = multiFile;
}
- public run(accessor: ServicesAccessor, editor: ICodeEditor): Promise {
-
- const markerService = accessor.get(IMarkerService);
- const editorService = accessor.get(ICodeEditorService);
- const controller = MarkerController.get(editor);
- if (!controller) {
- return Promise.resolve(undefined);
+ async run(_accessor: ServicesAccessor, editor: ICodeEditor): Promise {
+ if (editor.hasModel()) {
+ MarkerController.get(editor).nagivate(this._next, this._multiFile);
}
-
- const model = controller.getOrCreateModel();
- const atEdge = model.move(this._isNext, !this._multiFile);
- if (!atEdge || !this._multiFile) {
- return Promise.resolve(undefined);
- }
-
- // try with the next/prev file
- let markers = markerService.read({ severities: MarkerSeverity.Error | MarkerSeverity.Warning | MarkerSeverity.Info }).sort(MarkerNavigationAction.compareMarker);
- if (markers.length === 0) {
- return Promise.resolve(undefined);
- }
-
- const editorModel = editor.getModel();
- if (!editorModel) {
- return Promise.resolve(undefined);
- }
-
- let oldMarker = model.currentMarker || { resource: editorModel!.uri, severity: MarkerSeverity.Error, startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 };
- let idx = binarySearch(markers, oldMarker, MarkerNavigationAction.compareMarker);
- if (idx < 0) {
- // find best match...
- idx = ~idx;
- idx %= markers.length;
- } else if (this._isNext) {
- idx = (idx + 1) % markers.length;
- } else {
- idx = (idx + markers.length - 1) % markers.length;
- }
-
- let newMarker = markers[idx];
- if (isEqual(newMarker.resource, editorModel.uri)) {
- // the next `resource` is this resource which
- // means we cycle within this file
- model.move(this._isNext, true);
- return Promise.resolve(undefined);
- }
-
- // close the widget for this editor-instance, open the resource
- // for the next marker and re-start marker navigation in there
- controller.closeMarkersNavigation();
-
- return editorService.openCodeEditor({
- resource: newMarker.resource,
- options: { pinned: false, revealIfOpened: true, selectionRevealType: TextEditorSelectionRevealType.NearTop, selection: newMarker }
- }, editor).then(editor => {
- if (!editor) {
- return undefined;
- }
- return editor.getAction(this.id).run();
- });
- }
-
- static compareMarker(a: IMarker, b: IMarker): number {
- let res = compare(a.resource.toString(), b.resource.toString());
- if (res === 0) {
- res = MarkerSeverity.compare(a.severity, b.severity);
- }
- if (res === 0) {
- res = Range.compareRangesUsingStarts(a, b);
- }
- return res;
}
}
@@ -467,6 +243,12 @@ class NextMarkerInFilesAction extends MarkerNavigationAction {
kbExpr: EditorContextKeys.focus,
primary: KeyCode.F8,
weight: KeybindingWeight.EditorContrib
+ },
+ menuOpts: {
+ menuId: MenuId.MenubarGoMenu,
+ title: nls.localize({ key: 'miGotoNextProblem', comment: ['&& denotes a mnemonic'] }, "Next &&Problem"),
+ group: '6_problem_nav',
+ order: 1
}
});
}
@@ -483,6 +265,12 @@ class PrevMarkerInFilesAction extends MarkerNavigationAction {
kbExpr: EditorContextKeys.focus,
primary: KeyMod.Shift | KeyCode.F8,
weight: KeybindingWeight.EditorContrib
+ },
+ menuOpts: {
+ menuId: MenuId.MenubarGoMenu,
+ title: nls.localize({ key: 'miGotoPreviousProblem', comment: ['&& denotes a mnemonic'] }, "Previous &&Problem"),
+ group: '6_problem_nav',
+ order: 2
}
});
}
@@ -501,7 +289,7 @@ const MarkerCommand = EditorCommand.bindToContribution(MarkerC
registerEditorCommand(new MarkerCommand({
id: 'closeMarkersNavigation',
precondition: CONTEXT_MARKERS_NAVIGATION_VISIBLE,
- handler: x => x.closeMarkersNavigation(),
+ handler: x => x.close(),
kbOpts: {
weight: KeybindingWeight.EditorContrib + 50,
kbExpr: EditorContextKeys.focus,
@@ -509,22 +297,3 @@ registerEditorCommand(new MarkerCommand({
secondary: [KeyMod.Shift | KeyCode.Escape]
}
}));
-
-// Go to menu
-MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, {
- group: '6_problem_nav',
- command: {
- id: 'editor.action.marker.nextInFiles',
- title: nls.localize({ key: 'miGotoNextProblem', comment: ['&& denotes a mnemonic'] }, "Next &&Problem")
- },
- order: 1
-});
-
-MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, {
- group: '6_problem_nav',
- command: {
- id: 'editor.action.marker.prevInFiles',
- title: nls.localize({ key: 'miGotoPreviousProblem', comment: ['&& denotes a mnemonic'] }, "Previous &&Problem")
- },
- order: 2
-});
diff --git a/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts b/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts
index 76ffd641d15..7c2eed05550 100644
--- a/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts
+++ b/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts
@@ -8,7 +8,6 @@ import * as nls from 'vs/nls';
import * as dom from 'vs/base/browser/dom';
import { dispose, DisposableStore } from 'vs/base/common/lifecycle';
import { IMarker, MarkerSeverity, IRelatedInformation } from 'vs/platform/markers/common/markers';
-import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { registerColor, oneOf, textLinkForeground, editorErrorForeground, editorErrorBorder, editorWarningForeground, editorWarningBorder, editorInfoForeground, editorInfoBorder } from 'vs/platform/theme/common/colorRegistry';
@@ -296,6 +295,9 @@ export class MarkerNavigationWidget extends PeekViewWidget {
protected _fillHead(container: HTMLElement): void {
super._fillHead(container);
+
+ this._disposables.add(this._actionbarWidget!.actionRunner.onDidBeforeRun(e => this.editor.focus()));
+
const actions: IAction[] = [];
const menu = this._menuService.createMenu(MarkerNavigationWidget.TitleMenu, this._contextKeyService);
createAndFillInActionBarActions(menu, undefined, actions);
@@ -327,7 +329,7 @@ export class MarkerNavigationWidget extends PeekViewWidget {
this._disposables.add(this._message);
}
- show(where: Position, heightInLines: number): void {
+ show(): void {
throw new Error('call showAtMarker');
}
diff --git a/src/vs/editor/contrib/gotoError/markerNavigationService.ts b/src/vs/editor/contrib/gotoError/markerNavigationService.ts
new file mode 100644
index 00000000000..abf0fe7a4e0
--- /dev/null
+++ b/src/vs/editor/contrib/gotoError/markerNavigationService.ts
@@ -0,0 +1,217 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { IMarkerService, MarkerSeverity, IMarker } from 'vs/platform/markers/common/markers';
+import { URI } from 'vs/base/common/uri';
+import { Emitter, Event } from 'vs/base/common/event';
+import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
+import { Position } from 'vs/editor/common/core/position';
+import { Range } from 'vs/editor/common/core/range';
+import { compare } from 'vs/base/common/strings';
+import { binarySearch } from 'vs/base/common/arrays';
+import { ITextModel } from 'vs/editor/common/model';
+import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
+import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
+import { LinkedList } from 'vs/base/common/linkedList';
+
+export class MarkerCoordinate {
+ constructor(
+ readonly marker: IMarker,
+ readonly index: number,
+ readonly total: number
+ ) { }
+}
+
+export class MarkerList {
+
+ private readonly _onDidChange = new Emitter();
+ readonly onDidChange: Event = this._onDidChange.event;
+
+ private readonly _resourceFilter?: (uri: URI) => boolean;
+ private readonly _dispoables = new DisposableStore();
+
+ private _markers: IMarker[] = [];
+ private _nextIdx: number = -1;
+
+ constructor(
+ resourceFilter: URI | ((uri: URI) => boolean) | undefined,
+ @IMarkerService private readonly _markerService: IMarkerService,
+ ) {
+ if (URI.isUri(resourceFilter)) {
+ this._resourceFilter = uri => uri.toString() === resourceFilter.toString();
+ } else if (resourceFilter) {
+ this._resourceFilter = resourceFilter;
+ }
+
+ const updateMarker = () => {
+ this._markers = this._markerService.read({
+ resource: URI.isUri(resourceFilter) ? resourceFilter : undefined,
+ severities: MarkerSeverity.Error | MarkerSeverity.Warning | MarkerSeverity.Info
+ });
+ if (typeof resourceFilter === 'function') {
+ this._markers = this._markers.filter(m => this._resourceFilter!(m.resource));
+ }
+ this._markers.sort(MarkerList._compareMarker);
+ };
+
+ updateMarker();
+
+ this._dispoables.add(_markerService.onMarkerChanged(uris => {
+ if (!this._resourceFilter || uris.some(uri => this._resourceFilter!(uri))) {
+ updateMarker();
+ this._nextIdx = -1;
+ this._onDidChange.fire();
+ }
+ }));
+ }
+
+ dispose(): void {
+ this._dispoables.dispose();
+ this._onDidChange.dispose();
+ }
+
+ matches(uri: URI | undefined) {
+ if (!this._resourceFilter && !uri) {
+ return true;
+ }
+ if (!this._resourceFilter || !uri) {
+ return false;
+ }
+ return this._resourceFilter(uri);
+ }
+
+ get selected(): MarkerCoordinate | undefined {
+ const marker = this._markers[this._nextIdx];
+ return marker && new MarkerCoordinate(marker, this._nextIdx + 1, this._markers.length);
+ }
+
+ private _initIdx(model: ITextModel, position: Position, fwd: boolean): void {
+ let found = false;
+
+ let idx = this._markers.findIndex(marker => marker.resource.toString() === model.uri.toString());
+ if (idx < 0) {
+ idx = binarySearch(this._markers, { resource: model.uri }, (a, b) => compare(a.resource.toString(), b.resource.toString()));
+ if (idx < 0) {
+ idx = ~idx;
+ }
+ }
+
+ for (let i = idx; i < this._markers.length; i++) {
+ let range = Range.lift(this._markers[i]);
+
+ if (range.isEmpty()) {
+ const word = model.getWordAtPosition(range.getStartPosition());
+ if (word) {
+ range = new Range(range.startLineNumber, word.startColumn, range.startLineNumber, word.endColumn);
+ }
+ }
+
+ if (position && (range.containsPosition(position) || position.isBeforeOrEqual(range.getStartPosition()))) {
+ this._nextIdx = i;
+ found = true;
+ break;
+ }
+
+ if (this._markers[i].resource.toString() !== model.uri.toString()) {
+ break;
+ }
+ }
+
+ if (!found) {
+ // after the last change
+ this._nextIdx = fwd ? 0 : this._markers.length - 1;
+ }
+ if (this._nextIdx < 0) {
+ this._nextIdx = this._markers.length - 1;
+ }
+ }
+
+ resetIndex() {
+ this._nextIdx = -1;
+ }
+
+ move(fwd: boolean, model: ITextModel, position: Position): boolean {
+ if (this._markers.length === 0) {
+ return false;
+ }
+
+ let oldIdx = this._nextIdx;
+ if (this._nextIdx === -1) {
+ this._initIdx(model, position, fwd);
+ } else if (fwd) {
+ this._nextIdx = (this._nextIdx + 1) % this._markers.length;
+ } else if (!fwd) {
+ this._nextIdx = (this._nextIdx - 1 + this._markers.length) % this._markers.length;
+ }
+
+ if (oldIdx !== this._nextIdx) {
+ return true;
+ }
+ return false;
+ }
+
+ find(uri: URI, position: Position): MarkerCoordinate | undefined {
+ let idx = this._markers.findIndex(marker => marker.resource.toString() === uri.toString());
+ if (idx < 0) {
+ return undefined;
+ }
+ for (; idx < this._markers.length; idx++) {
+ if (Range.containsPosition(this._markers[idx], position)) {
+ return new MarkerCoordinate(this._markers[idx], idx + 1, this._markers.length);
+ }
+ }
+ return undefined;
+ }
+
+ private static _compareMarker(a: IMarker, b: IMarker): number {
+ let res = compare(a.resource.toString(), b.resource.toString());
+ if (res === 0) {
+ res = MarkerSeverity.compare(a.severity, b.severity);
+ }
+ if (res === 0) {
+ res = Range.compareRangesUsingStarts(a, b);
+ }
+ return res;
+ }
+}
+
+export const IMarkerNavigationService = createDecorator('IMarkerNavigationService');
+
+export interface IMarkerNavigationService {
+ readonly _serviceBrand: undefined;
+ registerProvider(provider: IMarkerListProvider): IDisposable;
+ getMarkerList(resource: URI | undefined): MarkerList;
+}
+
+export interface IMarkerListProvider {
+ getMarkerList(resource: URI | undefined): MarkerList | undefined;
+}
+
+class MarkerNavigationService implements IMarkerNavigationService, IMarkerListProvider {
+
+ readonly _serviceBrand: undefined;
+
+ private readonly _provider = new LinkedList();
+
+ constructor(@IMarkerService private readonly _markerService: IMarkerService) { }
+
+ registerProvider(provider: IMarkerListProvider): IDisposable {
+ const remove = this._provider.unshift(provider);
+ return toDisposable(() => remove());
+ }
+
+ getMarkerList(resource: URI | undefined): MarkerList {
+ for (let provider of this._provider) {
+ const result = provider.getMarkerList(resource);
+ if (result) {
+ return result;
+ }
+ }
+ // default
+ return new MarkerList(resource, this._markerService);
+ }
+}
+
+registerSingleton(IMarkerNavigationService, MarkerNavigationService, true);
diff --git a/src/vs/editor/contrib/gotoError/media/gotoErrorWidget.css b/src/vs/editor/contrib/gotoError/media/gotoErrorWidget.css
index c748c365bdc..bf9d507df0a 100644
--- a/src/vs/editor/contrib/gotoError/media/gotoErrorWidget.css
+++ b/src/vs/editor/contrib/gotoError/media/gotoErrorWidget.css
@@ -32,7 +32,7 @@
user-select: text;
-webkit-user-select: text;
-ms-user-select: text;
- padding: 8px 12px 0px 20px;
+ padding: 8px 12px 0 20px;
}
.monaco-editor .marker-widget .descriptioncontainer .message {
diff --git a/src/vs/editor/contrib/hover/modesContentHover.ts b/src/vs/editor/contrib/hover/modesContentHover.ts
index 27e5d9b096d..71055810784 100644
--- a/src/vs/editor/contrib/hover/modesContentHover.ts
+++ b/src/vs/editor/contrib/hover/modesContentHover.ts
@@ -567,7 +567,7 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
commandId: NextMarkerAction.ID,
run: () => {
this.hide();
- MarkerController.get(this._editor).show(markerHover.marker);
+ MarkerController.get(this._editor).showAtMarker(markerHover.marker);
this._editor.focus();
}
}));
@@ -686,4 +686,3 @@ registerThemingParticipant((theme, collector) => {
collector.addRule(`.monaco-editor-hover .hover-contents a.code-link span:hover { color: ${linkFg}; }`);
}
});
-
diff --git a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts
index d09f3e7a246..809192427fa 100644
--- a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts
+++ b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts
@@ -296,10 +296,10 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget {
private getParameterLabel(signature: modes.SignatureInformation, paramIdx: number): string {
const param = signature.parameters[paramIdx];
- if (typeof param.label === 'string') {
- return param.label;
- } else {
+ if (Array.isArray(param.label)) {
return signature.label.substring(param.label[0], param.label[1]);
+ } else {
+ return param.label;
}
}
diff --git a/src/vs/editor/contrib/rename/rename.ts b/src/vs/editor/contrib/rename/rename.ts
index ae87a914c9b..865d05eccdf 100644
--- a/src/vs/editor/contrib/rename/rename.ts
+++ b/src/vs/editor/contrib/rename/rename.ts
@@ -182,7 +182,7 @@ class RenameController implements IEditorContribution {
}
const supportPreview = this._bulkEditService.hasPreviewHandler() && this._configService.getValue(this.editor.getModel().uri, 'editor.rename.enablePreview');
- const inputFieldResult = await this._renameInputField.getValue().getInput(loc.range, loc.text, selectionStart, selectionEnd, supportPreview, this._cts.token);
+ const inputFieldResult = await this._renameInputField.value.getInput(loc.range, loc.text, selectionStart, selectionEnd, supportPreview, this._cts.token);
// no result, only hint to focus the editor or not
if (typeof inputFieldResult === 'boolean') {
@@ -230,11 +230,11 @@ class RenameController implements IEditorContribution {
}
acceptRenameInput(wantsPreview: boolean): void {
- this._renameInputField.getValue().acceptInput(wantsPreview);
+ this._renameInputField.value.acceptInput(wantsPreview);
}
cancelRenameInput(): void {
- this._renameInputField.getValue().cancelInput(true);
+ this._renameInputField.value.cancelInput(true);
}
}
diff --git a/src/vs/editor/contrib/suggest/suggestController.ts b/src/vs/editor/contrib/suggest/suggestController.ts
index 913c86fb47a..d19c3ce2b09 100644
--- a/src/vs/editor/contrib/suggest/suggestController.ts
+++ b/src/vs/editor/contrib/suggest/suggestController.ts
@@ -204,18 +204,18 @@ export class SuggestController implements IEditorContribution {
this._toDispose.add(_instantiationService.createInstance(WordContextKey, editor));
this._toDispose.add(this.model.onDidTrigger(e => {
- this.widget.getValue().showTriggered(e.auto, e.shy ? 250 : 50);
+ this.widget.value.showTriggered(e.auto, e.shy ? 250 : 50);
this._lineSuffix.value = new LineSuffix(this.editor.getModel()!, e.position);
}));
this._toDispose.add(this.model.onDidSuggest(e => {
if (!e.shy) {
let index = this._memoryService.select(this.editor.getModel()!, this.editor.getPosition()!, e.completionModel.items);
- this.widget.getValue().showSuggestions(e.completionModel, index, e.isFrozen, e.auto);
+ this.widget.value.showSuggestions(e.completionModel, index, e.isFrozen, e.auto);
}
}));
this._toDispose.add(this.model.onDidCancel(e => {
if (!e.retrigger) {
- this.widget.getValue().hideWidget();
+ this.widget.value.hideWidget();
}
}));
this._toDispose.add(this.editor.onDidBlurEditorWidget(() => {
@@ -248,7 +248,7 @@ export class SuggestController implements IEditorContribution {
flags: InsertFlags
): void {
if (!event || !event.item) {
- this._alternatives.getValue().reset();
+ this._alternatives.value.reset();
this.model.cancel();
this.model.clear();
return;
@@ -260,7 +260,6 @@ export class SuggestController implements IEditorContribution {
const model = this.editor.getModel();
const modelVersionNow = model.getAlternativeVersionId();
const { item } = event;
- const { completion: suggestion } = item;
// pushing undo stops *before* additional text edits and
// *after* the main edit
@@ -276,12 +275,12 @@ export class SuggestController implements IEditorContribution {
const scrollState = StableEditorScrollState.capture(this.editor);
- if (Array.isArray(suggestion.additionalTextEdits)) {
- this.editor.executeEdits('suggestController.additionalTextEdits', suggestion.additionalTextEdits.map(edit => EditOperation.replace(Range.lift(edit.range), edit.text)));
+ if (Array.isArray(item.completion.additionalTextEdits)) {
+ this.editor.executeEdits('suggestController.additionalTextEdits', item.completion.additionalTextEdits.map(edit => EditOperation.replace(Range.lift(edit.range), edit.text)));
}
- let { insertText } = suggestion;
- if (!(suggestion.insertTextRules! & CompletionItemInsertTextRule.InsertAsSnippet)) {
+ let { insertText } = item.completion;
+ if (!(item.completion.insertTextRules! & CompletionItemInsertTextRule.InsertAsSnippet)) {
insertText = SnippetParser.escape(insertText);
}
@@ -290,7 +289,7 @@ export class SuggestController implements IEditorContribution {
overwriteAfter: info.overwriteAfter,
undoStopBefore: false,
undoStopAfter: false,
- adjustWhitespace: !(suggestion.insertTextRules! & CompletionItemInsertTextRule.KeepWhitespace)
+ adjustWhitespace: !(item.completion.insertTextRules! & CompletionItemInsertTextRule.KeepWhitespace)
});
scrollState.restoreRelativeVerticalPositionOfCursor(this.editor);
@@ -299,25 +298,25 @@ export class SuggestController implements IEditorContribution {
this.editor.pushUndoStop();
}
- if (!suggestion.command) {
+ if (!item.completion.command) {
// done
this.model.cancel();
this.model.clear();
- } else if (suggestion.command.id === TriggerSuggestAction.id) {
+ } else if (item.completion.command.id === TriggerSuggestAction.id) {
// retigger
this.model.trigger({ auto: true, shy: false }, true);
} else {
// exec command, done
- this._commandService.executeCommand(suggestion.command.id, ...(suggestion.command.arguments ? [...suggestion.command.arguments] : []))
+ this._commandService.executeCommand(item.completion.command.id, ...(item.completion.command.arguments ? [...item.completion.command.arguments] : []))
.catch(onUnexpectedError)
.finally(() => this.model.clear()); // <- clear only now, keep commands alive
this.model.cancel();
}
if (flags & InsertFlags.KeepAlternativeSuggestions) {
- this._alternatives.getValue().set(event, next => {
+ this._alternatives.value.set(event, next => {
// this is not so pretty. when inserting the 'next'
// suggestion we undo until we are at the state at
// which we were before inserting the previous suggestion...
@@ -334,7 +333,7 @@ export class SuggestController implements IEditorContribution {
});
}
- this._alertCompletionItem(event.item);
+ this._alertCompletionItem(item);
}
getOverwriteInfo(item: CompletionItem, toggleMode: boolean): { overwriteBefore: number, overwriteAfter: number } {
@@ -440,7 +439,7 @@ export class SuggestController implements IEditorContribution {
}
acceptSelectedSuggestion(keepAlternativeSuggestions: boolean, alternativeOverwriteConfig: boolean): void {
- const item = this.widget.getValue().getFocusedItem();
+ const item = this.widget.value.getFocusedItem();
let flags = 0;
if (keepAlternativeSuggestions) {
flags |= InsertFlags.KeepAlternativeSuggestions;
@@ -451,53 +450,53 @@ export class SuggestController implements IEditorContribution {
this._insertSuggestion(item, flags);
}
acceptNextSuggestion() {
- this._alternatives.getValue().next();
+ this._alternatives.value.next();
}
acceptPrevSuggestion() {
- this._alternatives.getValue().prev();
+ this._alternatives.value.prev();
}
cancelSuggestWidget(): void {
this.model.cancel();
this.model.clear();
- this.widget.getValue().hideWidget();
+ this.widget.value.hideWidget();
}
selectNextSuggestion(): void {
- this.widget.getValue().selectNext();
+ this.widget.value.selectNext();
}
selectNextPageSuggestion(): void {
- this.widget.getValue().selectNextPage();
+ this.widget.value.selectNextPage();
}
selectLastSuggestion(): void {
- this.widget.getValue().selectLast();
+ this.widget.value.selectLast();
}
selectPrevSuggestion(): void {
- this.widget.getValue().selectPrevious();
+ this.widget.value.selectPrevious();
}
selectPrevPageSuggestion(): void {
- this.widget.getValue().selectPreviousPage();
+ this.widget.value.selectPreviousPage();
}
selectFirstSuggestion(): void {
- this.widget.getValue().selectFirst();
+ this.widget.value.selectFirst();
}
toggleSuggestionDetails(): void {
- this.widget.getValue().toggleDetails();
+ this.widget.value.toggleDetails();
}
toggleExplainMode(): void {
- this.widget.getValue().toggleExplainMode();
+ this.widget.value.toggleExplainMode();
}
toggleSuggestionFocus(): void {
- this.widget.getValue().toggleDetailsFocus();
+ this.widget.value.toggleDetailsFocus();
}
}
diff --git a/src/vs/editor/standalone/browser/standalone-tokens.css b/src/vs/editor/standalone/browser/standalone-tokens.css
index 6cc916c0aa2..4d173457365 100644
--- a/src/vs/editor/standalone/browser/standalone-tokens.css
+++ b/src/vs/editor/standalone/browser/standalone-tokens.css
@@ -6,7 +6,7 @@
/* Default standalone editor font */
.monaco-editor {
- font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Ubuntu", "Droid Sans", sans-serif;
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Ubuntu", "Droid Sans", sans-serif;
}
.monaco-menu .monaco-action-bar.vertical .action-item .action-menu-item:focus .action-label {
diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts
index c20e8327058..261a598fcfb 100644
--- a/src/vs/platform/actions/common/actions.ts
+++ b/src/vs/platform/actions/common/actions.ts
@@ -477,7 +477,7 @@ export function registerAction2(ctor: { new(): Action2 }): IDisposable {
disposables.add(MenuRegistry.appendMenuItem(menu.id, { command, ...menu }));
}
if (f1) {
- disposables.add(MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command }));
+ disposables.add(MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command, when: command.precondition }));
}
// keybinding
diff --git a/src/vs/platform/credentials/node/credentialsService.ts b/src/vs/platform/credentials/node/credentialsService.ts
index bc8d8d75914..3641cf65d33 100644
--- a/src/vs/platform/credentials/node/credentialsService.ts
+++ b/src/vs/platform/credentials/node/credentialsService.ts
@@ -14,27 +14,27 @@ export class KeytarCredentialsService implements ICredentialsService {
private readonly _keytar = new IdleValue>(() => import('keytar'));
async getPassword(service: string, account: string): Promise {
- const keytar = await this._keytar.getValue();
+ const keytar = await this._keytar.value;
return keytar.getPassword(service, account);
}
async setPassword(service: string, account: string, password: string): Promise {
- const keytar = await this._keytar.getValue();
+ const keytar = await this._keytar.value;
return keytar.setPassword(service, account, password);
}
async deletePassword(service: string, account: string): Promise {
- const keytar = await this._keytar.getValue();
+ const keytar = await this._keytar.value;
return keytar.deletePassword(service, account);
}
async findPassword(service: string): Promise {
- const keytar = await this._keytar.getValue();
+ const keytar = await this._keytar.value;
return keytar.findPassword(service);
}
async findCredentials(service: string): Promise> {
- const keytar = await this._keytar.getValue();
+ const keytar = await this._keytar.value;
return keytar.findCredentials(service);
}
}
diff --git a/src/vs/platform/electron/electron-main/electronMainService.ts b/src/vs/platform/electron/electron-main/electronMainService.ts
index a8cc247f89d..c21fb966278 100644
--- a/src/vs/platform/electron/electron-main/electronMainService.ts
+++ b/src/vs/platform/electron/electron-main/electronMainService.ts
@@ -114,7 +114,10 @@ export class ElectronMainService implements IElectronMainService {
}
private async doOpenEmptyWindow(windowId: number | undefined, options?: IOpenEmptyWindowOptions): Promise {
- this.windowsMainService.openEmptyWindow(OpenContext.API, options);
+ this.windowsMainService.openEmptyWindow({
+ context: OpenContext.API,
+ contextWindowId: windowId
+ }, options);
}
async toggleFullScreen(windowId: number | undefined): Promise {
diff --git a/src/vs/platform/instantiation/common/instantiationService.ts b/src/vs/platform/instantiation/common/instantiationService.ts
index cf9492900e9..c16ed7b4e9d 100644
--- a/src/vs/platform/instantiation/common/instantiationService.ts
+++ b/src/vs/platform/instantiation/common/instantiationService.ts
@@ -219,7 +219,7 @@ export class InstantiationService implements IInstantiationService {
if (key in target) {
return target[key];
}
- let obj = idle.getValue();
+ let obj = idle.value;
let prop = obj[key];
if (typeof prop !== 'function') {
return prop;
@@ -229,7 +229,7 @@ export class InstantiationService implements IInstantiationService {
return prop;
},
set(_target: T, p: PropertyKey, value: any): boolean {
- idle.getValue()[p] = value;
+ idle.value[p] = value;
return true;
}
});
diff --git a/src/vs/platform/launch/electron-main/launchMainService.ts b/src/vs/platform/launch/electron-main/launchMainService.ts
index 17698833e34..8775dd82ad2 100644
--- a/src/vs/platform/launch/electron-main/launchMainService.ts
+++ b/src/vs/platform/launch/electron-main/launchMainService.ts
@@ -6,7 +6,6 @@
import { ILogService } from 'vs/platform/log/common/log';
import { IURLService } from 'vs/platform/url/common/url';
import { IProcessEnvironment, isMacintosh } from 'vs/base/common/platform';
-import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { ParsedArgs } from 'vs/platform/environment/node/argv';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IWindowSettings } from 'vs/platform/windows/common/windows';
@@ -56,7 +55,6 @@ export interface ILaunchMainService {
start(args: ParsedArgs, userEnv: IProcessEnvironment): Promise;
getMainProcessId(): Promise;
getMainProcessInfo(): Promise;
- getLogsPath(): Promise;
getRemoteDiagnostics(options: IRemoteDiagnosticOptions): Promise<(IRemoteDiagnosticInfo | IRemoteDiagnosticError)[]>;
}
@@ -69,7 +67,6 @@ export class LaunchMainService implements ILaunchMainService {
@IWindowsMainService private readonly windowsMainService: IWindowsMainService,
@IURLService private readonly urlService: IURLService,
@IWorkspacesMainService private readonly workspacesMainService: IWorkspacesMainService,
- @IEnvironmentService private readonly environmentService: IEnvironmentService,
@IConfigurationService private readonly configurationService: IConfigurationService
) { }
@@ -84,7 +81,7 @@ export class LaunchMainService implements ILaunchMainService {
// Create a window if there is none
if (this.windowsMainService.getWindowCount() === 0) {
- const window = this.windowsMainService.openEmptyWindow(OpenContext.DESKTOP)[0];
+ const window = this.windowsMainService.openEmptyWindow({ context: OpenContext.DESKTOP })[0];
whenWindowReady = window.ready();
}
@@ -226,12 +223,6 @@ export class LaunchMainService implements ILaunchMainService {
});
}
- getLogsPath(): Promise {
- this.logService.trace('Received request for logs path from other instance.');
-
- return Promise.resolve(this.environmentService.logsPath);
- }
-
getRemoteDiagnostics(options: IRemoteDiagnosticOptions): Promise<(IRemoteDiagnosticInfo | IRemoteDiagnosticError)[]> {
const windows = this.windowsMainService.getWindows();
const promises: Promise[] = windows.map(window => {
diff --git a/src/vs/platform/list/browser/listService.ts b/src/vs/platform/list/browser/listService.ts
index f346cd655fd..f81f8534749 100644
--- a/src/vs/platform/list/browser/listService.ts
+++ b/src/vs/platform/list/browser/listService.ts
@@ -531,7 +531,7 @@ abstract class ResourceNavigator extends Disposable {
!!(browserEvent).preserveFocus :
!isDoubleClick;
- if (this.treeOrList.openOnSingleClick || isDoubleClick || isKeyboardEvent) {
+ if (this.options.openOnSingleClick || this.treeOrList.openOnSingleClick || isDoubleClick || isKeyboardEvent) {
const sideBySide = browserEvent instanceof MouseEvent && (browserEvent.ctrlKey || browserEvent.metaKey || browserEvent.altKey);
this.open(preserveFocus, isDoubleClick || isMiddleClick, sideBySide, browserEvent);
}
diff --git a/src/vs/platform/menubar/electron-main/menubar.ts b/src/vs/platform/menubar/electron-main/menubar.ts
index dd0251290f2..ac66913edbf 100644
--- a/src/vs/platform/menubar/electron-main/menubar.ts
+++ b/src/vs/platform/menubar/electron-main/menubar.ts
@@ -61,7 +61,7 @@ export class Menubar {
private keybindings: { [commandId: string]: IMenubarKeybinding };
- private fallbackMenuHandlers: { [id: string]: (menuItem: MenuItem, browserWindow: BrowserWindow, event: Event) => void } = {};
+ private readonly fallbackMenuHandlers: { [id: string]: (menuItem: MenuItem, browserWindow: BrowserWindow, event: Event) => void } = Object.create(null);
constructor(
@IUpdateService private readonly updateService: IUpdateService,
@@ -113,8 +113,8 @@ export class Menubar {
private addFallbackHandlers(): void {
// File Menu Items
- this.fallbackMenuHandlers['workbench.action.files.newUntitledFile'] = () => this.windowsMainService.openEmptyWindow(OpenContext.MENU);
- this.fallbackMenuHandlers['workbench.action.newWindow'] = () => this.windowsMainService.openEmptyWindow(OpenContext.MENU);
+ this.fallbackMenuHandlers['workbench.action.files.newUntitledFile'] = (menuItem, win, event) => this.windowsMainService.openEmptyWindow({ context: OpenContext.MENU, contextWindowId: win.id });
+ this.fallbackMenuHandlers['workbench.action.newWindow'] = (menuItem, win, event) => this.windowsMainService.openEmptyWindow({ context: OpenContext.MENU, contextWindowId: win.id });
this.fallbackMenuHandlers['workbench.action.files.openFileFolder'] = (menuItem, win, event) => this.electronMainService.pickFileFolderAndOpen(undefined, { forceNewWindow: this.isOptionClick(event), telemetryExtraData: { from: telemetryFrom } });
this.fallbackMenuHandlers['workbench.action.openWorkspace'] = (menuItem, win, event) => this.electronMainService.pickWorkspaceAndOpen(undefined, { forceNewWindow: this.isOptionClick(event), telemetryExtraData: { from: telemetryFrom } });
@@ -266,7 +266,7 @@ export class Menubar {
this.appMenuInstalled = true;
const dockMenu = new Menu();
- dockMenu.append(new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miNewWindow', comment: ['&& denotes a mnemonic'] }, "New &&Window")), click: () => this.windowsMainService.openEmptyWindow(OpenContext.DOCK) }));
+ dockMenu.append(new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miNewWindow', comment: ['&& denotes a mnemonic'] }, "New &&Window")), click: () => this.windowsMainService.openEmptyWindow({ context: OpenContext.DOCK }) }));
app.dock.setMenu(dockMenu);
}
diff --git a/src/vs/platform/menubar/electron-main/menubarMainService.ts b/src/vs/platform/menubar/electron-main/menubarMainService.ts
index 3cdb14892c0..7cadd57ecdb 100644
--- a/src/vs/platform/menubar/electron-main/menubarMainService.ts
+++ b/src/vs/platform/menubar/electron-main/menubarMainService.ts
@@ -13,28 +13,26 @@ export class MenubarMainService implements IMenubarService {
_serviceBrand: undefined;
- private _menubar: Menubar | undefined;
+ private menubar: Promise;
constructor(
@IInstantiationService private readonly instantiationService: IInstantiationService,
@ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService,
@ILogService private readonly logService: ILogService
) {
- // Install Menu
- this.lifecycleMainService.when(LifecycleMainPhase.AfterWindowOpen).then(() => {
- this._menubar = this.instantiationService.createInstance(Menubar);
- });
+ this.menubar = this.installMenuBarAfterWindowOpen();
}
- updateMenubar(windowId: number, menus: IMenubarData): Promise {
- return this.lifecycleMainService.when(LifecycleMainPhase.AfterWindowOpen).then(() => {
- this.logService.trace('menubarService#updateMenubar', windowId);
+ private async installMenuBarAfterWindowOpen(): Promise {
+ await this.lifecycleMainService.when(LifecycleMainPhase.AfterWindowOpen);
- if (this._menubar) {
- this._menubar.updateMenu(menus, windowId);
- }
+ return this.instantiationService.createInstance(Menubar);
+ }
- return undefined;
- });
+ async updateMenubar(windowId: number, menus: IMenubarData): Promise {
+ this.logService.trace('menubarService#updateMenubar', windowId);
+
+ const menubar = await this.menubar;
+ menubar.updateMenu(menus, windowId);
}
}
diff --git a/src/vs/platform/theme/common/tokenClassificationRegistry.ts b/src/vs/platform/theme/common/tokenClassificationRegistry.ts
index 14a1b52e4c3..754c93d5bd7 100644
--- a/src/vs/platform/theme/common/tokenClassificationRegistry.ts
+++ b/src/vs/platform/theme/common/tokenClassificationRegistry.ts
@@ -515,7 +515,7 @@ function createDefaultTokenClassificationRegistry(): TokenClassificationRegistry
registerTokenType('namespace', nls.localize('namespace', "Style for namespaces."), [['entity.name.namespace']]);
registerTokenType('type', nls.localize('type', "Style for types."), [['entity.name.type'], ['support.type']]);
- registerTokenType('struct', nls.localize('struct', "Style for structs."), [['storage.type.struct']]);
+ registerTokenType('struct', nls.localize('struct', "Style for structs."), [['entity.name.type.struct']]);
registerTokenType('class', nls.localize('class', "Style for classes."), [['entity.name.type.class'], ['support.class']]);
registerTokenType('interface', nls.localize('interface', "Style for interfaces."), [['entity.name.type.interface']]);
registerTokenType('enum', nls.localize('enum', "Style for enums."), [['entity.name.type.enum']]);
diff --git a/src/vs/platform/userDataSync/common/abstractSynchronizer.ts b/src/vs/platform/userDataSync/common/abstractSynchronizer.ts
index e52d759f8e4..4860d158671 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, FileOperationResult, FileOperationError } from 'vs/platform/files/common/files';
import { VSBuffer } from 'vs/base/common/buffer';
import { URI } from 'vs/base/common/uri';
-import { SyncResource, SyncStatus, IUserData, IUserDataSyncStoreService, UserDataSyncErrorCode, UserDataSyncError, IUserDataSyncLogService, IUserDataSyncUtilService, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService, Conflict, ISyncResourceHandle, USER_DATA_SYNC_SCHEME, ISyncPreviewResult } from 'vs/platform/userDataSync/common/userDataSync';
+import { SyncResource, SyncStatus, IUserData, IUserDataSyncStoreService, UserDataSyncErrorCode, UserDataSyncError, IUserDataSyncLogService, IUserDataSyncUtilService, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService, Conflict, ISyncResourceHandle, USER_DATA_SYNC_SCHEME, ISyncPreviewResult, IUserDataManifest } from 'vs/platform/userDataSync/common/userDataSync';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { joinPath, dirname, isEqual, basename } from 'vs/base/common/resources';
import { CancelablePromise } from 'vs/base/common/async';
@@ -108,7 +108,7 @@ export abstract class AbstractSynchroniser extends Disposable {
protected isEnabled(): boolean { return this.userDataSyncEnablementService.isResourceEnabled(this.resource); }
- async sync(ref?: string): Promise {
+ async sync(manifest: IUserDataManifest | null): Promise {
if (!this.isEnabled()) {
if (this.status !== SyncStatus.Idle) {
await this.stop();
@@ -129,7 +129,7 @@ export abstract class AbstractSynchroniser extends Disposable {
this.setStatus(SyncStatus.Syncing);
const lastSyncUserData = await this.getLastSyncUserData();
- const remoteUserData = ref && lastSyncUserData && lastSyncUserData.ref === ref ? lastSyncUserData : await this.getRemoteUserData(lastSyncUserData);
+ const remoteUserData = await this.getLatestRemoteUserData(manifest, lastSyncUserData);
let status: SyncStatus = SyncStatus.Idle;
try {
@@ -144,6 +144,24 @@ export abstract class AbstractSynchroniser extends Disposable {
}
}
+ private async getLatestRemoteUserData(manifest: IUserDataManifest | null, lastSyncUserData: IRemoteUserData | null): Promise {
+ if (lastSyncUserData) {
+
+ const latestRef = manifest && manifest.latest ? manifest.latest[this.resource] : undefined;
+
+ // Last time synced resource and latest resource on server are same
+ if (lastSyncUserData.ref === latestRef) {
+ return lastSyncUserData;
+ }
+
+ // There is no resource on server and last time it was synced with no resource
+ if (latestRef === undefined && lastSyncUserData.syncData === null) {
+ return lastSyncUserData;
+ }
+ }
+ return this.getRemoteUserData(lastSyncUserData);
+ }
+
async getSyncPreview(): Promise {
if (!this.isEnabled()) {
return { hasLocalChanged: false, hasRemoteChanged: false };
@@ -225,15 +243,19 @@ export abstract class AbstractSynchroniser extends Disposable {
} catch (e) { /* ignore */ }
}
- protected async getLastSyncUserData(): Promise {
+ async getLastSyncUserData(): Promise {
try {
const content = await this.fileService.readFile(this.lastSyncResource);
const parsed = JSON.parse(content.value.toString());
- let syncData: ISyncData = JSON.parse(parsed.content);
+ const userData: IUserData = parsed as IUserData;
+ if (userData.content === null) {
+ return { ref: parsed.ref, syncData: null } as T;
+ }
+ let syncData: ISyncData = JSON.parse(userData.content);
// Migration from old content to sync data
if (!isSyncData(syncData)) {
- syncData = { version: this.version, content: parsed.content };
+ syncData = { version: this.version, content: userData.content };
}
return { ...parsed, ...{ syncData, content: undefined } };
@@ -247,11 +269,11 @@ export abstract class AbstractSynchroniser extends Disposable {
}
protected async updateLastSyncUserData(lastSyncRemoteUserData: IRemoteUserData, additionalProps: IStringDictionary = {}): Promise {
- const lastSyncUserData: IUserData = { ref: lastSyncRemoteUserData.ref, content: JSON.stringify(lastSyncRemoteUserData.syncData), ...additionalProps };
+ const lastSyncUserData: IUserData = { ref: lastSyncRemoteUserData.ref, content: lastSyncRemoteUserData.syncData ? JSON.stringify(lastSyncRemoteUserData.syncData) : null, ...additionalProps };
await this.fileService.writeFile(this.lastSyncResource, VSBuffer.fromString(JSON.stringify(lastSyncUserData)));
}
- protected async getRemoteUserData(lastSyncData: IRemoteUserData | null): Promise {
+ async getRemoteUserData(lastSyncData: IRemoteUserData | null): Promise {
const { ref, content } = await this.getUserData(lastSyncData);
let syncData: ISyncData | null = null;
if (content !== null) {
diff --git a/src/vs/platform/userDataSync/common/extensionsMerge.ts b/src/vs/platform/userDataSync/common/extensionsMerge.ts
index bb02ff61d84..43f246b5733 100644
--- a/src/vs/platform/userDataSync/common/extensionsMerge.ts
+++ b/src/vs/platform/userDataSync/common/extensionsMerge.ts
@@ -21,11 +21,12 @@ export function merge(localExtensions: ISyncExtension[], remoteExtensions: ISync
const updated: ISyncExtension[] = [];
if (!remoteExtensions) {
+ const remote = localExtensions.filter(({ identifier }) => ignoredExtensions.every(id => id.toLowerCase() !== identifier.id.toLowerCase()));
return {
added,
removed,
updated,
- remote: localExtensions.filter(({ identifier }) => ignoredExtensions.every(id => id.toLowerCase() !== identifier.id.toLowerCase()))
+ remote: remote.length > 0 ? remote : null
};
}
diff --git a/src/vs/platform/userDataSync/common/extensionsSync.ts b/src/vs/platform/userDataSync/common/extensionsSync.ts
index 33901ed48ce..ae3e0ba4da8 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, SyncResource, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService, ISyncResourceHandle, ISyncPreviewResult } from 'vs/platform/userDataSync/common/userDataSync';
+import { SyncStatus, IUserDataSyncStoreService, ISyncExtension, IUserDataSyncLogService, IUserDataSynchroniser, SyncResource, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService, ISyncResourceHandle, ISyncPreviewResult, USER_DATA_SYNC_SCHEME } 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';
@@ -16,9 +16,10 @@ import { isNonEmptyArray } from 'vs/base/common/arrays';
import { AbstractSynchroniser, IRemoteUserData, ISyncData } from 'vs/platform/userDataSync/common/abstractSynchronizer';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { URI } from 'vs/base/common/uri';
-import { joinPath, dirname, basename } from 'vs/base/common/resources';
+import { joinPath, dirname, basename, isEqual } from 'vs/base/common/resources';
import { format } from 'vs/base/common/jsonFormatter';
import { applyEdits } from 'vs/base/common/jsonEdit';
+import { compare } from 'vs/base/common/strings';
interface IExtensionsSyncPreviewResult extends ISyncPreviewResult {
readonly localExtensions: ISyncExtension[];
@@ -35,8 +36,10 @@ interface ILastSyncUserData extends IRemoteUserData {
skippedExtensions: ISyncExtension[] | undefined;
}
+
export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUserDataSynchroniser {
+ private static readonly EXTENSIONS_DATA_URI = URI.from({ scheme: USER_DATA_SYNC_SCHEME, authority: 'extensions', path: `/current.json` });
protected readonly version: number = 2;
protected isEnabled(): boolean { return super.isEnabled() && this.extensionGalleryService.isEnabled(); }
@@ -132,28 +135,49 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
async stop(): Promise { }
async getAssociatedResources({ uri }: ISyncResourceHandle): Promise<{ resource: URI, comparableResource?: URI }[]> {
- return [{ resource: joinPath(uri, 'extensions.json') }];
+ return [{ resource: joinPath(uri, 'extensions.json'), comparableResource: ExtensionsSynchroniser.EXTENSIONS_DATA_URI }];
}
async resolveContent(uri: URI): Promise {
+ if (isEqual(uri, ExtensionsSynchroniser.EXTENSIONS_DATA_URI)) {
+ const localExtensions = await this.getLocalExtensions();
+ return this.format(localExtensions);
+ }
+
let content = await super.resolveContent(uri);
if (content) {
return content;
}
+
content = await super.resolveContent(dirname(uri));
if (content) {
const syncData = this.parseSyncData(content);
if (syncData) {
switch (basename(uri)) {
case 'extensions.json':
- const edits = format(syncData.content, undefined, {});
- return applyEdits(syncData.content, edits);
+ return this.format(this.parseExtensions(syncData));
}
}
}
+
return null;
}
+ private format(extensions: ISyncExtension[]): string {
+ extensions.sort((e1, e2) => {
+ if (!e1.identifier.uuid && e2.identifier.uuid) {
+ return -1;
+ }
+ if (e1.identifier.uuid && !e2.identifier.uuid) {
+ return 1;
+ }
+ return compare(e1.identifier.id, e2.identifier.id);
+ });
+ const content = JSON.stringify(extensions);
+ const edits = format(content, undefined, {});
+ return applyEdits(content, edits);
+ }
+
async acceptConflict(conflict: URI, content: string): Promise {
throw new Error(`${this.syncResourceLogLabel}: Conflicts should not occur`);
}
@@ -216,9 +240,7 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
}
if (hasLocalChanged) {
- // back up all disabled or market place extensions
- const backUpExtensions = localExtensions.filter(e => e.disabled || !!e.identifier.uuid);
- await this.backupLocal(JSON.stringify(backUpExtensions));
+ await this.backupLocal(JSON.stringify(localExtensions));
skippedExtensions = await this.updateLocalExtensions(added, removed, updated, skippedExtensions);
}
diff --git a/src/vs/platform/userDataSync/common/globalStateMerge.ts b/src/vs/platform/userDataSync/common/globalStateMerge.ts
index 4d4755fb245..9b7d47a307c 100644
--- a/src/vs/platform/userDataSync/common/globalStateMerge.ts
+++ b/src/vs/platform/userDataSync/common/globalStateMerge.ts
@@ -18,7 +18,7 @@ export interface IMergeResult {
export function merge(localStorage: IStringDictionary, remoteStorage: IStringDictionary | null, baseStorage: IStringDictionary | null, storageKeys: ReadonlyArray, previouslySkipped: string[], logService: ILogService): IMergeResult {
if (!remoteStorage) {
- return { remote: localStorage, local: { added: {}, removed: [], updated: {} }, skipped: [] };
+ return { remote: Object.keys(localStorage).length > 0 ? localStorage : null, local: { added: {}, removed: [], updated: {} }, skipped: [] };
}
const localToRemote = compare(localStorage, remoteStorage);
diff --git a/src/vs/platform/userDataSync/common/globalStateSync.ts b/src/vs/platform/userDataSync/common/globalStateSync.ts
index aad86c57f60..ca00b220b5b 100644
--- a/src/vs/platform/userDataSync/common/globalStateSync.ts
+++ b/src/vs/platform/userDataSync/common/globalStateSync.ts
@@ -3,11 +3,11 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import { SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IGlobalState, SyncResource, IUserDataSynchroniser, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService, ISyncResourceHandle, IStorageValue, ISyncPreviewResult } from 'vs/platform/userDataSync/common/userDataSync';
+import { SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IGlobalState, SyncResource, IUserDataSynchroniser, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService, ISyncResourceHandle, IStorageValue, ISyncPreviewResult, USER_DATA_SYNC_SCHEME } 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';
-import { dirname, joinPath, basename } from 'vs/base/common/resources';
+import { dirname, joinPath, basename, isEqual } from 'vs/base/common/resources';
import { IFileService } from 'vs/platform/files/common/files';
import { IStringDictionary } from 'vs/base/common/collections';
import { edit } from 'vs/platform/userDataSync/common/content';
@@ -41,6 +41,7 @@ interface ILastSyncUserData extends IRemoteUserData {
export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUserDataSynchroniser {
+ private static readonly GLOBAL_STATE_DATA_URI = URI.from({ scheme: USER_DATA_SYNC_SCHEME, authority: 'globalState', path: `/current.json` });
protected readonly version: number = 1;
constructor(
@@ -139,28 +140,44 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
async stop(): Promise { }
async getAssociatedResources({ uri }: ISyncResourceHandle): Promise<{ resource: URI, comparableResource?: URI }[]> {
- return [{ resource: joinPath(uri, 'globalState.json') }];
+ return [{ resource: joinPath(uri, 'globalState.json'), comparableResource: GlobalStateSynchroniser.GLOBAL_STATE_DATA_URI }];
}
async resolveContent(uri: URI): Promise {
+ if (isEqual(uri, GlobalStateSynchroniser.GLOBAL_STATE_DATA_URI)) {
+ const localGlobalState = await this.getLocalGlobalState();
+ return this.format(localGlobalState);
+ }
+
let content = await super.resolveContent(uri);
if (content) {
return content;
}
+
content = await super.resolveContent(dirname(uri));
if (content) {
const syncData = this.parseSyncData(content);
if (syncData) {
switch (basename(uri)) {
case 'globalState.json':
- const edits = format(syncData.content, undefined, {});
- return applyEdits(syncData.content, edits);
+ return this.format(JSON.parse(syncData.content));
}
}
}
+
return null;
}
+ private format(globalState: IGlobalState): string {
+ const storageKeys = Object.keys(globalState.storage).sort();
+ const storage: IStringDictionary = {};
+ storageKeys.forEach(key => storage[key] = globalState.storage[key]);
+ globalState.storage = storage;
+ const content = JSON.stringify(globalState);
+ const edits = format(content, undefined, {});
+ return applyEdits(content, edits);
+ }
+
async acceptConflict(conflict: URI, content: string): Promise {
throw new Error(`${this.syncResourceLogLabel}: Conflicts should not occur`);
}
diff --git a/src/vs/platform/userDataSync/common/keybindingsSync.ts b/src/vs/platform/userDataSync/common/keybindingsSync.ts
index ed6943af053..9d3c69444c9 100644
--- a/src/vs/platform/userDataSync/common/keybindingsSync.ts
+++ b/src/vs/platform/userDataSync/common/keybindingsSync.ts
@@ -248,10 +248,10 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem
this.logService.info(`${this.syncResourceLogLabel}: No changes found during synchronizing keybindings.`);
}
- if (lastSyncUserData?.ref !== remoteUserData.ref && (content !== null || fileContent !== null)) {
+ if (lastSyncUserData?.ref !== remoteUserData.ref) {
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 } });
+ const lastSyncContent = content !== null || fileContent !== null ? this.toSyncContent(content !== null ? content : fileContent!.value.toString(), null) : null;
+ await this.updateLastSyncUserData({ ref: remoteUserData.ref, syncData: lastSyncContent ? { version: remoteUserData.syncData!.version, content: lastSyncContent } : null });
this.logService.info(`${this.syncResourceLogLabel}: Updated last synchronized keybindings`);
}
@@ -315,7 +315,7 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem
return { fileContent, remoteUserData, lastSyncUserData, content, hasLocalChanged, hasRemoteChanged, hasConflicts };
}
- private getKeybindingsContentFromSyncContent(syncContent: string): string | null {
+ getKeybindingsContentFromSyncContent(syncContent: string): string | null {
try {
const parsed = JSON.parse(syncContent);
if (!this.configurationService.getValue('sync.keybindingsPerPlatform')) {
diff --git a/src/vs/platform/userDataSync/common/settingsSync.ts b/src/vs/platform/userDataSync/common/settingsSync.ts
index ce6c56c87b5..33b7eae171a 100644
--- a/src/vs/platform/userDataSync/common/settingsSync.ts
+++ b/src/vs/platform/userDataSync/common/settingsSync.ts
@@ -357,7 +357,7 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser {
return remoteUserData.syncData ? this.parseSettingsSyncContent(remoteUserData.syncData.content) : null;
}
- private parseSettingsSyncContent(syncContent: string): ISettingsSyncContent | null {
+ parseSettingsSyncContent(syncContent: string): ISettingsSyncContent | null {
try {
const parsed = JSON.parse(syncContent);
return isSettingsSyncContent(parsed) ? parsed : /* migrate */ { settings: syncContent };
diff --git a/src/vs/platform/userDataSync/common/snippetsMerge.ts b/src/vs/platform/userDataSync/common/snippetsMerge.ts
index 42a9dfaae05..07c944c53cd 100644
--- a/src/vs/platform/userDataSync/common/snippetsMerge.ts
+++ b/src/vs/platform/userDataSync/common/snippetsMerge.ts
@@ -26,7 +26,7 @@ export function merge(local: IStringDictionary, remote: IStringDictionar
removed: values(removed),
updated,
conflicts: [],
- remote: local
+ remote: Object.keys(local).length > 0 ? local : null
};
}
diff --git a/src/vs/platform/userDataSync/common/snippetsSync.ts b/src/vs/platform/userDataSync/common/snippetsSync.ts
index d802e543cf4..b8d4a194b8c 100644
--- a/src/vs/platform/userDataSync/common/snippetsSync.ts
+++ b/src/vs/platform/userDataSync/common/snippetsSync.ts
@@ -285,7 +285,7 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD
private async doGeneratePreview(local: IStringDictionary, remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, resolvedConflicts: IStringDictionary = {}, token: CancellationToken = CancellationToken.None): Promise {
const localSnippets = this.toSnippetsContents(local);
const remoteSnippets: IStringDictionary | null = remoteUserData.syncData ? this.parseSnippets(remoteUserData.syncData) : null;
- const lastSyncSnippets: IStringDictionary | null = lastSyncUserData ? this.parseSnippets(lastSyncUserData.syncData!) : null;
+ const lastSyncSnippets: IStringDictionary | null = lastSyncUserData && lastSyncUserData.syncData ? this.parseSnippets(lastSyncUserData.syncData) : null;
if (remoteSnippets) {
this.logService.trace(`${this.syncResourceLogLabel}: Merging remote snippets with local snippets...`);
diff --git a/src/vs/platform/userDataSync/common/userDataSync.ts b/src/vs/platform/userDataSync/common/userDataSync.ts
index eec0c4502a1..e158e4dce61 100644
--- a/src/vs/platform/userDataSync/common/userDataSync.ts
+++ b/src/vs/platform/userDataSync/common/userDataSync.ts
@@ -274,7 +274,7 @@ export interface IUserDataSynchroniser {
pull(): Promise;
push(): Promise;
- sync(ref?: string): Promise;
+ sync(manifest: IUserDataManifest | null): Promise;
stop(): Promise;
getSyncPreview(): Promise
diff --git a/src/vs/platform/userDataSync/common/userDataSyncService.ts b/src/vs/platform/userDataSync/common/userDataSyncService.ts
index 1c1bd056006..bd150093aab 100644
--- a/src/vs/platform/userDataSync/common/userDataSyncService.ts
+++ b/src/vs/platform/userDataSync/common/userDataSyncService.ts
@@ -136,7 +136,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
for (const synchroniser of this.synchronisers) {
try {
- await synchroniser.sync(manifest && manifest.latest ? manifest.latest[synchroniser.resource] : undefined);
+ await synchroniser.sync(manifest);
} catch (e) {
this.handleSyncError(e, synchroniser.resource);
this._syncErrors.push([synchroniser.resource, UserDataSyncError.toUserDataSyncError(e)]);
diff --git a/src/vs/platform/userDataSync/test/common/globalStateSync.test.ts b/src/vs/platform/userDataSync/test/common/globalStateSync.test.ts
index 9f86b9354a5..7a1f331d5fa 100644
--- a/src/vs/platform/userDataSync/test/common/globalStateSync.test.ts
+++ b/src/vs/platform/userDataSync/test/common/globalStateSync.test.ts
@@ -44,11 +44,58 @@ suite('GlobalStateSync', () => {
teardown(() => disposableStore.clear());
+ test('when global state does not exist', async () => {
+ assert.deepEqual(await testObject.getLastSyncUserData(), null);
+ let manifest = await testClient.manifest();
+ server.reset();
+ await testObject.sync(manifest);
+
+ assert.deepEqual(server.requests, [
+ { type: 'GET', url: `${server.url}/v1/resource/${testObject.resource}/latest`, headers: {} },
+ ]);
+
+ const lastSyncUserData = await testObject.getLastSyncUserData();
+ const remoteUserData = await testObject.getRemoteUserData(null);
+ assert.deepEqual(lastSyncUserData!.ref, remoteUserData.ref);
+ assert.deepEqual(lastSyncUserData!.syncData, remoteUserData.syncData);
+ assert.equal(lastSyncUserData!.syncData, null);
+
+ manifest = await testClient.manifest();
+ server.reset();
+ await testObject.sync(manifest);
+ assert.deepEqual(server.requests, []);
+
+ manifest = await testClient.manifest();
+ server.reset();
+ await testObject.sync(manifest);
+ assert.deepEqual(server.requests, []);
+ });
+
+ test('when global state is created after first sync', async () => {
+ await testObject.sync(await testClient.manifest());
+ updateStorage('a', 'value1', testClient);
+
+ let lastSyncUserData = await testObject.getLastSyncUserData();
+ const manifest = await testClient.manifest();
+ server.reset();
+ await testObject.sync(manifest);
+
+ assert.deepEqual(server.requests, [
+ { type: 'POST', url: `${server.url}/v1/resource/${testObject.resource}`, headers: { 'If-Match': lastSyncUserData?.ref } },
+ ]);
+
+ lastSyncUserData = await testObject.getLastSyncUserData();
+ const remoteUserData = await testObject.getRemoteUserData(null);
+ assert.deepEqual(lastSyncUserData!.ref, remoteUserData.ref);
+ assert.deepEqual(lastSyncUserData!.syncData, remoteUserData.syncData);
+ assert.deepEqual(JSON.parse(lastSyncUserData!.syncData!.content).storage, { 'a': { version: 1, value: 'value1' } });
+ });
+
test('first time sync - outgoing to server (no state)', async () => {
updateStorage('a', 'value1', testClient);
await updateLocale(testClient);
- await testObject.sync();
+ await testObject.sync(await testClient.manifest());
assert.equal(testObject.status, SyncStatus.Idle);
assert.deepEqual(testObject.conflicts, []);
@@ -63,7 +110,7 @@ suite('GlobalStateSync', () => {
await updateLocale(client2);
await client2.sync();
- await testObject.sync();
+ await testObject.sync(await testClient.manifest());
assert.equal(testObject.status, SyncStatus.Idle);
assert.deepEqual(testObject.conflicts, []);
@@ -76,7 +123,7 @@ suite('GlobalStateSync', () => {
await client2.sync();
updateStorage('b', 'value2', testClient);
- await testObject.sync();
+ await testObject.sync(await testClient.manifest());
assert.equal(testObject.status, SyncStatus.Idle);
assert.deepEqual(testObject.conflicts, []);
@@ -94,7 +141,7 @@ suite('GlobalStateSync', () => {
await client2.sync();
updateStorage('a', 'value2', client2);
- await testObject.sync();
+ await testObject.sync(await testClient.manifest());
assert.equal(testObject.status, SyncStatus.Idle);
assert.deepEqual(testObject.conflicts, []);
@@ -109,10 +156,10 @@ suite('GlobalStateSync', () => {
test('sync adding a storage value', async () => {
updateStorage('a', 'value1', testClient);
- await testObject.sync();
+ await testObject.sync(await testClient.manifest());
updateStorage('b', 'value2', testClient);
- await testObject.sync();
+ await testObject.sync(await testClient.manifest());
assert.equal(testObject.status, SyncStatus.Idle);
assert.deepEqual(testObject.conflicts, []);
@@ -127,10 +174,10 @@ suite('GlobalStateSync', () => {
test('sync updating a storage value', async () => {
updateStorage('a', 'value1', testClient);
- await testObject.sync();
+ await testObject.sync(await testClient.manifest());
updateStorage('a', 'value2', testClient);
- await testObject.sync();
+ await testObject.sync(await testClient.manifest());
assert.equal(testObject.status, SyncStatus.Idle);
assert.deepEqual(testObject.conflicts, []);
@@ -145,10 +192,10 @@ suite('GlobalStateSync', () => {
test('sync removing a storage value', async () => {
updateStorage('a', 'value1', testClient);
updateStorage('b', 'value2', testClient);
- await testObject.sync();
+ await testObject.sync(await testClient.manifest());
removeStorage('b', testClient);
- await testObject.sync();
+ await testObject.sync(await testClient.manifest());
assert.equal(testObject.status, SyncStatus.Idle);
assert.deepEqual(testObject.conflicts, []);
diff --git a/src/vs/platform/userDataSync/test/common/keybindingsSync.test.ts b/src/vs/platform/userDataSync/test/common/keybindingsSync.test.ts
new file mode 100644
index 00000000000..9fd790befce
--- /dev/null
+++ b/src/vs/platform/userDataSync/test/common/keybindingsSync.test.ts
@@ -0,0 +1,86 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import * as assert from 'assert';
+import { IUserDataSyncStoreService, IUserDataSyncService, SyncResource } 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 { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService';
+import { IFileService } from 'vs/platform/files/common/files';
+import { IEnvironmentService } from 'vs/platform/environment/common/environment';
+import { KeybindingsSynchroniser } from 'vs/platform/userDataSync/common/keybindingsSync';
+import { VSBuffer } from 'vs/base/common/buffer';
+
+suite('KeybindingsSync', () => {
+
+ const disposableStore = new DisposableStore();
+ const server = new UserDataSyncTestServer();
+ let client: UserDataSyncClient;
+
+ let testObject: KeybindingsSynchroniser;
+
+ setup(async () => {
+ client = disposableStore.add(new UserDataSyncClient(server));
+ await client.setUp(true);
+ testObject = (client.instantiationService.get(IUserDataSyncService) as UserDataSyncService).getSynchroniser(SyncResource.Keybindings) as KeybindingsSynchroniser;
+ disposableStore.add(toDisposable(() => client.instantiationService.get(IUserDataSyncStoreService).clear()));
+ });
+
+ teardown(() => disposableStore.clear());
+
+ test('when keybindings file does not exist', async () => {
+ const fileService = client.instantiationService.get(IFileService);
+ const keybindingsResource = client.instantiationService.get(IEnvironmentService).keybindingsResource;
+
+ assert.deepEqual(await testObject.getLastSyncUserData(), null);
+ let manifest = await client.manifest();
+ server.reset();
+ await testObject.sync(manifest);
+
+ assert.deepEqual(server.requests, [
+ { type: 'GET', url: `${server.url}/v1/resource/${testObject.resource}/latest`, headers: {} },
+ ]);
+ assert.ok(!await fileService.exists(keybindingsResource));
+
+ const lastSyncUserData = await testObject.getLastSyncUserData();
+ const remoteUserData = await testObject.getRemoteUserData(null);
+ assert.deepEqual(lastSyncUserData!.ref, remoteUserData.ref);
+ assert.deepEqual(lastSyncUserData!.syncData, remoteUserData.syncData);
+ assert.equal(lastSyncUserData!.syncData, null);
+
+ manifest = await client.manifest();
+ server.reset();
+ await testObject.sync(manifest);
+ assert.deepEqual(server.requests, []);
+
+ manifest = await client.manifest();
+ server.reset();
+ await testObject.sync(manifest);
+ assert.deepEqual(server.requests, []);
+ });
+
+ test('when keybindings file is created after first sync', async () => {
+ const fileService = client.instantiationService.get(IFileService);
+ const keybindingsResource = client.instantiationService.get(IEnvironmentService).keybindingsResource;
+ await testObject.sync(await client.manifest());
+ await fileService.createFile(keybindingsResource, VSBuffer.fromString('[]'));
+
+ let lastSyncUserData = await testObject.getLastSyncUserData();
+ const manifest = await client.manifest();
+ server.reset();
+ await testObject.sync(manifest);
+
+ assert.deepEqual(server.requests, [
+ { type: 'POST', url: `${server.url}/v1/resource/${testObject.resource}`, headers: { 'If-Match': lastSyncUserData?.ref } },
+ ]);
+
+ lastSyncUserData = await testObject.getLastSyncUserData();
+ const remoteUserData = await testObject.getRemoteUserData(null);
+ assert.deepEqual(lastSyncUserData!.ref, remoteUserData.ref);
+ assert.deepEqual(lastSyncUserData!.syncData, remoteUserData.syncData);
+ assert.equal(testObject.getKeybindingsContentFromSyncContent(lastSyncUserData!.syncData!.content!), '[]');
+ });
+
+});
diff --git a/src/vs/platform/userDataSync/test/common/settingsSync.test.ts b/src/vs/platform/userDataSync/test/common/settingsSync.test.ts
index c0f87712cf1..a9e62326673 100644
--- a/src/vs/platform/userDataSync/test/common/settingsSync.test.ts
+++ b/src/vs/platform/userDataSync/test/common/settingsSync.test.ts
@@ -43,13 +43,67 @@ suite('SettingsSync', () => {
setup(async () => {
client = disposableStore.add(new UserDataSyncClient(server));
- await client.setUp();
+ await client.setUp(true);
testObject = (client.instantiationService.get(IUserDataSyncService) as UserDataSyncService).getSynchroniser(SyncResource.Settings) as SettingsSynchroniser;
disposableStore.add(toDisposable(() => client.instantiationService.get(IUserDataSyncStoreService).clear()));
});
teardown(() => disposableStore.clear());
+ test('when settings file does not exist', async () => {
+ const fileService = client.instantiationService.get(IFileService);
+ const settingResource = client.instantiationService.get(IEnvironmentService).settingsResource;
+
+ assert.deepEqual(await testObject.getLastSyncUserData(), null);
+ let manifest = await client.manifest();
+ server.reset();
+ await testObject.sync(manifest);
+
+ assert.deepEqual(server.requests, [
+ { type: 'GET', url: `${server.url}/v1/resource/${testObject.resource}/latest`, headers: {} },
+ ]);
+ assert.ok(!await fileService.exists(settingResource));
+
+ const lastSyncUserData = await testObject.getLastSyncUserData();
+ const remoteUserData = await testObject.getRemoteUserData(null);
+ assert.deepEqual(lastSyncUserData!.ref, remoteUserData.ref);
+ assert.deepEqual(lastSyncUserData!.syncData, remoteUserData.syncData);
+ assert.equal(lastSyncUserData!.syncData, null);
+
+ manifest = await client.manifest();
+ server.reset();
+ await testObject.sync(manifest);
+ assert.deepEqual(server.requests, []);
+
+ manifest = await client.manifest();
+ server.reset();
+ await testObject.sync(manifest);
+ assert.deepEqual(server.requests, []);
+ });
+
+ test('when settings file is created after first sync', async () => {
+ const fileService = client.instantiationService.get(IFileService);
+
+ const settingsResource = client.instantiationService.get(IEnvironmentService).settingsResource;
+ await testObject.sync(await client.manifest());
+ await fileService.createFile(settingsResource, VSBuffer.fromString('{}'));
+
+ let lastSyncUserData = await testObject.getLastSyncUserData();
+ const manifest = await client.manifest();
+ server.reset();
+ await testObject.sync(manifest);
+
+ assert.deepEqual(server.requests, [
+ { type: 'POST', url: `${server.url}/v1/resource/${testObject.resource}`, headers: { 'If-Match': lastSyncUserData?.ref } },
+ ]);
+
+ lastSyncUserData = await testObject.getLastSyncUserData();
+ const remoteUserData = await testObject.getRemoteUserData(null);
+ assert.deepEqual(lastSyncUserData!.ref, remoteUserData.ref);
+ assert.deepEqual(lastSyncUserData!.syncData, remoteUserData.syncData);
+ assert.equal(testObject.parseSettingsSyncContent(lastSyncUserData!.syncData!.content!)?.settings, '{}');
+ });
+
test('sync for first time to the server', async () => {
const expected =
`{
@@ -75,7 +129,7 @@ suite('SettingsSync', () => {
}`;
await updateSettings(expected);
- await testObject.sync();
+ await testObject.sync(await client.manifest());
const { content } = await client.read(testObject.resource);
assert.ok(content !== null);
@@ -99,7 +153,7 @@ suite('SettingsSync', () => {
}`;
await updateSettings(settingsContent);
- await testObject.sync();
+ await testObject.sync(await client.manifest());
const { content } = await client.read(testObject.resource);
assert.ok(content !== null);
@@ -130,7 +184,7 @@ suite('SettingsSync', () => {
}`;
await updateSettings(settingsContent);
- await testObject.sync();
+ await testObject.sync(await client.manifest());
const { content } = await client.read(testObject.resource);
assert.ok(content !== null);
@@ -161,7 +215,7 @@ suite('SettingsSync', () => {
}`;
await updateSettings(settingsContent);
- await testObject.sync();
+ await testObject.sync(await client.manifest());
const { content } = await client.read(testObject.resource);
assert.ok(content !== null);
@@ -185,7 +239,7 @@ suite('SettingsSync', () => {
}`;
await updateSettings(settingsContent);
- await testObject.sync();
+ await testObject.sync(await client.manifest());
const { content } = await client.read(testObject.resource);
assert.ok(content !== null);
@@ -203,7 +257,7 @@ suite('SettingsSync', () => {
}`;
await updateSettings(settingsContent);
- await testObject.sync();
+ await testObject.sync(await client.manifest());
const { content } = await client.read(testObject.resource);
assert.ok(content !== null);
@@ -237,7 +291,7 @@ suite('SettingsSync', () => {
}`;
await updateSettings(settingsContent);
- await testObject.sync();
+ await testObject.sync(await client.manifest());
const { content } = await client.read(testObject.resource);
assert.ok(content !== null);
@@ -285,7 +339,7 @@ suite('SettingsSync', () => {
}`;
await updateSettings(settingsContent);
- await testObject.sync();
+ await testObject.sync(await client.manifest());
const { content } = await client.read(testObject.resource);
assert.ok(content !== null);
@@ -333,7 +387,7 @@ suite('SettingsSync', () => {
await updateSettings(expected);
try {
- await testObject.sync();
+ await testObject.sync(await client.manifest());
assert.fail('should fail with invalid content error');
} catch (e) {
assert.ok(e instanceof UserDataSyncError);
diff --git a/src/vs/platform/userDataSync/test/common/snippetsSync.test.ts b/src/vs/platform/userDataSync/test/common/snippetsSync.test.ts
index 7943e35359f..87da32e3a01 100644
--- a/src/vs/platform/userDataSync/test/common/snippetsSync.test.ts
+++ b/src/vs/platform/userDataSync/test/common/snippetsSync.test.ts
@@ -167,11 +167,62 @@ suite('SnippetsSync', () => {
teardown(() => disposableStore.clear());
+ test('when snippets does not exist', async () => {
+ const fileService = testClient.instantiationService.get(IFileService);
+ const snippetsResource = testClient.instantiationService.get(IEnvironmentService).snippetsHome;
+
+ assert.deepEqual(await testObject.getLastSyncUserData(), null);
+ let manifest = await testClient.manifest();
+ server.reset();
+ await testObject.sync(manifest);
+
+ assert.deepEqual(server.requests, [
+ { type: 'GET', url: `${server.url}/v1/resource/${testObject.resource}/latest`, headers: {} },
+ ]);
+ assert.ok(!await fileService.exists(snippetsResource));
+
+ const lastSyncUserData = await testObject.getLastSyncUserData();
+ const remoteUserData = await testObject.getRemoteUserData(null);
+ assert.deepEqual(lastSyncUserData!.ref, remoteUserData.ref);
+ assert.deepEqual(lastSyncUserData!.syncData, remoteUserData.syncData);
+ assert.equal(lastSyncUserData!.syncData, null);
+
+ manifest = await testClient.manifest();
+ server.reset();
+ await testObject.sync(manifest);
+ assert.deepEqual(server.requests, []);
+
+ manifest = await testClient.manifest();
+ server.reset();
+ await testObject.sync(manifest);
+ assert.deepEqual(server.requests, []);
+ });
+
+ test('when snippet is created after first sync', async () => {
+ await testObject.sync(await testClient.manifest());
+ await updateSnippet('html.json', htmlSnippet1, testClient);
+
+ let lastSyncUserData = await testObject.getLastSyncUserData();
+ const manifest = await testClient.manifest();
+ server.reset();
+ await testObject.sync(manifest);
+
+ assert.deepEqual(server.requests, [
+ { type: 'POST', url: `${server.url}/v1/resource/${testObject.resource}`, headers: { 'If-Match': lastSyncUserData?.ref } },
+ ]);
+
+ lastSyncUserData = await testObject.getLastSyncUserData();
+ const remoteUserData = await testObject.getRemoteUserData(null);
+ assert.deepEqual(lastSyncUserData!.ref, remoteUserData.ref);
+ assert.deepEqual(lastSyncUserData!.syncData, remoteUserData.syncData);
+ assert.deepEqual(lastSyncUserData!.syncData!.content, JSON.stringify({ 'html.json': htmlSnippet1 }));
+ });
+
test('first time sync - outgoing to server (no snippets)', async () => {
await updateSnippet('html.json', htmlSnippet1, testClient);
await updateSnippet('typescript.json', tsSnippet1, testClient);
- await testObject.sync();
+ await testObject.sync(await testClient.manifest());
assert.equal(testObject.status, SyncStatus.Idle);
assert.deepEqual(testObject.conflicts, []);
@@ -186,7 +237,7 @@ suite('SnippetsSync', () => {
await updateSnippet('typescript.json', tsSnippet1, client2);
await client2.sync();
- await testObject.sync();
+ await testObject.sync(await testClient.manifest());
assert.equal(testObject.status, SyncStatus.Idle);
assert.deepEqual(testObject.conflicts, []);
@@ -201,7 +252,7 @@ suite('SnippetsSync', () => {
await client2.sync();
await updateSnippet('typescript.json', tsSnippet1, testClient);
- await testObject.sync();
+ await testObject.sync(await testClient.manifest());
assert.equal(testObject.status, SyncStatus.Idle);
assert.deepEqual(testObject.conflicts, []);
@@ -221,7 +272,7 @@ suite('SnippetsSync', () => {
await client2.sync();
await updateSnippet('html.json', htmlSnippet2, testClient);
- await testObject.sync();
+ await testObject.sync(await testClient.manifest());
assert.equal(testObject.status, SyncStatus.HasConflicts);
const environmentService = testClient.instantiationService.get(IEnvironmentService);
@@ -234,7 +285,7 @@ suite('SnippetsSync', () => {
await client2.sync();
await updateSnippet('html.json', htmlSnippet2, testClient);
- await testObject.sync();
+ await testObject.sync(await testClient.manifest());
const conflicts = testObject.conflicts;
await testObject.acceptConflict(conflicts[0].local, htmlSnippet1);
@@ -259,7 +310,7 @@ suite('SnippetsSync', () => {
await updateSnippet('html.json', htmlSnippet2, testClient);
await updateSnippet('typescript.json', tsSnippet2, testClient);
- await testObject.sync();
+ await testObject.sync(await testClient.manifest());
assert.equal(testObject.status, SyncStatus.HasConflicts);
const environmentService = testClient.instantiationService.get(IEnvironmentService);
@@ -278,7 +329,7 @@ suite('SnippetsSync', () => {
await updateSnippet('html.json', htmlSnippet2, testClient);
await updateSnippet('typescript.json', tsSnippet2, testClient);
- await testObject.sync();
+ await testObject.sync(await testClient.manifest());
let conflicts = testObject.conflicts;
await testObject.acceptConflict(conflicts[0].local, htmlSnippet2);
@@ -299,7 +350,7 @@ suite('SnippetsSync', () => {
await updateSnippet('html.json', htmlSnippet2, testClient);
await updateSnippet('typescript.json', tsSnippet2, testClient);
- await testObject.sync();
+ await testObject.sync(await testClient.manifest());
const conflicts = testObject.conflicts;
await testObject.acceptConflict(conflicts[0].local, htmlSnippet2);
@@ -324,10 +375,10 @@ suite('SnippetsSync', () => {
test('sync adding a snippet', async () => {
await updateSnippet('html.json', htmlSnippet1, testClient);
- await testObject.sync();
+ await testObject.sync(await testClient.manifest());
await updateSnippet('typescript.json', tsSnippet1, testClient);
- await testObject.sync();
+ await testObject.sync(await testClient.manifest());
assert.equal(testObject.status, SyncStatus.Idle);
assert.deepEqual(testObject.conflicts, []);
@@ -345,12 +396,12 @@ suite('SnippetsSync', () => {
test('sync adding a snippet - accept', async () => {
await updateSnippet('html.json', htmlSnippet1, client2);
await client2.sync();
- await testObject.sync();
+ await testObject.sync(await testClient.manifest());
await updateSnippet('typescript.json', tsSnippet1, client2);
await client2.sync();
- await testObject.sync();
+ await testObject.sync(await testClient.manifest());
assert.equal(testObject.status, SyncStatus.Idle);
assert.deepEqual(testObject.conflicts, []);
@@ -362,10 +413,10 @@ suite('SnippetsSync', () => {
test('sync updating a snippet', async () => {
await updateSnippet('html.json', htmlSnippet1, testClient);
- await testObject.sync();
+ await testObject.sync(await testClient.manifest());
await updateSnippet('html.json', htmlSnippet2, testClient);
- await testObject.sync();
+ await testObject.sync(await testClient.manifest());
assert.equal(testObject.status, SyncStatus.Idle);
assert.deepEqual(testObject.conflicts, []);
@@ -381,12 +432,12 @@ suite('SnippetsSync', () => {
test('sync updating a snippet - accept', async () => {
await updateSnippet('html.json', htmlSnippet1, client2);
await client2.sync();
- await testObject.sync();
+ await testObject.sync(await testClient.manifest());
await updateSnippet('html.json', htmlSnippet2, client2);
await client2.sync();
- await testObject.sync();
+ await testObject.sync(await testClient.manifest());
assert.equal(testObject.status, SyncStatus.Idle);
assert.deepEqual(testObject.conflicts, []);
@@ -397,13 +448,13 @@ suite('SnippetsSync', () => {
test('sync updating a snippet - conflict', async () => {
await updateSnippet('html.json', htmlSnippet1, client2);
await client2.sync();
- await testObject.sync();
+ await testObject.sync(await testClient.manifest());
await updateSnippet('html.json', htmlSnippet2, client2);
await client2.sync();
await updateSnippet('html.json', htmlSnippet3, testClient);
- await testObject.sync();
+ await testObject.sync(await testClient.manifest());
assert.equal(testObject.status, SyncStatus.HasConflicts);
const environmentService = testClient.instantiationService.get(IEnvironmentService);
const local = joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'html.json');
@@ -413,13 +464,13 @@ suite('SnippetsSync', () => {
test('sync updating a snippet - resolve conflict', async () => {
await updateSnippet('html.json', htmlSnippet1, client2);
await client2.sync();
- await testObject.sync();
+ await testObject.sync(await testClient.manifest());
await updateSnippet('html.json', htmlSnippet2, client2);
await client2.sync();
await updateSnippet('html.json', htmlSnippet3, testClient);
- await testObject.sync();
+ await testObject.sync(await testClient.manifest());
await testObject.acceptConflict(testObject.conflicts[0].local, htmlSnippet2);
assert.equal(testObject.status, SyncStatus.Idle);
@@ -437,10 +488,10 @@ suite('SnippetsSync', () => {
test('sync removing a snippet', async () => {
await updateSnippet('html.json', htmlSnippet1, testClient);
await updateSnippet('typescript.json', tsSnippet1, testClient);
- await testObject.sync();
+ await testObject.sync(await testClient.manifest());
await removeSnippet('html.json', testClient);
- await testObject.sync();
+ await testObject.sync(await testClient.manifest());
assert.equal(testObject.status, SyncStatus.Idle);
assert.deepEqual(testObject.conflicts, []);
@@ -459,12 +510,12 @@ suite('SnippetsSync', () => {
await updateSnippet('html.json', htmlSnippet1, client2);
await updateSnippet('typescript.json', tsSnippet1, client2);
await client2.sync();
- await testObject.sync();
+ await testObject.sync(await testClient.manifest());
await removeSnippet('html.json', client2);
await client2.sync();
- await testObject.sync();
+ await testObject.sync(await testClient.manifest());
assert.equal(testObject.status, SyncStatus.Idle);
assert.deepEqual(testObject.conflicts, []);
@@ -478,13 +529,13 @@ suite('SnippetsSync', () => {
await updateSnippet('html.json', htmlSnippet1, client2);
await updateSnippet('typescript.json', tsSnippet1, client2);
await client2.sync();
- await testObject.sync();
+ await testObject.sync(await testClient.manifest());
await updateSnippet('html.json', htmlSnippet2, client2);
await client2.sync();
await removeSnippet('html.json', testClient);
- await testObject.sync();
+ await testObject.sync(await testClient.manifest());
assert.equal(testObject.status, SyncStatus.Idle);
assert.deepEqual(testObject.conflicts, []);
@@ -499,13 +550,13 @@ suite('SnippetsSync', () => {
await updateSnippet('html.json', htmlSnippet1, client2);
await updateSnippet('typescript.json', tsSnippet1, client2);
await client2.sync();
- await testObject.sync();
+ await testObject.sync(await testClient.manifest());
await removeSnippet('html.json', client2);
await client2.sync();
await updateSnippet('html.json', htmlSnippet2, testClient);
- await testObject.sync();
+ await testObject.sync(await testClient.manifest());
assert.equal(testObject.status, SyncStatus.HasConflicts);
const environmentService = testClient.instantiationService.get(IEnvironmentService);
@@ -517,13 +568,13 @@ suite('SnippetsSync', () => {
await updateSnippet('html.json', htmlSnippet1, client2);
await updateSnippet('typescript.json', tsSnippet1, client2);
await client2.sync();
- await testObject.sync();
+ await testObject.sync(await testClient.manifest());
await removeSnippet('html.json', client2);
await client2.sync();
await updateSnippet('html.json', htmlSnippet2, testClient);
- await testObject.sync();
+ await testObject.sync(await testClient.manifest());
await testObject.acceptConflict(testObject.conflicts[0].local, htmlSnippet3);
assert.equal(testObject.status, SyncStatus.Idle);
@@ -544,13 +595,13 @@ suite('SnippetsSync', () => {
await updateSnippet('html.json', htmlSnippet1, client2);
await updateSnippet('typescript.json', tsSnippet1, client2);
await client2.sync();
- await testObject.sync();
+ await testObject.sync(await testClient.manifest());
await removeSnippet('html.json', client2);
await client2.sync();
await updateSnippet('html.json', htmlSnippet2, testClient);
- await testObject.sync();
+ await testObject.sync(await testClient.manifest());
await testObject.acceptConflict(testObject.conflicts[0].local, '');
assert.equal(testObject.status, SyncStatus.Idle);
@@ -601,7 +652,7 @@ suite('SnippetsSync', () => {
await updateSnippet('html.json', htmlSnippet1, client2);
await client2.sync();
- await testObject.sync();
+ await testObject.sync(await testClient.manifest());
assert.equal(testObject.status, SyncStatus.Idle);
assert.deepEqual(testObject.conflicts, []);
@@ -622,7 +673,7 @@ suite('SnippetsSync', () => {
await updateSnippet('typescript.json', tsSnippet1, client2);
await client2.sync();
- await testObject.sync();
+ await testObject.sync(await testClient.manifest());
assert.equal(testObject.status, SyncStatus.Idle);
assert.deepEqual(testObject.conflicts, []);
diff --git a/src/vs/platform/userDataSync/test/common/synchronizer.test.ts b/src/vs/platform/userDataSync/test/common/synchronizer.test.ts
index 3c2583e4626..340b95c0f24 100644
--- a/src/vs/platform/userDataSync/test/common/synchronizer.test.ts
+++ b/src/vs/platform/userDataSync/test/common/synchronizer.test.ts
@@ -79,7 +79,7 @@ suite('TestSynchronizer', () => {
const promise = Event.toPromise(testObject.onDoSyncCall.event);
- testObject.sync();
+ testObject.sync(await client.manifest());
await promise;
assert.deepEqual(actual, [SyncStatus.Syncing]);
@@ -94,7 +94,7 @@ suite('TestSynchronizer', () => {
const actual: SyncStatus[] = [];
disposableStore.add(testObject.onDidChangeStatus(status => actual.push(status)));
- await testObject.sync();
+ await testObject.sync(await client.manifest());
assert.deepEqual(actual, [SyncStatus.Syncing, SyncStatus.Idle]);
assert.deepEqual(testObject.status, SyncStatus.Idle);
@@ -107,7 +107,7 @@ suite('TestSynchronizer', () => {
const actual: SyncStatus[] = [];
disposableStore.add(testObject.onDidChangeStatus(status => actual.push(status)));
- await testObject.sync();
+ await testObject.sync(await client.manifest());
assert.deepEqual(actual, [SyncStatus.Syncing, SyncStatus.HasConflicts]);
assert.deepEqual(testObject.status, SyncStatus.HasConflicts);
@@ -122,7 +122,7 @@ suite('TestSynchronizer', () => {
disposableStore.add(testObject.onDidChangeStatus(status => actual.push(status)));
try {
- await testObject.sync();
+ await testObject.sync(await client.manifest());
assert.fail('Should fail');
} catch (e) {
assert.deepEqual(actual, [SyncStatus.Syncing, SyncStatus.Idle]);
@@ -134,12 +134,12 @@ suite('TestSynchronizer', () => {
const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings);
const promise = Event.toPromise(testObject.onDoSyncCall.event);
- testObject.sync();
+ testObject.sync(await client.manifest());
await promise;
const actual: SyncStatus[] = [];
disposableStore.add(testObject.onDidChangeStatus(status => actual.push(status)));
- await testObject.sync();
+ await testObject.sync(await client.manifest());
assert.deepEqual(actual, []);
assert.deepEqual(testObject.status, SyncStatus.Syncing);
@@ -154,7 +154,7 @@ suite('TestSynchronizer', () => {
const actual: SyncStatus[] = [];
disposableStore.add(testObject.onDidChangeStatus(status => actual.push(status)));
- await testObject.sync();
+ await testObject.sync(await client.manifest());
assert.deepEqual(actual, []);
assert.deepEqual(testObject.status, SyncStatus.Idle);
@@ -164,11 +164,11 @@ suite('TestSynchronizer', () => {
const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings);
testObject.syncResult = { status: SyncStatus.HasConflicts };
testObject.syncBarrier.open();
- await testObject.sync();
+ await testObject.sync(await client.manifest());
const actual: SyncStatus[] = [];
disposableStore.add(testObject.onDidChangeStatus(status => actual.push(status)));
- await testObject.sync();
+ await testObject.sync(await client.manifest());
assert.deepEqual(actual, []);
assert.deepEqual(testObject.status, SyncStatus.HasConflicts);
@@ -178,7 +178,7 @@ suite('TestSynchronizer', () => {
const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings);
// Sync once
testObject.syncBarrier.open();
- await testObject.sync();
+ await testObject.sync(await client.manifest());
testObject.syncBarrier = new Barrier();
// update remote data before syncing so that 412 is thrown by server
@@ -190,8 +190,9 @@ suite('TestSynchronizer', () => {
});
// Start sycing
- const { ref } = await userDataSyncStoreService.read(testObject.resource, null);
- await testObject.sync(ref);
+ const manifest = await client.manifest();
+ const ref = manifest!.latest![testObject.resource];
+ await testObject.sync(await client.manifest());
assert.deepEqual(server.requests, [
{ type: 'POST', url: `${server.url}/v1/resource/${testObject.resource}`, headers: { 'If-Match': ref } },
diff --git a/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts b/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts
index d9168c084cc..ae2edfc85d1 100644
--- a/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts
+++ b/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts
@@ -124,6 +124,10 @@ export class UserDataSyncClient extends Disposable {
return this.instantiationService.get(IUserDataSyncStoreService).read(resource, null);
}
+ manifest(): Promise {
+ return this.instantiationService.get(IUserDataSyncStoreService).manifest();
+ }
+
}
export class UserDataSyncTestServer implements IRequestService {
diff --git a/src/vs/platform/userDataSync/test/common/userDataSyncService.test.ts b/src/vs/platform/userDataSync/test/common/userDataSyncService.test.ts
index 5c2b51c2840..b7fbb82ed1c 100644
--- a/src/vs/platform/userDataSync/test/common/userDataSyncService.test.ts
+++ b/src/vs/platform/userDataSync/test/common/userDataSyncService.test.ts
@@ -45,7 +45,6 @@ suite('UserDataSyncService', () => {
{ type: 'POST', url: `${target.url}/v1/resource/globalState`, headers: { 'If-Match': '0' } },
// Extensions
{ type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} },
- { type: 'POST', url: `${target.url}/v1/resource/extensions`, headers: { 'If-Match': '0' } },
// Manifest
{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} },
]);
@@ -71,13 +70,10 @@ suite('UserDataSyncService', () => {
{ type: 'GET', url: `${target.url}/v1/resource/keybindings/latest`, headers: {} },
// Snippets
{ type: 'GET', url: `${target.url}/v1/resource/snippets/latest`, headers: {} },
- { type: 'POST', url: `${target.url}/v1/resource/snippets`, headers: { 'If-Match': '0' } },
// Global state
{ type: 'GET', url: `${target.url}/v1/resource/globalState/latest`, headers: {} },
- { type: 'POST', url: `${target.url}/v1/resource/globalState`, headers: { 'If-Match': '0' } },
// Extensions
{ type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} },
- { type: 'POST', url: `${target.url}/v1/resource/extensions`, headers: { 'If-Match': '0' } },
// Manifest
{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} },
]);
@@ -384,7 +380,6 @@ suite('UserDataSyncService', () => {
{ type: 'POST', url: `${target.url}/v1/resource/globalState`, headers: { 'If-Match': '0' } },
// Extensions
{ type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} },
- { type: 'POST', url: `${target.url}/v1/resource/extensions`, headers: { 'If-Match': '0' } },
// Manifest
{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} },
]);
diff --git a/src/vs/platform/windows/electron-main/windows.ts b/src/vs/platform/windows/electron-main/windows.ts
index 0db6846abd9..5c6facab0a9 100644
--- a/src/vs/platform/windows/electron-main/windows.ts
+++ b/src/vs/platform/windows/electron-main/windows.ts
@@ -105,7 +105,7 @@ export interface IWindowsMainService {
readonly onWindowsCountChanged: Event;
open(openConfig: IOpenConfiguration): ICodeWindow[];
- openEmptyWindow(context: OpenContext, options?: IOpenEmptyWindowOptions): ICodeWindow[];
+ openEmptyWindow(openConfig: IOpenEmptyConfiguration, options?: IOpenEmptyWindowOptions): ICodeWindow[];
openExtensionDevelopmentHostWindow(extensionDevelopmentPath: string[], openConfig: IOpenConfiguration): ICodeWindow[];
sendToFocused(channel: string, ...args: any[]): void;
@@ -118,9 +118,12 @@ export interface IWindowsMainService {
getWindowCount(): number;
}
-export interface IOpenConfiguration {
+export interface IBaseOpenConfiguration {
readonly context: OpenContext;
readonly contextWindowId?: number;
+}
+
+export interface IOpenConfiguration extends IBaseOpenConfiguration {
readonly cli: ParsedArgs;
readonly userEnv?: IProcessEnvironment;
readonly urisToOpen?: IWindowOpenable[];
@@ -136,3 +139,5 @@ export interface IOpenConfiguration {
readonly initialStartup?: boolean;
readonly noRecentEntry?: boolean;
}
+
+export interface IOpenEmptyConfiguration extends IBaseOpenConfiguration { }
diff --git a/src/vs/platform/windows/electron-main/windowsMainService.ts b/src/vs/platform/windows/electron-main/windowsMainService.ts
index 266c235a0fe..cdd6731748d 100644
--- a/src/vs/platform/windows/electron-main/windowsMainService.ts
+++ b/src/vs/platform/windows/electron-main/windowsMainService.ts
@@ -24,7 +24,7 @@ import { IWindowSettings, IPath, isFileToOpen, isWorkspaceToOpen, isFolderToOpen
import { getLastActiveWindow, findBestWindowOrFolderForFile, findWindowOnWorkspace, findWindowOnExtensionDevelopmentPath, findWindowOnWorkspaceOrFolderUri, INativeWindowConfiguration, OpenContext, IAddFoldersRequest, IPathsToWaitFor } from 'vs/platform/windows/node/window';
import { Emitter } from 'vs/base/common/event';
import product from 'vs/platform/product/common/product';
-import { IWindowsMainService, IOpenConfiguration, IWindowsCountChangedEvent, ICodeWindow, IWindowState as ISingleWindowState, WindowMode } from 'vs/platform/windows/electron-main/windows';
+import { IWindowsMainService, IOpenConfiguration, IWindowsCountChangedEvent, ICodeWindow, IWindowState as ISingleWindowState, WindowMode, IOpenEmptyConfiguration } from 'vs/platform/windows/electron-main/windows';
import { IWorkspacesHistoryMainService } from 'vs/platform/workspaces/electron-main/workspacesHistoryMainService';
import { IProcessEnvironment, isMacintosh, isWindows } from 'vs/base/common/platform';
import { IWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, hasWorkspaceFileExtension, IRecent } from 'vs/platform/workspaces/common/workspaces';
@@ -393,7 +393,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
};
}
- openEmptyWindow(context: OpenContext, options?: IOpenEmptyWindowOptions): ICodeWindow[] {
+ openEmptyWindow(openConfig: IOpenEmptyConfiguration, options?: IOpenEmptyWindowOptions): ICodeWindow[] {
let cli = this.environmentService.args;
const remote = options?.remoteAuthority;
if (cli && (cli.remote !== remote)) {
@@ -403,7 +403,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
const forceReuseWindow = options?.forceReuseWindow;
const forceNewWindow = !forceReuseWindow;
- return this.open({ context, cli, forceEmpty: true, forceNewWindow, forceReuseWindow });
+ return this.open({ ...openConfig, cli, forceEmpty: true, forceNewWindow, forceReuseWindow });
}
open(openConfig: IOpenConfiguration): ICodeWindow[] {
@@ -474,7 +474,6 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
// Make sure to pass focus to the most relevant of the windows if we open multiple
if (usedWindows.length > 1) {
-
const focusLastActive = this.windowsState.lastActiveWindow && !openConfig.forceEmpty && openConfig.cli._.length && !openConfig.cli['file-uri'] && !openConfig.cli['folder-uri'] && !(openConfig.urisToOpen && openConfig.urisToOpen.length);
let focusLastOpened = true;
let focusLastWindow = true;
@@ -753,15 +752,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
const remoteAuthority = fileInputs ? fileInputs.remoteAuthority : (openConfig.cli && openConfig.cli.remote || undefined);
for (let i = 0; i < emptyToOpen; i++) {
- usedWindows.push(this.openInBrowserWindow({
- userEnv: openConfig.userEnv,
- cli: openConfig.cli,
- initialStartup: openConfig.initialStartup,
- remoteAuthority,
- forceNewWindow: openFolderInNewWindow,
- forceNewTabbedWindow: openConfig.forceNewTabbedWindow,
- fileInputs
- }));
+ usedWindows.push(this.doOpenEmpty(openConfig, openFolderInNewWindow, remoteAuthority, fileInputs));
// Reset these because we handled them
fileInputs = undefined;
@@ -801,12 +792,29 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
return window;
}
+ private doOpenEmpty(openConfig: IOpenConfiguration, forceNewWindow: boolean, remoteAuthority: string | undefined, fileInputs: IFileInputs | undefined, windowToUse?: ICodeWindow): ICodeWindow {
+ if (!forceNewWindow && !windowToUse && typeof openConfig.contextWindowId === 'number') {
+ windowToUse = this.getWindowById(openConfig.contextWindowId); // fix for https://github.com/microsoft/vscode/issues/97172
+ }
+
+ return this.openInBrowserWindow({
+ userEnv: openConfig.userEnv,
+ cli: openConfig.cli,
+ initialStartup: openConfig.initialStartup,
+ remoteAuthority,
+ forceNewWindow,
+ forceNewTabbedWindow: openConfig.forceNewTabbedWindow,
+ fileInputs,
+ windowToUse
+ });
+ }
+
private doOpenFolderOrWorkspace(openConfig: IOpenConfiguration, folderOrWorkspace: IPathToOpen, forceNewWindow: boolean, fileInputs: IFileInputs | undefined, windowToUse?: ICodeWindow): ICodeWindow {
if (!forceNewWindow && !windowToUse && typeof openConfig.contextWindowId === 'number') {
windowToUse = this.getWindowById(openConfig.contextWindowId); // fix for https://github.com/Microsoft/vscode/issues/49587
}
- const browserWindow = this.openInBrowserWindow({
+ return this.openInBrowserWindow({
userEnv: openConfig.userEnv,
cli: openConfig.cli,
initialStartup: openConfig.initialStartup,
@@ -818,8 +826,6 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
forceNewTabbedWindow: openConfig.forceNewTabbedWindow,
windowToUse
});
-
- return browserWindow;
}
private getPathsToOpen(openConfig: IOpenConfiguration): IPathToOpen[] {
diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts
index 6b7f9014818..d412b4e8a56 100644
--- a/src/vs/vscode.d.ts
+++ b/src/vs/vscode.d.ts
@@ -2270,7 +2270,7 @@ declare module 'vscode' {
* A code lens provider adds [commands](#Command) to source text. The commands will be shown
* as dedicated horizontal lines in between the source text.
*/
- export interface CodeLensProvider {
+ export interface CodeLensProvider {
/**
* An optional event to signal that the code lenses from this provider have changed.
@@ -2287,17 +2287,17 @@ declare module 'vscode' {
* @return An array of code lenses or a thenable that resolves to such. The lack of a result can be
* signaled by returning `undefined`, `null`, or an empty array.
*/
- provideCodeLenses(document: TextDocument, token: CancellationToken): ProviderResult;
+ provideCodeLenses(document: TextDocument, token: CancellationToken): ProviderResult;
/**
* This function will be called for each visible code lens, usually when scrolling and after
* calls to [compute](#CodeLensProvider.provideCodeLenses)-lenses.
*
- * @param codeLens code lens that must be resolved.
+ * @param codeLens Code lens that must be resolved.
* @param token A cancellation token.
* @return The given, resolved code lens or thenable that resolves to such.
*/
- resolveCodeLens?(codeLens: CodeLens, token: CancellationToken): ProviderResult;
+ resolveCodeLens?(codeLens: T, token: CancellationToken): ProviderResult;
}
/**
@@ -2798,7 +2798,7 @@ declare module 'vscode' {
* The workspace symbol provider interface defines the contract between extensions and
* the [symbol search](https://code.visualstudio.com/docs/editor/editingevolved#_open-symbol-by-name)-feature.
*/
- export interface WorkspaceSymbolProvider {
+ export interface WorkspaceSymbolProvider {
/**
* Project-wide search for a symbol matching the given query string.
@@ -2817,7 +2817,7 @@ declare module 'vscode' {
* @return An array of document highlights or a thenable that resolves to such. The lack of a result can be
* signaled by returning `undefined`, `null`, or an empty array.
*/
- provideWorkspaceSymbols(query: string, token: CancellationToken): ProviderResult;
+ provideWorkspaceSymbols(query: string, token: CancellationToken): ProviderResult;
/**
* Given a symbol fill in its [location](#SymbolInformation.location). This method is called whenever a symbol
@@ -2831,7 +2831,7 @@ declare module 'vscode' {
* @return The resolved symbol or a thenable that resolves to that. When no result is returned,
* the given `symbol` is used.
*/
- resolveWorkspaceSymbol?(symbol: SymbolInformation, token: CancellationToken): ProviderResult;
+ resolveWorkspaceSymbol?(symbol: T, token: CancellationToken): ProviderResult;
}
/**
@@ -3858,7 +3858,7 @@ declare module 'vscode' {
* Represents a collection of [completion items](#CompletionItem) to be presented
* in the editor.
*/
- export class CompletionList {
+ export class CompletionList {
/**
* This list is not complete. Further typing should result in recomputing
@@ -3869,7 +3869,7 @@ declare module 'vscode' {
/**
* The completion items.
*/
- items: CompletionItem[];
+ items: T[];
/**
* Creates a new completion list.
@@ -3877,7 +3877,7 @@ declare module 'vscode' {
* @param items The completion items.
* @param isIncomplete The list is not complete.
*/
- constructor(items?: CompletionItem[], isIncomplete?: boolean);
+ constructor(items?: T[], isIncomplete?: boolean);
}
/**
@@ -3931,7 +3931,7 @@ declare module 'vscode' {
* Providers are asked for completions either explicitly by a user gesture or -depending on the configuration-
* implicitly when typing words or trigger characters.
*/
- export interface CompletionItemProvider {
+ export interface CompletionItemProvider {
/**
* Provide completion items for the given position and document.
@@ -3944,7 +3944,7 @@ declare module 'vscode' {
* @return An array of completions, a [completion list](#CompletionList), or a thenable that resolves to either.
* The lack of a result can be signaled by returning `undefined`, `null`, or an empty array.
*/
- provideCompletionItems(document: TextDocument, position: Position, token: CancellationToken, context: CompletionContext): ProviderResult;
+ provideCompletionItems(document: TextDocument, position: Position, token: CancellationToken, context: CompletionContext): ProviderResult>;
/**
* Given a completion item fill in more data, like [doc-comment](#CompletionItem.documentation)
@@ -3961,7 +3961,7 @@ declare module 'vscode' {
* @return The resolved completion item or a thenable that resolves to of such. It is OK to return the given
* `item`. When no result is returned, the given `item` will be used.
*/
- resolveCompletionItem?(item: CompletionItem, token: CancellationToken): ProviderResult;
+ resolveCompletionItem?(item: T, token: CancellationToken): ProviderResult;
}
@@ -4003,7 +4003,7 @@ declare module 'vscode' {
* The document link provider defines the contract between extensions and feature of showing
* links in the editor.
*/
- export interface DocumentLinkProvider {
+ export interface DocumentLinkProvider {
/**
* Provide links for the given document. Note that the editor ships with a default provider that detects
@@ -4014,7 +4014,7 @@ declare module 'vscode' {
* @return An array of [document links](#DocumentLink) or a thenable that resolves to such. The lack of a result
* can be signaled by returning `undefined`, `null`, or an empty array.
*/
- provideDocumentLinks(document: TextDocument, token: CancellationToken): ProviderResult;
+ provideDocumentLinks(document: TextDocument, token: CancellationToken): ProviderResult;
/**
* Given a link fill in its [target](#DocumentLink.target). This method is called when an incomplete
@@ -4025,7 +4025,7 @@ declare module 'vscode' {
* @param link The link that is to be resolved.
* @param token A cancellation token.
*/
- resolveDocumentLink?(link: DocumentLink, token: CancellationToken): ProviderResult;
+ resolveDocumentLink?(link: T, token: CancellationToken): ProviderResult;
}
/**
@@ -6036,13 +6036,13 @@ declare module 'vscode' {
* A task provider allows to add tasks to the task service.
* A task provider is registered via #tasks.registerTaskProvider.
*/
- export interface TaskProvider {
+ export interface TaskProvider {
/**
* Provides tasks.
* @param token A cancellation token.
* @return an array of tasks
*/
- provideTasks(token?: CancellationToken): ProviderResult;
+ provideTasks(token?: CancellationToken): ProviderResult;
/**
* Resolves a task that has no [`execution`](#Task.execution) set. Tasks are
@@ -6057,7 +6057,7 @@ declare module 'vscode' {
* @param token A cancellation token.
* @return The resolved task
*/
- resolveTask(task: Task, token?: CancellationToken): ProviderResult;
+ resolveTask(task: T, token?: CancellationToken): ProviderResult;
}
/**
diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts
index da705cb2bb8..5eda9075136 100644
--- a/src/vs/vscode.proposed.d.ts
+++ b/src/vs/vscode.proposed.d.ts
@@ -77,6 +77,11 @@ declare module 'vscode' {
readonly id: string;
readonly displayName: string;
+ /**
+ * Whether it is possible to be signed into multiple accounts at once.
+ */
+ supportsMultipleAccounts: boolean;
+
/**
* An [event](#Event) which fires when the array of sessions has changed, or data
* within a session has changed.
@@ -109,6 +114,41 @@ declare module 'vscode' {
export const providerIds: string[];
/**
+ * Returns whether a provider has any sessions matching the requested scopes. This request
+ * is transparent to the user, not UI is shown. Rejects if a provider with providerId is not
+ * registered.
+ * @param providerId The id of the provider
+ * @param scopes A list of scopes representing the permissions requested. These are dependent on the authentication
+ * provider
+ */
+ export function hasSessions(providerId: string, scopes: string[]): Thenable;
+
+ export interface GetSessionOptions {
+ /**
+ * Whether login should be performed if there is no matching session. Defaults to false.
+ */
+ createIfNone?: boolean;
+
+ /**
+ * Whether the existing user session preference should be cleared. Set to allow the user to switch accounts.
+ * Defaults to false.
+ */
+ clearSessionPreference?: boolean;
+ }
+
+ /**
+ * Get an authentication session matching the desired scopes. Rejects if a provider with providerId is not
+ * registered, or if the user does not consent to sharing authentication information with
+ * the extension. If there are multiple sessions with the same scopes, the user will be shown a
+ * quickpick to select which account they would like to use.
+ * @param providerId The id of the provider to use
+ * @param scopes A list of scopes representing the permissions requested. These are dependent on the authentication provider
+ * @param options The [getSessionOptions](#GetSessionOptions) to use
+ */
+ export function getSession(providerId: string, scopes: string[], options: GetSessionOptions): Thenable;
+
+ /**
+ * @deprecated
* Get existing authentication sessions. Rejects if a provider with providerId is not
* registered, or if the user does not consent to sharing authentication information with
* the extension.
@@ -116,9 +156,10 @@ declare module 'vscode' {
* @param scopes A list of scopes representing the permissions requested. These are dependent on the authentication
* provider
*/
- export function getSessions(providerId: string, scopes: string[]): Thenable;
+ export function getSessions(providerId: string, scopes: string[]): Thenable>;
/**
+ * @deprecated
* Prompt a user to login to create a new authenticaiton session. Rejects if a provider with
* providerId is not registered, or if the user does not consent to sharing authentication
* information with the extension.
@@ -129,6 +170,7 @@ declare module 'vscode' {
export function login(providerId: string, scopes: string[]): Thenable;
/**
+ * @deprecated
* Logout of a specific session.
* @param providerId The id of the provider to use
* @param sessionId The session id to remove
@@ -1640,12 +1682,6 @@ declare module 'vscode' {
edit(callback: (editBuilder: NotebookEditorCellEdit) => void): Thenable;
}
- export interface NotebookProvider {
- resolveNotebook(editor: NotebookEditor): Promise;
- executeCell(document: NotebookDocument, cell: NotebookCell | undefined, token: CancellationToken): Promise;
- save(document: NotebookDocument): Promise;
- }
-
export interface NotebookOutputSelector {
type: string;
subTypes?: string[];
@@ -1688,11 +1724,20 @@ declare module 'vscode' {
readonly metadata: NotebookDocumentMetadata;
}
+ interface NotebookDocumentEditEvent {
+
+ /**
+ * The document that the edit is for.
+ */
+ readonly document: NotebookDocument;
+ }
+
export interface NotebookContentProvider {
openNotebook(uri: Uri): NotebookData | Promise;
saveNotebook(document: NotebookDocument, cancellation: CancellationToken): Promise;
saveNotebookAs(targetResource: Uri, document: NotebookDocument, cancellation: CancellationToken): Promise;
- readonly onDidChangeNotebook: Event;
+ readonly onDidChangeNotebook: Event;
+
// revert?(document: NotebookDocument, cancellation: CancellationToken): Thenable;
// backup?(document: NotebookDocument, cancellation: CancellationToken): Thenable;
@@ -1708,11 +1753,6 @@ declare module 'vscode' {
provider: NotebookContentProvider
): Disposable;
- export function registerNotebookProvider(
- notebookType: string,
- provider: NotebookProvider
- ): Disposable;
-
export function registerNotebookOutputRenderer(
type: string,
outputSelector: NotebookOutputSelector,
diff --git a/src/vs/workbench/api/browser/mainThreadAuthentication.ts b/src/vs/workbench/api/browser/mainThreadAuthentication.ts
index ccabaa3af36..1ae18d4aee4 100644
--- a/src/vs/workbench/api/browser/mainThreadAuthentication.ts
+++ b/src/vs/workbench/api/browser/mainThreadAuthentication.ts
@@ -294,7 +294,8 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
@IStorageService private readonly storageService: IStorageService,
@INotificationService private readonly notificationService: INotificationService,
@IStorageKeysSyncRegistryService private readonly storageKeysSyncRegistryService: IStorageKeysSyncRegistryService,
- @IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService
+ @IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService,
+ @IQuickInputService private readonly quickInputService: IQuickInputService
) {
super();
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostAuthentication);
@@ -314,6 +315,72 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
this.authenticationService.sessionsUpdate(id, event);
}
+ async $getSession(providerId: string, providerName: string, extensionId: string, extensionName: string, potentialSessions: modes.AuthenticationSession[], scopes: string[], clearSessionPreference: boolean): Promise {
+ if (!potentialSessions.length) {
+ throw new Error('No potential sessions found');
+ }
+
+ if (clearSessionPreference) {
+ this.storageService.remove(`${extensionName}-${providerId}`, StorageScope.GLOBAL);
+ } else {
+ const existingSessionPreference = this.storageService.get(`${extensionName}-${providerId}`, StorageScope.GLOBAL);
+ if (existingSessionPreference) {
+ const matchingSession = potentialSessions.find(session => session.id === existingSessionPreference);
+ if (matchingSession) {
+ const allowed = await this.$getSessionsPrompt(providerId, matchingSession.account.displayName, providerName, extensionId, extensionName);
+ if (allowed) {
+ return matchingSession;
+ }
+ }
+ }
+ }
+
+ return new Promise((resolve, reject) => {
+ const quickPick = this.quickInputService.createQuickPick<{ label: string, session?: modes.AuthenticationSession }>();
+ quickPick.ignoreFocusOut = true;
+ const items: { label: string, session?: modes.AuthenticationSession }[] = potentialSessions.map(session => {
+ return {
+ label: session.account.displayName,
+ session
+ };
+ });
+
+ items.push({
+ label: nls.localize('useOtherAccount', "Sign in to another account")
+ });
+
+ quickPick.items = items;
+ quickPick.title = nls.localize('selectAccount', "The extension '{0}' wants to access a {1} account", extensionName, providerName);
+ quickPick.placeholder = nls.localize('getSessionPlateholder', "Select an account for '{0}' to use or Esc to cancel", extensionName);
+
+ quickPick.onDidAccept(async _ => {
+ const selected = quickPick.selectedItems[0];
+
+ const session = selected.session ?? await this.authenticationService.login(providerId, scopes);
+
+ const accountName = session.account.displayName;
+ const allowList = readAllowedExtensions(this.storageService, providerId, accountName);
+ allowList.push({ id: extensionId, name: extensionName });
+ this.storageService.store(`${providerId}-${accountName}`, JSON.stringify(allowList), StorageScope.GLOBAL);
+
+ this.storageService.store(`${extensionName}-${providerId}`, session.id, StorageScope.GLOBAL);
+
+ quickPick.dispose();
+ resolve(session);
+ });
+
+ quickPick.onDidHide(_ => {
+ if (!quickPick.selectedItems[0]) {
+ reject('User did not consent to account access');
+ }
+
+ quickPick.dispose();
+ });
+
+ quickPick.show();
+ });
+ }
+
async $getSessionsPrompt(providerId: string, accountName: string, providerName: string, extensionId: string, extensionName: string): Promise {
addAccountUsage(providerId, accountName, extensionName);
diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts
index 4653ef3ffec..efdef129e3f 100644
--- a/src/vs/workbench/api/browser/mainThreadNotebook.ts
+++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts
@@ -8,7 +8,7 @@ import { MainContext, MainThreadNotebookShape, NotebookExtensionDescription, IEx
import { Disposable } from 'vs/base/common/lifecycle';
import { URI, UriComponents } from 'vs/base/common/uri';
import { INotebookService, IMainNotebookController } from 'vs/workbench/contrib/notebook/common/notebookService';
-import { INotebookTextModel, INotebookMimeTypeSelector, NOTEBOOK_DISPLAY_ORDER, NotebookCellOutputsSplice, CellKind, NotebookDocumentMetadata, NotebookCellMetadata, ICellEditOperation, ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER } from 'vs/workbench/contrib/notebook/common/notebookCommon';
+import { INotebookTextModel, INotebookMimeTypeSelector, NOTEBOOK_DISPLAY_ORDER, NotebookCellOutputsSplice, NotebookDocumentMetadata, NotebookCellMetadata, ICellEditOperation, ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, CellEditType } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
@@ -84,7 +84,9 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
registerListeners() {
this._register(this._notebookService.onDidChangeActiveEditor(e => {
- this._proxy.$updateActiveEditor(e.viewType, e.uri);
+ this._proxy.$acceptDocumentAndEditorsDelta({
+ newActiveEditor: e.uri
+ });
}));
const updateOrder = () => {
@@ -129,16 +131,6 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
return;
}
- async $createNotebookDocument(handle: number, viewType: string, resource: UriComponents): Promise {
- let controller = this._notebookProviders.get(viewType);
-
- if (controller) {
- controller.createNotebookDocument(handle, viewType, resource);
- }
-
- return;
- }
-
async $updateNotebookLanguages(viewType: string, resource: UriComponents, languages: string[]): Promise {
let controller = this._notebookProviders.get(viewType);
@@ -163,11 +155,6 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
}
}
- async resolveNotebook(viewType: string, uri: URI): Promise {
- let handle = await this._proxy.$resolveNotebook(viewType, uri);
- return handle;
- }
-
async $spliceNotebookCellOutputs(viewType: string, resource: UriComponents, cellHandle: number, splices: NotebookCellOutputsSplice[], renderers: number[]): Promise {
let controller = this._notebookProviders.get(viewType);
controller?.spliceNotebookCellOutputs(resource, cellHandle, splices, renderers);
@@ -195,6 +182,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
export class MainThreadNotebookController implements IMainNotebookController {
private _mapping: Map = new Map();
+ static documentHandle: number = 0;
constructor(
private readonly _proxy: ExtHostNotebookShape,
@@ -203,26 +191,44 @@ export class MainThreadNotebookController implements IMainNotebookController {
) {
}
- async resolveNotebook(viewType: string, uri: URI): Promise {
- // TODO: resolve notebook should wait for all notebook document destory operations to finish.
+ async createNotebook(viewType: string, uri: URI, forBackup: boolean, forceReload: boolean): Promise {
let mainthreadNotebook = this._mapping.get(URI.from(uri).toString());
if (mainthreadNotebook) {
+ if (forceReload) {
+ const data = await this._proxy.$resolveNotebookData(viewType, uri);
+ if (!data) {
+ return;
+ }
+
+ mainthreadNotebook.textModel.languages = data.languages;
+ mainthreadNotebook.textModel.metadata = data.metadata;
+ mainthreadNotebook.textModel.applyEdit(mainthreadNotebook.textModel.versionId, [
+ { editType: CellEditType.Delete, count: mainthreadNotebook.textModel.cells.length, index: 0 },
+ { editType: CellEditType.Insert, index: 0, cells: data.cells }
+ ]);
+ }
return mainthreadNotebook.textModel;
}
- let notebookHandle = await this._mainThreadNotebook.resolveNotebook(viewType, uri);
- if (notebookHandle !== undefined) {
- mainthreadNotebook = this._mapping.get(URI.from(uri).toString());
- if (mainthreadNotebook && mainthreadNotebook.textModel.cells.length === 0) {
- // it's empty, we should create an empty template one
- const mainCell = mainthreadNotebook.textModel.createCellTextModel([''], mainthreadNotebook.textModel.languages.length ? mainthreadNotebook.textModel.languages[0] : '', CellKind.Code, [], undefined);
- mainthreadNotebook.textModel.insertTemplateCell(mainCell);
- }
- return mainthreadNotebook?.textModel;
+ let document = new MainThreadNotebookDocument(this._proxy, MainThreadNotebookController.documentHandle++, viewType, uri);
+ await this.createNotebookDocument(document);
+
+ if (forBackup) {
+ return document.textModel;
}
- return undefined;
+ // open notebook document
+ const data = await this._proxy.$resolveNotebookData(viewType, uri);
+ if (!data) {
+ return;
+ }
+
+ document.textModel.languages = data.languages;
+ document.textModel.metadata = data.metadata;
+ document.textModel.initialize(data!.cells);
+
+ return document.textModel;
}
async tryApplyEdits(resource: UriComponents, modelVersionId: number, edits: ICellEditOperation[], renderers: number[]): Promise {
@@ -250,12 +256,32 @@ export class MainThreadNotebookController implements IMainNotebookController {
this._proxy.$onDidReceiveMessage(uri, message);
}
- // Methods for ExtHost
- async createNotebookDocument(handle: number, viewType: string, resource: UriComponents): Promise {
- let document = new MainThreadNotebookDocument(this._proxy, handle, viewType, URI.revive(resource));
- this._mapping.set(URI.revive(resource).toString(), document);
+ async createNotebookDocument(document: MainThreadNotebookDocument): Promise {
+ this._mapping.set(document.uri.toString(), document);
+
+ await this._proxy.$acceptDocumentAndEditorsDelta({
+ addedDocuments: [{
+ viewType: document.viewType,
+ handle: document.handle,
+ uri: document.uri
+ }]
+ });
}
+ async removeNotebookDocument(notebook: INotebookTextModel): Promise {
+ let document = this._mapping.get(URI.from(notebook.uri).toString());
+
+ if (!document) {
+ return;
+ }
+
+ await this._proxy.$acceptDocumentAndEditorsDelta({ removedDocuments: [notebook.uri] });
+ document.dispose();
+ this._mapping.delete(URI.from(notebook.uri).toString());
+ }
+
+ // Methods for ExtHost
+
updateLanguages(resource: UriComponents, languages: string[]) {
let document = this._mapping.get(URI.from(resource).toString());
document?.textModel.updateLanguages(languages);
@@ -280,21 +306,12 @@ export class MainThreadNotebookController implements IMainNotebookController {
return this._proxy.$executeNotebook(this._viewType, uri, handle, token);
}
- async destoryNotebookDocument(notebook: INotebookTextModel): Promise {
- let document = this._mapping.get(URI.from(notebook.uri).toString());
-
- if (!document) {
- return;
- }
-
- let removeFromExtHost = await this._proxy.$destoryNotebookDocument(this._viewType, notebook.uri);
- if (removeFromExtHost) {
- document.dispose();
- this._mapping.delete(URI.from(notebook.uri).toString());
- }
- }
-
async save(uri: URI, token: CancellationToken): Promise {
return this._proxy.$saveNotebook(this._viewType, uri, token);
}
+
+ async saveAs(uri: URI, target: URI, token: CancellationToken): Promise {
+ return this._proxy.$saveNotebookAs(this._viewType, uri, target, token);
+
+ }
}
diff --git a/src/vs/workbench/api/browser/mainThreadTheming.ts b/src/vs/workbench/api/browser/mainThreadTheming.ts
index 4f0fa417240..d810863f852 100644
--- a/src/vs/workbench/api/browser/mainThreadTheming.ts
+++ b/src/vs/workbench/api/browser/mainThreadTheming.ts
@@ -25,6 +25,7 @@ export class MainThreadTheming implements MainThreadThemingShape {
this._themeChangeListener = this._themeService.onDidColorThemeChange(e => {
this._proxy.$onColorThemeChange(this._themeService.getColorTheme().type);
});
+ this._proxy.$onColorThemeChange(this._themeService.getColorTheme().type);
}
dispose(): void {
diff --git a/src/vs/workbench/api/browser/mainThreadWebview.ts b/src/vs/workbench/api/browser/mainThreadWebview.ts
index 66dd9fa03c8..9e4d58a2cd8 100644
--- a/src/vs/workbench/api/browser/mainThreadWebview.ts
+++ b/src/vs/workbench/api/browser/mainThreadWebview.ts
@@ -325,13 +325,14 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
throw new Error(`Provider for ${viewType} already registered`);
}
- this._customEditorService.registerCustomEditorCapabilities(viewType, {
- supportsMultipleEditorsPerDocument
- });
-
const extension = reviveWebviewExtension(extensionData);
const disposables = new DisposableStore();
+
+ disposables.add(this._customEditorService.registerCustomEditorCapabilities(viewType, {
+ supportsMultipleEditorsPerDocument
+ }));
+
disposables.add(this._webviewWorkbenchService.registerResolver({
canResolve: (webviewInput) => {
return webviewInput instanceof CustomEditorInput && webviewInput.viewType === viewType;
diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts
index 46bf7934fd0..df17b498274 100644
--- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts
+++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts
@@ -80,6 +80,9 @@ interface IUserFriendlyViewDescriptor {
name: string;
when?: string;
+ icon?: string;
+ contextualTitle?: string;
+
// From 'remoteViewDescriptor' type
group?: string;
remoteName?: string | string[];
@@ -100,6 +103,14 @@ const viewDescriptor: IJSONSchema = {
description: localize('vscode.extension.contributes.view.when', 'Condition which must be true to show this view'),
type: 'string'
},
+ icon: {
+ description: localize('vscode.extension.contributes.view.icon', "Path to the view icon. View icons are displayed when the name of the view cannot be shown. It is recommended that icons be in SVG, though any image file type is accepted."),
+ type: 'string'
+ },
+ contextualTitle: {
+ description: localize('vscode.extension.contributes.view.contextualTitle', "Human-readable context for when the view is moved out of its original location. By default, the view's container name will be used. Will be shown"),
+ type: 'string'
+ },
}
};
@@ -406,12 +417,14 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
? container.viewOrderDelegate.getOrder(item.group)
: undefined;
+ const icon = item.icon ? resources.joinPath(extension.description.extensionLocation, item.icon) : undefined;
const viewDescriptor = {
id: item.id,
name: item.name,
ctorDescriptor: new SyncDescriptor(TreeViewPane),
when: ContextKeyExpr.deserialize(item.when),
- containerIcon: viewContainer?.icon,
+ containerIcon: icon || viewContainer?.icon,
+ containerTitle: item.contextualTitle || viewContainer?.name,
canToggleVisibility: true,
canMoveView: true,
treeView: this.instantiationService.createInstance(CustomTreeView, item.id, item.name),
@@ -468,6 +481,14 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
collector.error(localize('optstring', "property `{0}` can be omitted or must be of type `string`", 'when'));
return false;
}
+ if (descriptor.icon && typeof descriptor.icon !== 'string') {
+ collector.error(localize('optstring', "property `{0}` can be omitted or must be of type `string`", 'icon'));
+ return false;
+ }
+ if (descriptor.contextualTitle && typeof descriptor.contextualTitle !== 'string') {
+ collector.error(localize('optstring', "property `{0}` can be omitted or must be of type `string`", 'contextualTitle'));
+ return false;
+ }
}
return true;
diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts
index a4736ede416..57de023e859 100644
--- a/src/vs/workbench/api/common/extHost.api.impl.ts
+++ b/src/vs/workbench/api/common/extHost.api.impl.ts
@@ -196,6 +196,12 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
get providerIds(): string[] {
return extHostAuthentication.providerIds;
},
+ hasSessions(providerId: string, scopes: string[]): Thenable {
+ return extHostAuthentication.hasSessions(providerId, scopes);
+ },
+ getSession(providerId: string, scopes: string[], options: vscode.authentication.GetSessionOptions): Thenable {
+ return extHostAuthentication.getSession(extension, providerId, scopes, options);
+ },
getSessions(providerId: string, scopes: string[]): Thenable {
return extHostAuthentication.getSessions(extension, providerId, scopes);
},
@@ -910,10 +916,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
checkProposedApiEnabled(extension);
return extHostNotebook.onDidCloseNotebookDocument;
},
- registerNotebookProvider: (viewType: string, provider: vscode.NotebookProvider) => {
- checkProposedApiEnabled(extension);
- return extHostNotebook.registerNotebookProvider(extension, viewType, provider);
- },
registerNotebookContentProvider: (viewType: string, provider: vscode.NotebookContentProvider) => {
checkProposedApiEnabled(extension);
return extHostNotebook.registerNotebookContentProvider(extension, viewType, provider);
diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts
index c6b3c31999d..316f06d40d8 100644
--- a/src/vs/workbench/api/common/extHost.protocol.ts
+++ b/src/vs/workbench/api/common/extHost.protocol.ts
@@ -51,7 +51,7 @@ import { TunnelDto } from 'vs/workbench/api/common/extHostTunnelService';
import { TunnelOptions } from 'vs/platform/remote/common/tunnel';
import { Timeline, TimelineChangeEvent, TimelineOptions, TimelineProviderDescriptor, InternalTimelineOptions } from 'vs/workbench/contrib/timeline/common/timeline';
import { revive } from 'vs/base/common/marshalling';
-import { INotebookMimeTypeSelector, IOutput, INotebookDisplayOrder, NotebookCellMetadata, NotebookDocumentMetadata, ICellEditOperation, NotebookCellsChangedEvent } from 'vs/workbench/contrib/notebook/common/notebookCommon';
+import { INotebookMimeTypeSelector, IOutput, INotebookDisplayOrder, NotebookCellMetadata, NotebookDocumentMetadata, ICellEditOperation, NotebookCellsChangedEvent, NotebookDataDto } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { CallHierarchyItem } from 'vs/workbench/contrib/callHierarchy/common/callHierarchy';
import { Dto } from 'vs/base/common/types';
import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable';
@@ -160,6 +160,7 @@ export interface MainThreadAuthenticationShape extends IDisposable {
$registerAuthenticationProvider(id: string, displayName: string): void;
$unregisterAuthenticationProvider(id: string): void;
$onDidChangeSessions(providerId: string, event: modes.AuthenticationSessionsChangeEvent): void;
+ $getSession(providerId: string, providerName: string, extensionId: string, extensionName: string, potentialSessions: modes.AuthenticationSession[], scopes: string[], clearSessionPreference: boolean): Promise;
$getSessionsPrompt(providerId: string, accountName: string, providerName: string, extensionId: string, extensionName: string): Promise;
$loginPrompt(providerName: string, extensionName: string): Promise;
$setTrustedExtension(providerId: string, accountName: string, extensionId: string, extensionName: string): Promise;
@@ -691,7 +692,6 @@ export interface MainThreadNotebookShape extends IDisposable {
$unregisterNotebookProvider(viewType: string): Promise;
$registerNotebookRenderer(extension: NotebookExtensionDescription, type: string, selectors: INotebookMimeTypeSelector, handle: number, preloads: UriComponents[]): Promise;
$unregisterNotebookRenderer(handle: number): Promise;
- $createNotebookDocument(handle: number, viewType: string, resource: UriComponents): Promise;
$tryApplyEdits(viewType: string, resource: UriComponents, modelVersionId: number, edits: ICellEditOperation[], renderers: number[]): Promise;
$updateNotebookLanguages(viewType: string, resource: UriComponents, languages: string[]): Promise;
$updateNotebookMetadata(viewType: string, resource: UriComponents, metadata: NotebookDocumentMetadata): Promise;
@@ -1538,16 +1538,31 @@ export interface INotebookEditorPropertiesChangeData {
selections: INotebookSelectionChangeEvent | null;
}
+export interface INotebookModelAddedData {
+ uri: UriComponents;
+ handle: number;
+ // versionId: number;
+ viewType: string;
+}
+
+export interface INotebookDocumentsAndEditorsDelta {
+ removedDocuments?: UriComponents[];
+ addedDocuments?: INotebookModelAddedData[];
+ // removedEditors?: string[];
+ // addedEditors?: ITextEditorAddData[];
+ newActiveEditor?: UriComponents | null;
+}
+
export interface ExtHostNotebookShape {
- $resolveNotebook(viewType: string, uri: UriComponents): Promise;
+ $resolveNotebookData(viewType: string, uri: UriComponents): Promise;
$executeNotebook(viewType: string, uri: UriComponents, cellHandle: number | undefined, token: CancellationToken): Promise;
$saveNotebook(viewType: string, uri: UriComponents, token: CancellationToken): Promise;
- $updateActiveEditor(viewType: string, uri: UriComponents): Promise;
- $destoryNotebookDocument(viewType: string, uri: UriComponents): Promise;
+ $saveNotebookAs(viewType: string, uri: UriComponents, target: UriComponents, token: CancellationToken): Promise;
$acceptDisplayOrder(displayOrder: INotebookDisplayOrder): void;
$onDidReceiveMessage(uri: UriComponents, message: any): void;
$acceptModelChanged(uriComponents: UriComponents, event: NotebookCellsChangedEvent): void;
$acceptEditorPropertiesChanged(uriComponents: UriComponents, data: INotebookEditorPropertiesChangeData): void;
+ $acceptDocumentAndEditorsDelta(delta: INotebookDocumentsAndEditorsDelta): Promise;
}
export interface ExtHostStorageShape {
diff --git a/src/vs/workbench/api/common/extHostAuthentication.ts b/src/vs/workbench/api/common/extHostAuthentication.ts
index 888aefea967..1cab57a73f4 100644
--- a/src/vs/workbench/api/common/extHostAuthentication.ts
+++ b/src/vs/workbench/api/common/extHostAuthentication.ts
@@ -33,6 +33,56 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape {
return ids;
}
+ async hasSessions(providerId: string, scopes: string[]): Promise {
+ const provider = this._authenticationProviders.get(providerId);
+ if (!provider) {
+ throw new Error(`No authentication provider with id '${providerId}' is currently registered.`);
+ }
+
+ const orderedScopes = scopes.sort().join(' ');
+ return !!(await provider.getSessions()).filter(session => session.scopes.sort().join(' ') === orderedScopes).length;
+ }
+
+ async getSession(requestingExtension: IExtensionDescription, providerId: string, scopes: string[], options: vscode.authentication.GetSessionOptions): Promise {
+ const provider = this._authenticationProviders.get(providerId);
+ if (!provider) {
+ throw new Error(`No authentication provider with id '${providerId}' is currently registered.`);
+ }
+
+ const orderedScopes = scopes.sort().join(' ');
+ const sessions = (await provider.getSessions()).filter(session => session.scopes.sort().join(' ') === orderedScopes);
+ if (sessions.length) {
+ const extensionName = requestingExtension.displayName || requestingExtension.name;
+ if (!provider.supportsMultipleAccounts) {
+ const session = sessions[0];
+ const allowed = await this._proxy.$getSessionsPrompt(provider.id, session.account.displayName, provider.displayName, ExtensionIdentifier.toKey(requestingExtension.identifier), extensionName);
+ if (allowed) {
+ return session;
+ } else {
+ throw new Error('User did not consent to login.');
+ }
+ }
+
+ // On renderer side, confirm consent, ask user to choose between accounts if multiple sessions are valid
+ const selected = await this._proxy.$getSession(provider.id, provider.displayName, ExtensionIdentifier.toKey(requestingExtension.identifier), extensionName, sessions, scopes, !!options.clearSessionPreference);
+ return sessions.find(session => session.id === selected.id);
+ } else {
+ if (options.createIfNone) {
+ const extensionName = requestingExtension.displayName || requestingExtension.name;
+ const isAllowed = await this._proxy.$loginPrompt(provider.displayName, extensionName);
+ if (!isAllowed) {
+ throw new Error('User did not consent to login.');
+ }
+
+ const session = await provider.login(scopes);
+ await this._proxy.$setTrustedExtension(provider.id, session.account.displayName, ExtensionIdentifier.toKey(requestingExtension.identifier), extensionName);
+ return session;
+ } else {
+ return undefined;
+ }
+ }
+ }
+
async getSessions(requestingExtension: IExtensionDescription, providerId: string, scopes: string[]): Promise {
const provider = this._authenticationProviders.get(providerId);
if (!provider) {
diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts
index 1b1d7db1f80..66dba087614 100644
--- a/src/vs/workbench/api/common/extHostNotebook.ts
+++ b/src/vs/workbench/api/common/extHostNotebook.ts
@@ -10,10 +10,10 @@ import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecyc
import { ISplice } from 'vs/base/common/sequence';
import { URI, UriComponents } from 'vs/base/common/uri';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
-import { CellKind, CellOutputKind, ExtHostNotebookShape, IMainContext, MainContext, MainThreadNotebookShape, NotebookCellOutputsSplice, MainThreadDocumentsShape, INotebookEditorPropertiesChangeData } from 'vs/workbench/api/common/extHost.protocol';
+import { CellKind, CellOutputKind, ExtHostNotebookShape, IMainContext, MainContext, MainThreadNotebookShape, NotebookCellOutputsSplice, MainThreadDocumentsShape, INotebookEditorPropertiesChangeData, INotebookDocumentsAndEditorsDelta } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
-import { CellEditType, CellUri, diff, ICellEditOperation, ICellInsertEdit, IErrorOutput, INotebookDisplayOrder, INotebookEditData, IOrderedMimeType, IStreamOutput, ITransformedDisplayOutputDto, mimeTypeSupportedByCore, NotebookCellsChangedEvent, NotebookCellsSplice2, sortMimeTypes, ICellDeleteEdit, notebookDocumentMetadataDefaults, NotebookCellsChangeType } from 'vs/workbench/contrib/notebook/common/notebookCommon';
+import { CellEditType, CellUri, diff, ICellEditOperation, ICellInsertEdit, IErrorOutput, INotebookDisplayOrder, INotebookEditData, IOrderedMimeType, IStreamOutput, ITransformedDisplayOutputDto, mimeTypeSupportedByCore, NotebookCellsChangedEvent, NotebookCellsSplice2, sortMimeTypes, ICellDeleteEdit, notebookDocumentMetadataDefaults, NotebookCellsChangeType, NotebookDataDto } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { Disposable as VSCodeDisposable } from './extHostTypes';
import { CancellationToken } from 'vs/base/common/cancellation';
import { ExtHostDocumentData } from 'vs/workbench/api/common/extHostDocumentData';
@@ -345,8 +345,6 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo
transformMimeTypes(output: vscode.CellDisplayOutput): ITransformedDisplayOutputDto {
let mimeTypes = Object.keys(output.data);
-
- // TODO@rebornix, the document display order might be assigned a bit later. We need to postpone sending the outputs to the core side.
let coreDisplayOrder = this.renderingHandler.outputDisplayOrder;
const sorted = sortMimeTypes(mimeTypes, coreDisplayOrder?.userOrder || [], this._displayOrder, coreDisplayOrder?.defaultOrder || []);
@@ -415,7 +413,7 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo
}
}
-export class NotebookEditorCellEdit {
+export class NotebookEditorCellEditBuilder implements vscode.NotebookEditorCellEdit {
private _finalized: boolean = false;
private readonly _documentVersionId: number;
private _collectedEdits: ICellEditOperation[] = [];
@@ -526,13 +524,13 @@ export class ExtHostNotebookEditor extends Disposable implements vscode.Notebook
}));
}
- edit(callback: (editBuilder: NotebookEditorCellEdit) => void): Thenable {
- const edit = new NotebookEditorCellEdit(this);
+ edit(callback: (editBuilder: NotebookEditorCellEditBuilder) => void): Thenable {
+ const edit = new NotebookEditorCellEditBuilder(this);
callback(edit);
return this._applyEdit(edit);
}
- private _applyEdit(editBuilder: NotebookEditorCellEdit): Promise {
+ private _applyEdit(editBuilder: NotebookEditorCellEditBuilder): Promise {
const editData = editBuilder.finalize();
// return when there is nothing to do
@@ -625,7 +623,6 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
private static _handlePool: number = 0;
private readonly _proxy: MainThreadNotebookShape;
- private readonly _notebookProviders = new Map();
private readonly _notebookContentProviders = new Map();
private readonly _documents = new Map();
private readonly _editors = new Map