diff --git a/.vscode/settings.json b/.vscode/settings.json
index e0fd4e2342..d56f75c41d 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -22,6 +22,8 @@
"i18n/**": true,
"extensions/**/out/**": true,
"test/smoke/out/**": true,
+ "test/automation/out/**": true,
+ "test/integration/browser/out/**": true,
"src/vs/base/test/node/uri.test.data.txt": true
},
"lcov.path": [
diff --git a/build/azure-pipelines/darwin/entitlements.plist b/build/azure-pipelines/darwin/entitlements.plist
index 46f6756611..be8b7163da 100644
--- a/build/azure-pipelines/darwin/entitlements.plist
+++ b/build/azure-pipelines/darwin/entitlements.plist
@@ -8,5 +8,7 @@
com.apple.security.cs.disable-library-validation
+ com.apple.security.cs.allow-dyld-environment-variables
+
diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml
index bb75d8570d..6be05972f7 100644
--- a/build/azure-pipelines/darwin/product-build-darwin.yml
+++ b/build/azure-pipelines/darwin/product-build-darwin.yml
@@ -120,17 +120,19 @@ steps:
- script: |
set -e
- VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-darwin" \
- ./resources/server/test/test-web-integration.sh --browser webkit
- displayName: Run integration tests (Browser)
+ APP_ROOT=$(agent.builddirectory)/VSCode-darwin
+ APP_NAME="`ls $APP_ROOT | head -n 1`"
+ INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME/Contents/MacOS/Electron" \
+ VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-darwin" \
+ ./resources/server/test/test-remote-integration.sh
+ displayName: Run remote integration tests (Electron)
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
- script: |
set -e
VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-darwin" \
- yarn smoketest --web --headless --browser webkit
- continueOnError: true
- displayName: Run smoke tests (Browser)
+ ./resources/server/test/test-web-integration.sh --browser webkit
+ displayName: Run integration tests (Browser)
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
- script: |
diff --git a/build/azure-pipelines/darwin/publish.sh b/build/azure-pipelines/darwin/publish.sh
index 58f110c5df..f0375d2a3c 100755
--- a/build/azure-pipelines/darwin/publish.sh
+++ b/build/azure-pipelines/darwin/publish.sh
@@ -19,7 +19,9 @@ node build/azure-pipelines/common/createAsset.js \
../vscode-server-darwin.zip
# publish hockeyapp symbols
-node build/azure-pipelines/common/symbols.js "$VSCODE_MIXIN_PASSWORD" "$VSCODE_HOCKEYAPP_TOKEN" x64 "$VSCODE_HOCKEYAPP_ID_MACOS"
+# 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/product-build-linux.yml b/build/azure-pipelines/linux/product-build-linux.yml
index 7cd8e73039..cbe3bf051e 100644
--- a/build/azure-pipelines/linux/product-build-linux.yml
+++ b/build/azure-pipelines/linux/product-build-linux.yml
@@ -123,20 +123,21 @@ steps:
displayName: Run integration tests (Electron)
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
-# Fails due to weird error: Protocol error (Target.getBrowserContexts): Target closed.
-# - script: |
-# set -e
-# VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-linux-x64" \
-# DISPLAY=:10 ./resources/server/test/test-web-integration.sh --browser chromium
-# displayName: Run integration tests (Browser)
-# condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
+- script: |
+ set -e
+ APP_ROOT=$(agent.builddirectory)/VSCode-linux-x64
+ APP_NAME=$(node -p "require(\"$APP_ROOT/resources/app/product.json\").applicationName")
+ INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME" \
+ VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-linux-x64" \
+ DISPLAY=:10 ./resources/server/test/test-remote-integration.sh
+ displayName: Run remote integration tests (Electron)
+ condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
- script: |
set -e
VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-linux-x64" \
- yarn smoketest --web --headless --browser firefox
- continueOnError: true
- displayName: Run smoke tests (Firefox)
+ DISPLAY=:10 ./resources/server/test/test-web-integration.sh --browser chromium
+ displayName: Run integration tests (Browser)
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
- script: |
diff --git a/build/azure-pipelines/linux/publish.sh b/build/azure-pipelines/linux/publish.sh
index 3da6ea3eed..b168ab9cf7 100755
--- a/build/azure-pipelines/linux/publish.sh
+++ b/build/azure-pipelines/linux/publish.sh
@@ -28,7 +28,9 @@ rm -rf $ROOT/vscode-server-*.tar.*
node build/azure-pipelines/common/createAsset.js "server-$PLATFORM_LINUX" archive-unsigned "$SERVER_TARBALL_FILENAME" "$SERVER_TARBALL_PATH"
# Publish hockeyapp symbols
-node build/azure-pipelines/common/symbols.js "$VSCODE_MIXIN_PASSWORD" "$VSCODE_HOCKEYAPP_TOKEN" "x64" "$VSCODE_HOCKEYAPP_ID_LINUX64"
+# node build/azure-pipelines/common/symbols.js "$VSCODE_MIXIN_PASSWORD" "$VSCODE_HOCKEYAPP_TOKEN" "x64" "$VSCODE_HOCKEYAPP_ID_LINUX64"
+# Skip hockey app because build failure.
+# https://github.com/microsoft/vscode/issues/90491
# Publish DEB
PLATFORM_DEB="linux-deb-x64"
diff --git a/build/azure-pipelines/win32/product-build-win32.yml b/build/azure-pipelines/win32/product-build-win32.yml
index 796259ab98..7a8a12aa28 100644
--- a/build/azure-pipelines/win32/product-build-win32.yml
+++ b/build/azure-pipelines/win32/product-build-win32.yml
@@ -135,16 +135,18 @@ steps:
- powershell: |
. build/azure-pipelines/win32/exec.ps1
$ErrorActionPreference = "Stop"
- exec { $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-web-win32-$(VSCODE_ARCH)"; .\resources\server\test\test-web-integration.bat --browser chromium }
- displayName: Run integration tests (Browser)
+ $AppRoot = "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)"
+ $AppProductJson = Get-Content -Raw -Path "$AppRoot\resources\app\product.json" | ConvertFrom-Json
+ $AppNameShort = $AppProductJson.nameShort
+ exec { $env:INTEGRATION_TEST_ELECTRON_PATH = "$AppRoot\$AppNameShort.exe"; $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-win32-$(VSCODE_ARCH)"; .\resources\server\test\test-remote-integration.bat }
+ displayName: Run remote integration tests (Electron)
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
- powershell: |
. build/azure-pipelines/win32/exec.ps1
$ErrorActionPreference = "Stop"
- exec { $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-web-win32-$(VSCODE_ARCH)"; yarn smoketest --web --headless --browser chromium }
- continueOnError: true
- displayName: Run smoke tests (Browser)
+ exec { $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-web-win32-$(VSCODE_ARCH)"; .\resources\server\test\test-web-integration.bat --browser firefox }
+ displayName: Run integration tests (Browser)
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
- task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1
diff --git a/build/azure-pipelines/win32/publish.ps1 b/build/azure-pipelines/win32/publish.ps1
index 5a22d4749c..c9ff9d09ec 100644
--- a/build/azure-pipelines/win32/publish.ps1
+++ b/build/azure-pipelines/win32/publish.ps1
@@ -11,13 +11,12 @@ $SystemExe = "$Repo\.build\win32-$Arch\system-setup\VSCodeSetup.exe"
$UserExe = "$Repo\.build\win32-$Arch\user-setup\VSCodeSetup.exe"
$Zip = "$Repo\.build\win32-$Arch\archive\VSCode-win32-$Arch.zip"
$LegacyServer = "$Root\vscode-reh-win32-$Arch"
-$ServerName = "vscode-server-win32-$Arch"
-$Server = "$Root\$ServerName"
+$Server = "$Root\vscode-server-win32-$Arch"
$ServerZip = "$Repo\.build\vscode-server-win32-$Arch.zip"
$Build = "$Root\VSCode-win32-$Arch"
# Create server archive
-exec { Rename-Item -Path $LegacyServer -NewName $ServerName }
+exec { xcopy $LegacyServer $Server /H /E /I }
exec { .\node_modules\7zip\7zip-lite\7z.exe a -tzip $ServerZip $Server -r }
# get version
@@ -31,6 +30,8 @@ exec { node build/azure-pipelines/common/createAsset.js "$AssetPlatform" setup "
exec { node build/azure-pipelines/common/createAsset.js "$AssetPlatform-user" setup "VSCodeUserSetup-$Arch-$Version.exe" $UserExe }
exec { node build/azure-pipelines/common/createAsset.js "server-$AssetPlatform" archive "vscode-server-win32-$Arch.zip" $ServerZip }
+# Skip hockey app because build failure.
+# https://github.com/microsoft/vscode/issues/90491
# publish hockeyapp symbols
-$hockeyAppId = if ("$Arch" -eq "ia32") { "$env:VSCODE_HOCKEYAPP_ID_WIN32" } else { "$env:VSCODE_HOCKEYAPP_ID_WIN64" }
-exec { node build/azure-pipelines/common/symbols.js "$env:VSCODE_MIXIN_PASSWORD" "$env:VSCODE_HOCKEYAPP_TOKEN" "$Arch" $hockeyAppId }
+# $hockeyAppId = if ("$Arch" -eq "ia32") { "$env:VSCODE_HOCKEYAPP_ID_WIN32" } else { "$env:VSCODE_HOCKEYAPP_ID_WIN64" }
+# exec { node build/azure-pipelines/common/symbols.js "$env:VSCODE_MIXIN_PASSWORD" "$env:VSCODE_HOCKEYAPP_TOKEN" "$Arch" $hockeyAppId }
diff --git a/build/lib/i18n.resources.json b/build/lib/i18n.resources.json
index 31446bfe4d..fe693b695b 100644
--- a/build/lib/i18n.resources.json
+++ b/build/lib/i18n.resources.json
@@ -110,6 +110,10 @@
"name": "vs/workbench/contrib/output",
"project": "vscode-workbench"
},
+ {
+ "name": "vs/workbench/contrib/openInDesktop",
+ "project": "vscode-workbench"
+ },
{
"name": "vs/workbench/contrib/performance",
"project": "vscode-workbench"
diff --git a/extensions/azurecore/src/apiWrapper.ts b/extensions/azurecore/src/apiWrapper.ts
index 750a43f7f8..303a3e5611 100644
--- a/extensions/azurecore/src/apiWrapper.ts
+++ b/extensions/azurecore/src/apiWrapper.ts
@@ -93,7 +93,7 @@ export class ApiWrapper {
return vscode.workspace.asRelativePath(uri);
}
- public getWorkspaceFolders(): vscode.WorkspaceFolder[] {
+ public getWorkspaceFolders(): readonly vscode.WorkspaceFolder[] {
return vscode.workspace.workspaceFolders;
}
@@ -178,7 +178,7 @@ export class ApiWrapper {
return vscode.window.createStatusBarItem(alignment, priority);
}
- public get workspaceFolders(): vscode.WorkspaceFolder[] {
+ public get workspaceFolders(): readonly vscode.WorkspaceFolder[] {
return vscode.workspace.workspaceFolders;
}
diff --git a/extensions/cms/src/apiWrapper.ts b/extensions/cms/src/apiWrapper.ts
index 9294dd12d8..87a07139bd 100644
--- a/extensions/cms/src/apiWrapper.ts
+++ b/extensions/cms/src/apiWrapper.ts
@@ -65,7 +65,7 @@ export class ApiWrapper {
return vscode.workspace.asRelativePath(uri);
}
- public getWorkspaceFolders(): vscode.WorkspaceFolder[] {
+ public getWorkspaceFolders(): readonly vscode.WorkspaceFolder[] {
return vscode.workspace.workspaceFolders;
}
@@ -144,7 +144,7 @@ export class ApiWrapper {
return vscode.window.createStatusBarItem(alignment, priority);
}
- public get workspaceFolders(): vscode.WorkspaceFolder[] {
+ public get workspaceFolders(): readonly vscode.WorkspaceFolder[] {
return vscode.workspace.workspaceFolders;
}
diff --git a/extensions/configuration-editing/src/settingsDocumentHelper.ts b/extensions/configuration-editing/src/settingsDocumentHelper.ts
index 362799fe39..918b7b2e04 100644
--- a/extensions/configuration-editing/src/settingsDocumentHelper.ts
+++ b/extensions/configuration-editing/src/settingsDocumentHelper.ts
@@ -86,7 +86,7 @@ export class SettingsDocument {
}));
} else {
// Value
- return this.provideLanguageCompletionItems(location, range);
+ return this.provideLanguageCompletionItemsForLanguageOverrides(location, range);
}
}
@@ -158,6 +158,11 @@ export class SettingsDocument {
}
private provideLanguageCompletionItems(_location: Location, range: vscode.Range, formatFunc: (string: string) => string = (l) => JSON.stringify(l)): Thenable {
+ return vscode.languages.getLanguages()
+ .then(languages => languages.map(l => this.newSimpleCompletionItem(formatFunc(l), range)));
+ }
+
+ private provideLanguageCompletionItemsForLanguageOverrides(_location: Location, range: vscode.Range, formatFunc: (string: string) => string = (l) => JSON.stringify(l)): Thenable {
return vscode.languages.getLanguages().then(languages => {
const completionItems = [];
const configuration = vscode.workspace.getConfiguration();
@@ -182,7 +187,7 @@ export class SettingsDocument {
let text = this.document.getText(range);
if (text && text.trim().startsWith('[')) {
range = new vscode.Range(new vscode.Position(range.start.line, range.start.character + text.indexOf('[')), range.end);
- return this.provideLanguageCompletionItems(location, range, language => `"[${language}]"`);
+ return this.provideLanguageCompletionItemsForLanguageOverrides(location, range, language => `"[${language}]"`);
}
range = this.document.getWordRangeAtPosition(position) || new vscode.Range(position, position);
@@ -209,7 +214,7 @@ export class SettingsDocument {
// Suggestion model word matching includes closed sqaure bracket and ending quote
// Hence include them in the proposal to replace
let range = this.document.getWordRangeAtPosition(position) || new vscode.Range(position, position);
- return this.provideLanguageCompletionItems(location, range, language => `"[${language}]"`);
+ return this.provideLanguageCompletionItemsForLanguageOverrides(location, range, language => `"[${language}]"`);
}
return Promise.resolve([]);
}
diff --git a/extensions/mssql/src/apiWrapper.ts b/extensions/mssql/src/apiWrapper.ts
index 08c2d090c0..5193cd1078 100644
--- a/extensions/mssql/src/apiWrapper.ts
+++ b/extensions/mssql/src/apiWrapper.ts
@@ -98,7 +98,7 @@ export class ApiWrapper {
return vscode.window.showTextDocument(document, options);
}
- public get workspaceFolders(): vscode.WorkspaceFolder[] {
+ public get workspaceFolders(): readonly vscode.WorkspaceFolder[] {
return vscode.workspace.workspaceFolders;
}
diff --git a/extensions/notebook/src/extension.ts b/extensions/notebook/src/extension.ts
index cde65112d2..1b5e3f21dd 100644
--- a/extensions/notebook/src/extension.ts
+++ b/extensions/notebook/src/extension.ts
@@ -104,7 +104,7 @@ export async function activate(extensionContext: vscode.ExtensionContext): Promi
return undefined;
}
- let workspaceFolders = vscode.workspace.workspaceFolders || [];
+ let workspaceFolders = vscode.workspace.workspaceFolders?.slice() ?? [];
const bookTreeViewProvider = new BookTreeViewProvider(workspaceFolders, extensionContext, false, BOOKS_VIEWID);
await bookTreeViewProvider.initialized;
const untitledBookTreeViewProvider = new BookTreeViewProvider([], extensionContext, true, READONLY_BOOKS_VIEWID);
diff --git a/extensions/search-result/syntaxes/generateTMLanguage.js b/extensions/search-result/syntaxes/generateTMLanguage.js
index ce2acc34ff..7a9eb9204f 100644
--- a/extensions/search-result/syntaxes/generateTMLanguage.js
+++ b/extensions/search-result/syntaxes/generateTMLanguage.js
@@ -1,6 +1,4 @@
// @ts-check
-// todo@jackson
-/* eslint code-no-unexternalized-strings: 0 */
const mappings = [
['bat', 'source.batchfile'],
@@ -40,6 +38,7 @@ const mappings = [
['perl', 'source.perl'],
['php', 'source.php'],
['pl', 'source.perl'],
+ ['pm', 'source.perl'],
['ps1', 'source.powershell'],
['pug', 'text.pug'],
['py', 'source.python'],
@@ -104,43 +103,43 @@ mappings.forEach(([ext, scope, regexp]) =>
repository[ext] = {
name: scopes.resultBlock.meta,
begin: `^(?!\\s)(.*?)([^\\\\\\/\\n]*${regexp || `\\.${ext}`})(:)$`,
- end: "^(?!\\s)",
+ end: '^(?!\\s)',
beginCaptures: {
- "0": { name: scopes.resultBlock.path.meta },
- "1": { name: scopes.resultBlock.path.dirname },
- "2": { name: scopes.resultBlock.path.basename },
- "3": { name: scopes.resultBlock.path.colon },
+ '0': { name: scopes.resultBlock.path.meta },
+ '1': { name: scopes.resultBlock.path.dirname },
+ '2': { name: scopes.resultBlock.path.basename },
+ '3': { name: scopes.resultBlock.path.colon },
},
patterns: [
{
name: [scopes.resultBlock.result.meta, scopes.resultBlock.result.metaMultiLine].join(' '),
- begin: "^ ((\\d+) )",
- while: "^ (?:((\\d+)(:))|((\\d+) ))",
+ begin: '^ ((\\d+) )',
+ while: '^ (?:((\\d+)(:))|((\\d+) ))',
beginCaptures: {
- "0": { name: scopes.resultBlock.result.prefix.meta },
- "1": { name: scopes.resultBlock.result.prefix.metaContext },
- "2": { name: scopes.resultBlock.result.prefix.lineNumber },
+ '0': { name: scopes.resultBlock.result.prefix.meta },
+ '1': { name: scopes.resultBlock.result.prefix.metaContext },
+ '2': { name: scopes.resultBlock.result.prefix.lineNumber },
},
whileCaptures: {
- "0": { name: scopes.resultBlock.result.prefix.meta },
- "1": { name: scopes.resultBlock.result.prefix.metaMatch },
- "2": { name: scopes.resultBlock.result.prefix.lineNumber },
- "3": { name: scopes.resultBlock.result.prefix.colon },
+ '0': { name: scopes.resultBlock.result.prefix.meta },
+ '1': { name: scopes.resultBlock.result.prefix.metaMatch },
+ '2': { name: scopes.resultBlock.result.prefix.lineNumber },
+ '3': { name: scopes.resultBlock.result.prefix.colon },
- "4": { name: scopes.resultBlock.result.prefix.metaContext },
- "5": { name: scopes.resultBlock.result.prefix.lineNumber },
+ '4': { name: scopes.resultBlock.result.prefix.metaContext },
+ '5': { name: scopes.resultBlock.result.prefix.lineNumber },
},
patterns: [{ include: scope }]
},
{
- begin: "^ ((\\d+)(:))",
- while: "(?=not)possible",
+ begin: '^ ((\\d+)(:))',
+ while: '(?=not)possible',
name: [scopes.resultBlock.result.meta, scopes.resultBlock.result.metaSingleLine].join(' '),
beginCaptures: {
- "0": { name: scopes.resultBlock.result.prefix.meta },
- "1": { name: scopes.resultBlock.result.prefix.metaMatch },
- "2": { name: scopes.resultBlock.result.prefix.lineNumber },
- "3": { name: scopes.resultBlock.result.prefix.colon },
+ '0': { name: scopes.resultBlock.result.prefix.meta },
+ '1': { name: scopes.resultBlock.result.prefix.metaMatch },
+ '2': { name: scopes.resultBlock.result.prefix.lineNumber },
+ '3': { name: scopes.resultBlock.result.prefix.colon },
},
patterns: [{ include: scope }]
}
@@ -149,10 +148,10 @@ mappings.forEach(([ext, scope, regexp]) =>
const header = [
{
- begin: "^(# Query): ",
- end: "\n",
+ begin: '^(# Query): ',
+ end: '\n',
name: scopes.header.meta,
- beginCaptures: { "1": { name: scopes.header.key }, },
+ beginCaptures: { '1': { name: scopes.header.key }, },
patterns: [
{
match: '(\\\\n)|(\\\\\\\\)',
@@ -169,10 +168,10 @@ const header = [
]
},
{
- begin: "^(# Flags): ",
- end: "\n",
+ begin: '^(# Flags): ',
+ end: '\n',
name: scopes.header.meta,
- beginCaptures: { "1": { name: scopes.header.key }, },
+ beginCaptures: { '1': { name: scopes.header.key }, },
patterns: [
{
match: '(RegExp|CaseSensitive|IgnoreExcludeSettings|WordMatch)',
@@ -182,10 +181,10 @@ const header = [
]
},
{
- begin: "^(# ContextLines): ",
- end: "\n",
+ begin: '^(# ContextLines): ',
+ end: '\n',
name: scopes.header.meta,
- beginCaptures: { "1": { name: scopes.header.key }, },
+ beginCaptures: { '1': { name: scopes.header.key }, },
patterns: [
{
match: '\\d',
@@ -195,42 +194,42 @@ const header = [
]
},
{
- match: "^(# (?:Including|Excluding)): (.*)$",
+ match: '^(# (?:Including|Excluding)): (.*)$',
name: scopes.header.meta,
captures: {
- "1": { name: scopes.header.key },
- "2": { name: scopes.header.value }
+ '1': { name: scopes.header.key },
+ '2': { name: scopes.header.value }
}
},
];
const plainText = [
{
- match: "^(?!\\s)(.*?)([^\\\\\\/\\n]*)(:)$",
+ match: '^(?!\\s)(.*?)([^\\\\\\/\\n]*)(:)$',
name: [scopes.resultBlock.meta, scopes.resultBlock.path.meta].join(' '),
captures: {
- "1": { name: scopes.resultBlock.path.dirname },
- "2": { name: scopes.resultBlock.path.basename },
- "3": { name: scopes.resultBlock.path.colon }
+ '1': { name: scopes.resultBlock.path.dirname },
+ '2': { name: scopes.resultBlock.path.basename },
+ '3': { name: scopes.resultBlock.path.colon }
}
},
{
- match: "^ (?:((\\d+)(:))|((\\d+)( ))(.*))",
+ match: '^ (?:((\\d+)(:))|((\\d+)( ))(.*))',
name: [scopes.resultBlock.meta, scopes.resultBlock.result.meta].join(' '),
captures: {
- "1": { name: [scopes.resultBlock.result.prefix.meta, scopes.resultBlock.result.prefix.metaMatch].join(' ') },
- "2": { name: scopes.resultBlock.result.prefix.lineNumber },
- "3": { name: scopes.resultBlock.result.prefix.colon },
+ '1': { name: [scopes.resultBlock.result.prefix.meta, scopes.resultBlock.result.prefix.metaMatch].join(' ') },
+ '2': { name: scopes.resultBlock.result.prefix.lineNumber },
+ '3': { name: scopes.resultBlock.result.prefix.colon },
- "4": { name: [scopes.resultBlock.result.prefix.meta, scopes.resultBlock.result.prefix.metaContext].join(' ') },
- "5": { name: scopes.resultBlock.result.prefix.lineNumber },
+ '4': { name: [scopes.resultBlock.result.prefix.meta, scopes.resultBlock.result.prefix.metaContext].join(' ') },
+ '5': { name: scopes.resultBlock.result.prefix.lineNumber },
}
}
];
const tmLanguage = {
- "information_for_contributors": "This file is generated from ./generateTMLanguage.js.",
- name: "Search Results",
+ 'information_for_contributors': 'This file is generated from ./generateTMLanguage.js.',
+ name: 'Search Results',
scopeName: scopes.root,
patterns: [
...header,
diff --git a/extensions/search-result/syntaxes/searchResult.tmLanguage.json b/extensions/search-result/syntaxes/searchResult.tmLanguage.json
index ea4e3efb15..8edb4a7f89 100644
--- a/extensions/search-result/syntaxes/searchResult.tmLanguage.json
+++ b/extensions/search-result/syntaxes/searchResult.tmLanguage.json
@@ -189,6 +189,9 @@
{
"include": "#pl"
},
+ {
+ "include": "#pm"
+ },
{
"include": "#ps1"
},
@@ -3457,6 +3460,92 @@
}
]
},
+ "pm": {
+ "name": "meta.resultBlock.search",
+ "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.pm)(:)$",
+ "end": "^(?!\\s)",
+ "beginCaptures": {
+ "0": {
+ "name": "string meta.path.search"
+ },
+ "1": {
+ "name": "meta.path.dirname.search"
+ },
+ "2": {
+ "name": "meta.path.basename.search"
+ },
+ "3": {
+ "name": "punctuation.separator"
+ }
+ },
+ "patterns": [
+ {
+ "name": "meta.resultLine.search meta.resultLine.multiLine.search",
+ "begin": "^ ((\\d+) )",
+ "while": "^ (?:((\\d+)(:))|((\\d+) ))",
+ "beginCaptures": {
+ "0": {
+ "name": "constant.numeric.integer meta.resultLinePrefix.search"
+ },
+ "1": {
+ "name": "meta.resultLinePrefix.contextLinePrefix.search"
+ },
+ "2": {
+ "name": "meta.resultLinePrefix.lineNumber.search"
+ }
+ },
+ "whileCaptures": {
+ "0": {
+ "name": "constant.numeric.integer meta.resultLinePrefix.search"
+ },
+ "1": {
+ "name": "meta.resultLinePrefix.matchLinePrefix.search"
+ },
+ "2": {
+ "name": "meta.resultLinePrefix.lineNumber.search"
+ },
+ "3": {
+ "name": "punctuation.separator"
+ },
+ "4": {
+ "name": "meta.resultLinePrefix.contextLinePrefix.search"
+ },
+ "5": {
+ "name": "meta.resultLinePrefix.lineNumber.search"
+ }
+ },
+ "patterns": [
+ {
+ "include": "source.perl"
+ }
+ ]
+ },
+ {
+ "begin": "^ ((\\d+)(:))",
+ "while": "(?=not)possible",
+ "name": "meta.resultLine.search meta.resultLine.singleLine.search",
+ "beginCaptures": {
+ "0": {
+ "name": "constant.numeric.integer meta.resultLinePrefix.search"
+ },
+ "1": {
+ "name": "meta.resultLinePrefix.matchLinePrefix.search"
+ },
+ "2": {
+ "name": "meta.resultLinePrefix.lineNumber.search"
+ },
+ "3": {
+ "name": "punctuation.separator"
+ }
+ },
+ "patterns": [
+ {
+ "include": "source.perl"
+ }
+ ]
+ }
+ ]
+ },
"ps1": {
"name": "meta.resultBlock.search",
"begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.ps1)(:)$",
diff --git a/package.json b/package.json
index bc8919aec3..6671c45317 100644
--- a/package.json
+++ b/package.json
@@ -78,7 +78,7 @@
"vscode-ripgrep": "^1.5.8",
"vscode-sqlite3": "4.0.9",
"vscode-textmate": "4.4.0",
- "xterm": "4.4.0",
+ "xterm": "4.5.0-beta.4",
"xterm-addon-search": "0.5.0",
"xterm-addon-unicode11": "0.1.1",
"xterm-addon-web-links": "0.2.1",
diff --git a/remote/package.json b/remote/package.json
index 14b25d4666..7d3a3755dd 100644
--- a/remote/package.json
+++ b/remote/package.json
@@ -20,7 +20,7 @@
"vscode-proxy-agent": "^0.5.2",
"vscode-ripgrep": "^1.5.8",
"vscode-textmate": "4.4.0",
- "xterm": "4.4.0",
+ "xterm": "4.5.0-beta.4",
"xterm-addon-search": "0.5.0",
"xterm-addon-unicode11": "0.1.1",
"xterm-addon-web-links": "0.2.1",
diff --git a/remote/web/package.json b/remote/web/package.json
index cdb1fd8db2..ffcd7add28 100644
--- a/remote/web/package.json
+++ b/remote/web/package.json
@@ -5,7 +5,7 @@
"onigasm-umd": "2.2.5",
"semver-umd": "^5.5.5",
"vscode-textmate": "4.4.0",
- "xterm": "4.4.0",
+ "xterm": "4.5.0-beta.4",
"xterm-addon-search": "0.5.0",
"xterm-addon-unicode11": "0.1.1",
"xterm-addon-web-links": "0.2.1",
diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock
index 0b1d4fb601..c9f5c9ebb9 100644
--- a/remote/web/yarn.lock
+++ b/remote/web/yarn.lock
@@ -51,7 +51,7 @@ xterm-addon-webgl@0.5.0:
resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.5.0.tgz#c1031dc7599cce3509824643ab5f15361c928e3e"
integrity sha512-hQrvabKCnwXFaEZ+YtoJM9Pm0CIBXL5KSwoU+RiGStU3KYTAcqYP2GsH3dWdvKX6kTWhWLS81dtDsGkfbOciuA==
-xterm@4.4.0:
- version "4.4.0"
- resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.4.0.tgz#5915d3c4c8800fadbcf555a0a603c672ab9df589"
- integrity sha512-JGIpigWM3EBWvnS3rtBuefkiToIILSK1HYMXy4BCsUpO+O4UeeV+/U1AdAXgCB6qJrnPNb7yLgBsVCQUNMteig==
+xterm@4.5.0-beta.4:
+ version "4.5.0-beta.4"
+ resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.5.0-beta.4.tgz#701f05553b643236d3fcd8bb7f14045bd4537c92"
+ integrity sha512-Yv1Bf60LTLBMaig1rv033hPz8hQGXZN6VYW2oe/409t2NbJXPg5xZgf47qyaWFV7a5k1BFiwjayJCWaL2nYBew==
diff --git a/remote/yarn.lock b/remote/yarn.lock
index 3fa3b05348..d576786f98 100644
--- a/remote/yarn.lock
+++ b/remote/yarn.lock
@@ -433,10 +433,10 @@ xterm-addon-webgl@0.5.0:
resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.5.0.tgz#c1031dc7599cce3509824643ab5f15361c928e3e"
integrity sha512-hQrvabKCnwXFaEZ+YtoJM9Pm0CIBXL5KSwoU+RiGStU3KYTAcqYP2GsH3dWdvKX6kTWhWLS81dtDsGkfbOciuA==
-xterm@4.4.0:
- version "4.4.0"
- resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.4.0.tgz#5915d3c4c8800fadbcf555a0a603c672ab9df589"
- integrity sha512-JGIpigWM3EBWvnS3rtBuefkiToIILSK1HYMXy4BCsUpO+O4UeeV+/U1AdAXgCB6qJrnPNb7yLgBsVCQUNMteig==
+xterm@4.5.0-beta.4:
+ version "4.5.0-beta.4"
+ resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.5.0-beta.4.tgz#701f05553b643236d3fcd8bb7f14045bd4537c92"
+ integrity sha512-Yv1Bf60LTLBMaig1rv033hPz8hQGXZN6VYW2oe/409t2NbJXPg5xZgf47qyaWFV7a5k1BFiwjayJCWaL2nYBew==
yauzl@^2.9.2:
version "2.10.0"
diff --git a/scripts/test-integration.bat b/scripts/test-integration.bat
index 14c62def64..f40cc44a96 100755
--- a/scripts/test-integration.bat
+++ b/scripts/test-integration.bat
@@ -67,10 +67,6 @@ if %errorlevel% neq 0 exit /b %errorlevel%
call .\scripts\node-electron.bat .\node_modules\mocha\bin\_mocha .\extensions\*\server\out\test\**\*.test.js
if %errorlevel% neq 0 exit /b %errorlevel%
-if exist ".\resources\server\test\test-remote-integration.bat" (
- call .\resources\server\test\test-remote-integration.bat
-)
-
rmdir /s /q %VSCODEUSERDATADIR%
popd
diff --git a/scripts/test-integration.sh b/scripts/test-integration.sh
index b74c5189a3..a90a4870e4 100755
--- a/scripts/test-integration.sh
+++ b/scripts/test-integration.sh
@@ -51,11 +51,6 @@ fi
"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/azurecore/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/azurecore --extensionTestsPath=$ROOT/extensions/azurecore/out/test --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --skip-getting-started --user-data-dir=$VSCODEUSERDATADIR
-# Remote Integration Tests
-if [ -f ./resources/server/test/test-remote-integration.sh ]; then
- ./resources/server/test/test-remote-integration.sh
-fi
-
# Tests in commonJS
cd $ROOT/extensions/css-language-features/server && $ROOT/scripts/node-electron.sh test/index.js
cd $ROOT/extensions/html-language-features/server && $ROOT/scripts/node-electron.sh test/index.js
diff --git a/src/sql/workbench/api/browser/mainThreadNotebookDocumentsAndEditors.ts b/src/sql/workbench/api/browser/mainThreadNotebookDocumentsAndEditors.ts
index 1f45d47b46..7707680e06 100644
--- a/src/sql/workbench/api/browser/mainThreadNotebookDocumentsAndEditors.ts
+++ b/src/sql/workbench/api/browser/mainThreadNotebookDocumentsAndEditors.ts
@@ -38,6 +38,7 @@ import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/commo
import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput';
import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
+import { UntitledTextEditorModel } from 'vs/workbench/services/untitled/common/untitledTextEditorModel';
class MainThreadNotebookEditor extends Disposable {
private _contentChangedEmitter = new Emitter();
@@ -458,12 +459,17 @@ export class MainThreadNotebookDocumentsAndEditors extends Disposable implements
};
let isUntitled: boolean = uri.scheme === Schemas.untitled;
- let fileInput;
+ let fileInput: UntitledTextEditorInput | FileEditorInput;
if (isUntitled && path.isAbsolute(uri.fsPath)) {
- fileInput = this._untitledEditorService.create({ associatedResource: uri, mode: 'notebook', initialValue: options.initialContent });
+ const model = this._untitledEditorService.create({ associatedResource: uri, mode: 'notebook', initialValue: options.initialContent });
+ fileInput = this._instantiationService.createInstance(UntitledTextEditorInput, model);
} else {
- fileInput = isUntitled ? this._untitledEditorService.create({ untitledResource: uri, mode: 'notebook', initialValue: options.initialContent }) :
- this._editorService.createInput({ resource: uri, mode: 'notebook' });
+ if (isUntitled) {
+ const model = this._untitledEditorService.create({ untitledResource: uri, mode: 'notebook', initialValue: options.initialContent });
+ fileInput = this._instantiationService.createInstance(UntitledTextEditorInput, model);
+ } else {
+ fileInput = this._editorService.createInput({ forceFile: true, resource: uri, mode: 'notebook' }) as FileEditorInput;
+ }
}
let input: NotebookInput;
if (isUntitled) {
@@ -478,7 +484,7 @@ export class MainThreadNotebookDocumentsAndEditors extends Disposable implements
await untitledModel.load();
input.untitledEditorModel = untitledModel;
if (options.initialDirtyState === false) {
- input.untitledEditorModel.setDirty(false);
+ (input.untitledEditorModel as UntitledTextEditorModel).setDirty(false);
}
}
let editor = await this._editorService.openEditor(input, editorOptions, viewColumnToEditorGroup(this._editorGroupService, options.position));
diff --git a/src/sql/workbench/browser/modal/optionsDialog.ts b/src/sql/workbench/browser/modal/optionsDialog.ts
index 52bb2db3af..1a42f4a865 100644
--- a/src/sql/workbench/browser/modal/optionsDialog.ts
+++ b/src/sql/workbench/browser/modal/optionsDialog.ts
@@ -36,6 +36,7 @@ import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/vie
import { attachModalDialogStyler, attachPanelStyler } from 'sql/workbench/common/styler';
import { IViewDescriptorService } from 'vs/workbench/common/views';
import { ServiceOptionType } from 'sql/platform/connection/common/interfaces';
+import { IOpenerService } from 'vs/platform/opener/common/opener';
export class CategoryView extends ViewPane {
@@ -48,9 +49,11 @@ export class CategoryView extends ViewPane {
@IConfigurationService configurationService: IConfigurationService,
@IContextKeyService contextKeyService: IContextKeyService,
@IInstantiationService instantiationService: IInstantiationService,
- @IViewDescriptorService viewDescriptorService: IViewDescriptorService
+ @IViewDescriptorService viewDescriptorService: IViewDescriptorService,
+ @IOpenerService protected openerService: IOpenerService,
+ @IThemeService protected themeService: IThemeService
) {
- super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService);
+ super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, opener, themeService);
}
// we want a fixed size, so when we render to will measure our content and set that to be our
diff --git a/src/sql/workbench/browser/modelComponents/editor.component.ts b/src/sql/workbench/browser/modelComponents/editor.component.ts
index 91f4025304..f6010527f9 100644
--- a/src/sql/workbench/browser/modelComponents/editor.component.ts
+++ b/src/sql/workbench/browser/modelComponents/editor.component.ts
@@ -24,6 +24,7 @@ import { ServiceCollection } from 'vs/platform/instantiation/common/serviceColle
import { IEditorProgressService } from 'vs/platform/progress/common/progress';
import { SimpleProgressIndicator } from 'sql/workbench/services/progress/browser/simpleProgressIndicator';
import { IComponent, IComponentDescriptor, IModelStore, ComponentEventType } from 'sql/platform/dashboard/browser/interfaces';
+import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
@Component({
template: '',
@@ -47,7 +48,8 @@ export default class EditorComponent extends ComponentBase implements IComponent
@Inject(IInstantiationService) private _instantiationService: IInstantiationService,
@Inject(IModelService) private _modelService: IModelService,
@Inject(IModeService) private _modeService: IModeService,
- @Inject(ILogService) private _logService: ILogService
+ @Inject(ILogService) private _logService: ILogService,
+ @Inject(IEditorService) private readonly editorService: IEditorService
) {
super(changeRef, el);
}
@@ -66,7 +68,7 @@ export default class EditorComponent extends ComponentBase implements IComponent
this._editor.create(this._el.nativeElement);
this._editor.setVisible(true);
let uri = this.createUri();
- this._editorInput = this._instantiationService.createInstance(UntitledTextEditorInput, uri, false, 'plaintext', '', '');
+ this._editorInput = this.editorService.createInput({ forceUntitled: true, resource: uri, mode: 'plaintext' }) as UntitledTextEditorInput;
await this._editor.setInput(this._editorInput, undefined);
const model = await this._editorInput.resolve();
this._editorModel = model.textEditorModel;
diff --git a/src/sql/workbench/browser/parts/views/customView.ts b/src/sql/workbench/browser/parts/views/customView.ts
index 8b00dd1c8e..94b3ccbb9f 100644
--- a/src/sql/workbench/browser/parts/views/customView.ts
+++ b/src/sql/workbench/browser/parts/views/customView.ts
@@ -25,7 +25,7 @@ import { ResourceLabels, IResourceLabel } from 'vs/workbench/browser/labels';
import { ActionBar, IActionViewItemProvider, ActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar';
import { URI } from 'vs/base/common/uri';
import { dirname, basename } from 'vs/base/common/resources';
-import { LIGHT, FileThemeIcon, FolderThemeIcon, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
+import { LIGHT, FileThemeIcon, FolderThemeIcon, registerThemingParticipant, IThemeService } from 'vs/platform/theme/common/themeService';
import { FileKind } from 'vs/platform/files/common/files';
import { WorkbenchAsyncDataTree, TreeResourceNavigator } from 'vs/platform/list/browser/listService';
import { localize } from 'vs/nls';
@@ -63,9 +63,11 @@ export class CustomTreeViewPanel extends ViewPane {
@IConfigurationService configurationService: IConfigurationService,
@IContextKeyService contextKeyService: IContextKeyService,
@IInstantiationService instantiationService: IInstantiationService,
- @IViewDescriptorService viewDescriptorService: IViewDescriptorService
+ @IViewDescriptorService viewDescriptorService: IViewDescriptorService,
+ @IOpenerService protected openerService: IOpenerService,
+ @IThemeService protected themeService: IThemeService
) {
- super({ ...(options as IViewPaneOptions), ariaHeaderLabel: options.title }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService);
+ super({ ...(options as IViewPaneOptions), ariaHeaderLabel: options.title }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService);
const { treeView } = (Registry.as(Extensions.ViewsRegistry).getView(options.id));
this.treeView = treeView as ITreeView;
this._register(this.treeView.onDidChangeActions(() => this.updateActions(), this));
diff --git a/src/sql/workbench/contrib/accounts/browser/accountDialog.ts b/src/sql/workbench/contrib/accounts/browser/accountDialog.ts
index 7ebc6d9d3c..0105d0703d 100644
--- a/src/sql/workbench/contrib/accounts/browser/accountDialog.ts
+++ b/src/sql/workbench/contrib/accounts/browser/accountDialog.ts
@@ -38,6 +38,7 @@ import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry';
import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer';
import { attachModalDialogStyler, attachPanelStyler } from 'sql/workbench/common/styler';
import { IViewDescriptorService } from 'vs/workbench/common/views';
+import { IOpenerService } from 'vs/platform/opener/common/opener';
class AccountPanel extends ViewPane {
public index: number;
@@ -48,12 +49,13 @@ class AccountPanel extends ViewPane {
@IKeybindingService keybindingService: IKeybindingService,
@IContextMenuService contextMenuService: IContextMenuService,
@IConfigurationService configurationService: IConfigurationService,
- @IThemeService private themeService: IThemeService,
+ @IThemeService themeService: IThemeService,
@IContextKeyService contextKeyService: IContextKeyService,
@IInstantiationService instantiationService: IInstantiationService,
- @IViewDescriptorService viewDescriptorService: IViewDescriptorService
+ @IViewDescriptorService viewDescriptorService: IViewDescriptorService,
+ @IOpenerService openerService: IOpenerService,
) {
- super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService);
+ super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService);
}
protected renderBody(container: HTMLElement): void {
@@ -130,7 +132,8 @@ export class AccountDialog extends Modal {
@IClipboardService clipboardService: IClipboardService,
@ILogService logService: ILogService,
@IViewDescriptorService private viewDescriptorService: IViewDescriptorService,
- @ITextResourcePropertiesService textResourcePropertiesService: ITextResourcePropertiesService
+ @ITextResourcePropertiesService textResourcePropertiesService: ITextResourcePropertiesService,
+ @IOpenerService protected readonly openerService: IOpenerService
) {
super(
localize('linkedAccounts', "Linked accounts"),
@@ -301,7 +304,8 @@ export class AccountDialog extends Modal {
this._themeService,
this.contextKeyService,
this._instantiationService,
- this.viewDescriptorService
+ this.viewDescriptorService,
+ this.openerService
);
attachPanelStyler(providerView, this._themeService);
diff --git a/src/sql/workbench/contrib/accounts/test/browser/accountDialogController.test.ts b/src/sql/workbench/contrib/accounts/test/browser/accountDialogController.test.ts
index b1cc1abc28..6e33dea445 100644
--- a/src/sql/workbench/contrib/accounts/test/browser/accountDialogController.test.ts
+++ b/src/sql/workbench/contrib/accounts/test/browser/accountDialogController.test.ts
@@ -86,7 +86,7 @@ function createInstantiationService(addAccountFailureEmitter?: Emitter):
.returns(() => undefined);
// Create a mock account dialog
- let accountDialog = new AccountDialog(undefined!, undefined!, instantiationService.object, undefined!, undefined!, undefined!, undefined!, new MockContextKeyService(), undefined!, undefined!, undefined!, undefined!);
+ let accountDialog = new AccountDialog(undefined!, undefined!, instantiationService.object, undefined!, undefined!, undefined!, undefined!, new MockContextKeyService(), undefined!, undefined!, undefined!, undefined!, undefined!);
let mockAccountDialog = TypeMoq.Mock.ofInstance(accountDialog);
mockAccountDialog.setup(x => x.onAddAccountErrorEvent)
.returns(() => { return addAccountFailureEmitter ? addAccountFailureEmitter.event : mockEvent.event; });
diff --git a/src/sql/workbench/contrib/charts/browser/actions.ts b/src/sql/workbench/contrib/charts/browser/actions.ts
index 794e6201a3..c647c4d299 100644
--- a/src/sql/workbench/contrib/charts/browser/actions.ts
+++ b/src/sql/workbench/contrib/charts/browser/actions.ts
@@ -21,6 +21,8 @@ import { VSBuffer } from 'vs/base/common/buffer';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { assign } from 'vs/base/common/objects';
import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService';
+import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
+import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput';
export interface IChartActionContext {
options: IInsightOptions;
@@ -35,7 +37,8 @@ export class CreateInsightAction extends Action {
constructor(
@IEditorService private editorService: IEditorService,
@INotificationService private notificationService: INotificationService,
- @IUntitledTextEditorService private untitledEditorService: IUntitledTextEditorService
+ @IUntitledTextEditorService private untitledEditorService: IUntitledTextEditorService,
+ @IInstantiationService private readonly instantiationService: IInstantiationService
) {
super(CreateInsightAction.ID, CreateInsightAction.LABEL, CreateInsightAction.ICON);
}
@@ -74,7 +77,7 @@ export class CreateInsightAction extends Action {
let input = this.untitledEditorService.create({ mode: 'json', initialValue: JSON.stringify(widgetConfig) });
- return this.editorService.openEditor(input, { pinned: true })
+ return this.editorService.openEditor(this.instantiationService.createInstance(UntitledTextEditorInput, input), { pinned: true })
.then(
() => true,
error => {
diff --git a/src/sql/workbench/contrib/commandLine/test/electron-browser/commandLine.test.ts b/src/sql/workbench/contrib/commandLine/test/electron-browser/commandLine.test.ts
index 105477746c..ae805c819f 100644
--- a/src/sql/workbench/contrib/commandLine/test/electron-browser/commandLine.test.ts
+++ b/src/sql/workbench/contrib/commandLine/test/electron-browser/commandLine.test.ts
@@ -22,19 +22,18 @@ import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ILogService, NullLogService } from 'vs/platform/log/common/log';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
-import { TestEditorService, TestDialogService, TestFileService } from 'vs/workbench/test/browser/workbenchTestServices';
+import { TestEditorService, TestDialogService, workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices';
import { URI } from 'vs/base/common/uri';
import { UntitledQueryEditorInput } from 'sql/workbench/contrib/query/common/untitledQueryEditorInput';
import { TestQueryModelService } from 'sql/workbench/services/query/test/common/testQueryModelService';
import { Event } from 'vs/base/common/event';
import { IQueryModelService } from 'sql/workbench/services/query/common/queryModel';
-import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService';
import { isUndefinedOrNull } from 'vs/base/common/types';
import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput';
-import { LabelService } from 'vs/workbench/services/label/common/labelService';
+import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService';
class TestParsedArgs implements ParsedArgs {
[arg: string]: any;
@@ -391,9 +390,11 @@ suite('commandLineService tests', () => {
const querymodelService = TypeMoq.Mock.ofType(TestQueryModelService, TypeMoq.MockBehavior.Strict);
querymodelService.setup(c => c.onRunQueryStart).returns(() => Event.None);
querymodelService.setup(c => c.onRunQueryComplete).returns(() => Event.None);
- const instantiationService = new TestInstantiationService();
let uri = URI.file(args._[0]);
- const untitledEditorInput = new UntitledTextEditorInput(uri, false, undefined, undefined, undefined, instantiationService, undefined, new LabelService(undefined, undefined), undefined, undefined, new TestFileService(), undefined);
+ const workbenchinstantiationService = workbenchInstantiationService();
+ const accessor = workbenchinstantiationService.createInstance(ServiceAccessor);
+ const service = accessor.untitledTextEditorService;
+ const untitledEditorInput = workbenchinstantiationService.createInstance(UntitledTextEditorInput, service.create({ associatedResource: uri }));
const queryInput = new UntitledQueryEditorInput(undefined, untitledEditorInput, undefined, connectionManagementService.object, querymodelService.object, configurationService.object);
queryInput.state.connected = true;
const editorService: TypeMoq.Mock = TypeMoq.Mock.ofType(TestEditorService, TypeMoq.MockBehavior.Strict);
@@ -567,3 +568,9 @@ suite('commandLineService tests', () => {
});
});
+
+class ServiceAccessor {
+ constructor(
+ @IUntitledTextEditorService public readonly untitledTextEditorService: IUntitledTextEditorService
+ ) { }
+}
diff --git a/src/sql/workbench/contrib/dataExplorer/browser/connectionViewletPanel.ts b/src/sql/workbench/contrib/dataExplorer/browser/connectionViewletPanel.ts
index 4c120582e3..78af40bc1c 100644
--- a/src/sql/workbench/contrib/dataExplorer/browser/connectionViewletPanel.ts
+++ b/src/sql/workbench/contrib/dataExplorer/browser/connectionViewletPanel.ts
@@ -21,6 +21,8 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { ITree } from 'vs/base/parts/tree/browser/tree';
import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer';
import { IViewDescriptorService } from 'vs/workbench/common/views';
+import { IOpenerService } from 'vs/platform/opener/common/opener';
+import { IThemeService } from 'vs/platform/theme/common/themeService';
export class ConnectionViewletPanel extends ViewPane {
@@ -41,8 +43,10 @@ export class ConnectionViewletPanel extends ViewPane {
@IObjectExplorerService private readonly objectExplorerService: IObjectExplorerService,
@IContextKeyService contextKeyService: IContextKeyService,
@IViewDescriptorService viewDescriptorService: IViewDescriptorService,
+ @IOpenerService protected openerService: IOpenerService,
+ @IThemeService protected themeService: IThemeService
) {
- super({ ...(options as IViewPaneOptions), ariaHeaderLabel: options.title }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService);
+ super({ ...(options as IViewPaneOptions), ariaHeaderLabel: options.title }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, opener, themeService);
this._addServerAction = this.instantiationService.createInstance(AddServerAction,
AddServerAction.ID,
AddServerAction.LABEL);
diff --git a/src/sql/workbench/contrib/editData/browser/editDataInput.ts b/src/sql/workbench/contrib/editData/browser/editDataInput.ts
index 12a30edb45..8577805ea9 100644
--- a/src/sql/workbench/contrib/editData/browser/editDataInput.ts
+++ b/src/sql/workbench/contrib/editData/browser/editDataInput.ts
@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import { EditorInput, EditorModel, EncodingMode, IEditorInput } from 'vs/workbench/common/editor';
+import { EditorInput, EncodingMode, IEditorInput } from 'vs/workbench/common/editor';
import { IConnectionManagementService, IConnectableInput, INewConnectionParams } from 'sql/platform/connection/common/connectionManagement';
import { IQueryModelService } from 'sql/workbench/services/query/common/queryModel';
import { Event, Emitter } from 'vs/base/common/event';
@@ -15,6 +15,8 @@ import Severity from 'vs/base/common/severity';
import { EditDataResultsInput } from 'sql/workbench/contrib/editData/browser/editDataResultsInput';
import { IEditorViewState } from 'vs/editor/common/editorCommon';
import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput';
+import { IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService';
+import { IUntitledTextEditorModel, UntitledTextEditorModel } from 'vs/workbench/services/untitled/common/untitledTextEditorModel';
/**
* Input for the EditDataEditor.
@@ -61,7 +63,7 @@ export class EditDataInput extends EditorInput implements IConnectableInput {
// also set dirty status to false to prevent rerendering.
if (this._sql) {
this._register(this._sql.onDidChangeDirty(async () => {
- const model = await this._sql.resolve();
+ const model = await this._sql.resolve() as UntitledTextEditorModel;
model.setDirty(false);
this._onDidChangeDirty.fire();
}));
@@ -222,11 +224,10 @@ export class EditDataInput extends EditorInput implements IConnectableInput {
return this._connectionManagementService.getTabColorForUri(this.uri);
}
- public get onDidModelChangeEncoding(): Event { return this._sql.onDidModelChangeEncoding; }
- public resolve(refresh?: boolean): Promise { return this._sql.resolve(); }
+ public resolve(refresh?: boolean): Promise { return this._sql.resolve(); }
public getEncoding(): string { return this._sql.getEncoding(); }
public getName(): string { return this._sql.getName(); }
- public get hasAssociatedFilePath(): boolean { return this._sql.hasAssociatedFilePath; }
+ public get hasAssociatedFilePath(): boolean { return this._sql.model.hasAssociatedFilePath; }
public setEncoding(encoding: string, mode: EncodingMode /* ignored, we only have Encode */): void {
this._sql.setEncoding(encoding, mode);
diff --git a/src/sql/workbench/contrib/editorReplacement/test/common/editorReplacerContribution.test.ts b/src/sql/workbench/contrib/editorReplacement/test/common/editorReplacerContribution.test.ts
index 823a655ee4..1b9c8bc52e 100644
--- a/src/sql/workbench/contrib/editorReplacement/test/common/editorReplacerContribution.test.ts
+++ b/src/sql/workbench/contrib/editorReplacement/test/common/editorReplacerContribution.test.ts
@@ -28,6 +28,7 @@ import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/u
import { UntitledQueryEditorInput } from 'sql/workbench/contrib/query/common/untitledQueryEditorInput';
import { INotebookService } from 'sql/workbench/services/notebook/browser/notebookService';
import { NotebookServiceStub } from 'sql/workbench/contrib/notebook/test/stubs';
+import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService';
const languageAssociations = Registry.as(LanguageAssociationExtensions.LanguageAssociations);
@@ -122,7 +123,10 @@ suite('Editor Replacer Contribution', () => {
const instantiationService = workbenchInstantiationService();
instantiationService.stub(IEditorService, editorService);
const contrib = instantiationService.createInstance(EditorReplacementContribution);
- const input = instantiationService.createInstance(UntitledTextEditorInput, URI.file('/test/file'), false, undefined, undefined, undefined);
+ const accessor = instantiationService.createInstance(ServiceAccessor);
+ const service = accessor.untitledTextEditorService;
+
+ const input = instantiationService.createInstance(UntitledTextEditorInput, service.create());
const response = editorService.fireOpenEditor(input, undefined, undefined as IEditorGroup);
assert(response?.override);
const newinput = (await response.override) as EditorInput; // our test service returns this so we are fine to cast this
@@ -137,7 +141,9 @@ suite('Editor Replacer Contribution', () => {
const instantiationService = workbenchInstantiationService();
instantiationService.stub(IEditorService, editorService);
const contrib = instantiationService.createInstance(EditorReplacementContribution);
- const untitled = instantiationService.createInstance(UntitledTextEditorInput, URI.file('/test/file'), false, undefined, undefined, undefined);
+ const accessor = instantiationService.createInstance(ServiceAccessor);
+ const service = accessor.untitledTextEditorService;
+ const untitled = instantiationService.createInstance(UntitledTextEditorInput, service.create());
const input = instantiationService.createInstance(UntitledQueryEditorInput, '', untitled, undefined);
const response = editorService.fireOpenEditor(input, undefined, undefined as IEditorGroup);
assert(response === undefined);
@@ -150,7 +156,9 @@ suite('Editor Replacer Contribution', () => {
const instantiationService = workbenchInstantiationService();
instantiationService.stub(IEditorService, editorService);
const contrib = instantiationService.createInstance(EditorReplacementContribution);
- const input = instantiationService.createInstance(UntitledTextEditorInput, URI.file('/test/file.unknown'), false, undefined, undefined, undefined);
+ const accessor = instantiationService.createInstance(ServiceAccessor);
+ const service = accessor.untitledTextEditorService;
+ const input = instantiationService.createInstance(UntitledTextEditorInput, service.create());
const response = editorService.fireOpenEditor(input, undefined, undefined as IEditorGroup);
assert(response === undefined);
@@ -254,3 +262,9 @@ class TestModeService implements IModeService {
throw new Error('Method not implemented.');
}
}
+
+class ServiceAccessor {
+ constructor(
+ @IUntitledTextEditorService public readonly untitledTextEditorService: IUntitledTextEditorService
+ ) { }
+}
diff --git a/src/sql/workbench/contrib/notebook/browser/cellViews/code.component.ts b/src/sql/workbench/contrib/notebook/browser/cellViews/code.component.ts
index 1bc8527fba..9cd45a590e 100644
--- a/src/sql/workbench/contrib/notebook/browser/cellViews/code.component.ts
+++ b/src/sql/workbench/contrib/notebook/browser/cellViews/code.component.ts
@@ -210,11 +210,12 @@ export class CodeComponent extends CellView implements OnInit, OnChanges {
let uri = this.cellModel.cellUri;
let cellModelSource: string;
cellModelSource = Array.isArray(this.cellModel.source) ? this.cellModel.source.join('') : this.cellModel.source;
- this._editorInput = this._instantiationService.createInstance(UntitledTextEditorInput, uri, false, this.cellModel.language, cellModelSource, '');
+ const model = this._instantiationService.createInstance(UntitledTextEditorModel, uri, false, cellModelSource, this.cellModel.language, undefined);
+ this._editorInput = this._instantiationService.createInstance(UntitledTextEditorInput, model);
await this._editor.setInput(this._editorInput, undefined);
this.setFocusAndScroll();
- let untitledEditorModel: UntitledTextEditorModel = await this._editorInput.resolve();
+ let untitledEditorModel = await this._editorInput.resolve() as UntitledTextEditorModel;
this._editorModel = untitledEditorModel.textEditorModel;
let isActive = this.cellModel.id === this._activeCellId;
diff --git a/src/sql/workbench/contrib/notebook/browser/models/notebookInput.ts b/src/sql/workbench/contrib/notebook/browser/models/notebookInput.ts
index b6f9bdb16c..d40de963ee 100644
--- a/src/sql/workbench/contrib/notebook/browser/models/notebookInput.ts
+++ b/src/sql/workbench/contrib/notebook/browser/models/notebookInput.ts
@@ -16,7 +16,7 @@ import { ITextModelService } from 'vs/editor/common/services/resolverService';
import { INotebookModel, IContentManager, NotebookContentChange } from 'sql/workbench/contrib/notebook/browser/models/modelInterfaces';
import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel';
import { Schemas } from 'vs/base/common/network';
-import { ITextFileSaveOptions } from 'vs/workbench/services/textfile/common/textfiles';
+import { ITextFileSaveOptions, ITextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles';
import { LocalContentManager } from 'sql/workbench/services/notebook/common/localContentManager';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
@@ -26,7 +26,7 @@ import { Deferred } from 'sql/base/common/promise';
import { NotebookTextFileModel } from 'sql/workbench/contrib/notebook/browser/models/notebookTextFileModel';
import { ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfigurationService';
import { ResourceEditorModel } from 'vs/workbench/common/editor/resourceEditorModel';
-import { UntitledTextEditorModel } from 'vs/workbench/services/untitled/common/untitledTextEditorModel';
+import { UntitledTextEditorModel, IUntitledTextEditorModel } from 'vs/workbench/services/untitled/common/untitledTextEditorModel';
import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput';
import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput';
import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput';
@@ -42,7 +42,7 @@ export class NotebookEditorModel extends EditorModel {
private readonly _onDidChangeDirty: Emitter = this._register(new Emitter());
private _lastEditFullReplacement: boolean;
constructor(public readonly notebookUri: URI,
- private textEditorModel: TextFileEditorModel | UntitledTextEditorModel | ResourceEditorModel,
+ private textEditorModel: ITextFileEditorModel | IUntitledTextEditorModel | ResourceEditorModel,
@INotebookService private notebookService: INotebookService,
@ITextResourcePropertiesService private textResourcePropertiesService: ITextResourcePropertiesService
) {
@@ -198,7 +198,7 @@ export abstract class NotebookInput extends EditorInput {
private _parentContainer: HTMLElement;
private readonly _layoutChanged: Emitter = this._register(new Emitter());
private _model: NotebookEditorModel;
- private _untitledEditorModel: UntitledTextEditorModel;
+ private _untitledEditorModel: IUntitledTextEditorModel;
private _contentManager: IContentManager;
private _providersLoaded: Promise;
private _dirtyListener: IDisposable;
@@ -328,11 +328,11 @@ export abstract class NotebookInput extends EditorInput {
return this.resource;
}
- public get untitledEditorModel(): UntitledTextEditorModel {
+ public get untitledEditorModel(): IUntitledTextEditorModel {
return this._untitledEditorModel;
}
- public set untitledEditorModel(value: UntitledTextEditorModel) {
+ public set untitledEditorModel(value: IUntitledTextEditorModel) {
this._untitledEditorModel = value;
}
@@ -346,7 +346,7 @@ export abstract class NotebookInput extends EditorInput {
if (this._model) {
return Promise.resolve(this._model);
} else {
- let textOrUntitledEditorModel: TextFileEditorModel | UntitledTextEditorModel | ResourceEditorModel;
+ let textOrUntitledEditorModel: ITextFileEditorModel | IUntitledTextEditorModel | ResourceEditorModel;
if (this.resource.scheme === Schemas.untitled) {
if (this._untitledEditorModel) {
this._untitledEditorModel.textEditorModel.onBeforeAttached();
diff --git a/src/sql/workbench/contrib/notebook/browser/models/notebookTextFileModel.ts b/src/sql/workbench/contrib/notebook/browser/models/notebookTextFileModel.ts
index 6b9b9c024e..a0cf75d853 100644
--- a/src/sql/workbench/contrib/notebook/browser/models/notebookTextFileModel.ts
+++ b/src/sql/workbench/contrib/notebook/browser/models/notebookTextFileModel.ts
@@ -7,8 +7,8 @@ import { Range, IRange } from 'vs/editor/common/core/range';
import { FindMatch } from 'vs/editor/common/model';
import { NotebookContentChange, INotebookModel } from 'sql/workbench/contrib/notebook/browser/models/modelInterfaces';
import { NotebookChangeType } from 'sql/workbench/services/notebook/common/contracts';
-import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel';
import { repeat } from 'vs/base/common/strings';
+import { ITextEditorModel } from 'vs/workbench/common/editor';
export class NotebookTextFileModel {
// save active cell's line/column in editor model for the beginning of the source property
@@ -33,7 +33,7 @@ export class NotebookTextFileModel {
}
}
- public transformAndApplyEditForSourceUpdate(contentChange: NotebookContentChange, textEditorModel: BaseTextEditorModel): boolean {
+ public transformAndApplyEditForSourceUpdate(contentChange: NotebookContentChange, textEditorModel: ITextEditorModel): boolean {
let cellGuidRange = this.getCellNodeByGuid(textEditorModel, contentChange.cells[0].cellGuid);
// convert the range to leverage offsets in the json
@@ -109,7 +109,7 @@ export class NotebookTextFileModel {
return false;
}
- public transformAndApplyEditForOutputUpdate(contentChange: NotebookContentChange, textEditorModel: BaseTextEditorModel): boolean {
+ public transformAndApplyEditForOutputUpdate(contentChange: NotebookContentChange, textEditorModel: ITextEditorModel): boolean {
if (Array.isArray(contentChange.cells[0].outputs) && contentChange.cells[0].outputs.length > 0) {
let newOutput = JSON.stringify(contentChange.cells[0].outputs[contentChange.cells[0].outputs.length - 1], undefined, ' ');
if (contentChange.cells[0].outputs.length > 1) {
@@ -136,7 +136,7 @@ export class NotebookTextFileModel {
return false;
}
- public transformAndApplyEditForCellUpdated(contentChange: NotebookContentChange, textEditorModel: BaseTextEditorModel): boolean {
+ public transformAndApplyEditForCellUpdated(contentChange: NotebookContentChange, textEditorModel: ITextEditorModel): boolean {
let executionCountMatch = this.getExecutionCountRange(textEditorModel, contentChange.cells[0].cellGuid);
if (executionCountMatch && executionCountMatch.range) {
// Execution count can be between 0 and n characters long
@@ -161,7 +161,7 @@ export class NotebookTextFileModel {
return true;
}
- public transformAndApplyEditForClearOutput(contentChange: NotebookContentChange, textEditorModel: BaseTextEditorModel): boolean {
+ public transformAndApplyEditForClearOutput(contentChange: NotebookContentChange, textEditorModel: ITextEditorModel): boolean {
if (!textEditorModel || !contentChange || !contentChange.cells || !contentChange.cells[0] || !contentChange.cells[0].cellGuid) {
return false;
}
@@ -178,7 +178,7 @@ export class NotebookTextFileModel {
return false;
}
- public replaceEntireTextEditorModel(notebookModel: INotebookModel, type: NotebookChangeType, textEditorModel: BaseTextEditorModel) {
+ public replaceEntireTextEditorModel(notebookModel: INotebookModel, type: NotebookChangeType, textEditorModel: ITextEditorModel) {
let content = JSON.stringify(notebookModel.toJSON(type), undefined, ' ');
let model = textEditorModel.textEditorModel;
let endLine = model.getLineCount();
@@ -190,7 +190,7 @@ export class NotebookTextFileModel {
}
// Find the beginning of a cell's source in the text editor model
- private updateSourceBeginRange(textEditorModel: BaseTextEditorModel, cellGuid: string): void {
+ private updateSourceBeginRange(textEditorModel: ITextEditorModel, cellGuid: string): void {
if (!cellGuid) {
return;
}
@@ -210,7 +210,7 @@ export class NotebookTextFileModel {
}
// Find the beginning of a cell's outputs in the text editor model
- private updateOutputBeginRange(textEditorModel: BaseTextEditorModel, cellGuid: string): void {
+ private updateOutputBeginRange(textEditorModel: ITextEditorModel, cellGuid: string): void {
if (!cellGuid) {
return undefined;
}
@@ -230,7 +230,7 @@ export class NotebookTextFileModel {
// Find the end of a cell's outputs in the text editor model
// This will be used as a starting point for any future outputs
- private getEndOfOutputs(textEditorModel: BaseTextEditorModel, cellGuid: string) {
+ private getEndOfOutputs(textEditorModel: ITextEditorModel, cellGuid: string) {
let outputsBegin;
if (this._activeCellGuid === cellGuid) {
outputsBegin = this._outputBeginRange;
@@ -272,7 +272,7 @@ export class NotebookTextFileModel {
}
// Determine what text needs to be replaced when execution counts are updated
- private getExecutionCountRange(textEditorModel: BaseTextEditorModel, cellGuid: string) {
+ private getExecutionCountRange(textEditorModel: ITextEditorModel, cellGuid: string) {
let endOutputRange = this.getEndOfOutputs(textEditorModel, cellGuid);
if (endOutputRange && endOutputRange.endLineNumber) {
return textEditorModel.textEditorModel.findNextMatch('"execution_count": ', { lineNumber: endOutputRange.endLineNumber, column: endOutputRange.endColumn }, false, true, undefined, true);
@@ -282,14 +282,14 @@ export class NotebookTextFileModel {
// Find a cell's location, given its cellGuid
// If it doesn't exist (e.g. it's not the active cell), attempt to find it
- private getCellNodeByGuid(textEditorModel: BaseTextEditorModel, guid: string) {
+ private getCellNodeByGuid(textEditorModel: ITextEditorModel, guid: string) {
if (this._activeCellGuid !== guid || !this._sourceBeginRange) {
this.updateSourceBeginRange(textEditorModel, guid);
}
return this._sourceBeginRange;
}
- private getOutputNodeByGuid(textEditorModel: BaseTextEditorModel, guid: string) {
+ private getOutputNodeByGuid(textEditorModel: ITextEditorModel, guid: string) {
if (this._activeCellGuid !== guid) {
this.updateOutputBeginRange(textEditorModel, guid);
}
@@ -302,7 +302,7 @@ function areRangePropertiesPopulated(range: Range) {
return range && range.startLineNumber !== 0 && range.startColumn !== 0 && range.endLineNumber !== 0 && range.endColumn !== 0;
}
-function findOrSetCellGuidMatch(textEditorModel: BaseTextEditorModel, cellGuid: string): FindMatch[] {
+function findOrSetCellGuidMatch(textEditorModel: ITextEditorModel, cellGuid: string): FindMatch[] {
if (!textEditorModel || !cellGuid) {
return undefined;
}
diff --git a/src/sql/workbench/contrib/notebook/test/browser/notebookInput.test.ts b/src/sql/workbench/contrib/notebook/test/browser/notebookInput.test.ts
index 10516a86fe..61f993d194 100644
--- a/src/sql/workbench/contrib/notebook/test/browser/notebookInput.test.ts
+++ b/src/sql/workbench/contrib/notebook/test/browser/notebookInput.test.ts
@@ -6,7 +6,7 @@
import * as TypeMoq from 'typemoq';
import * as assert from 'assert';
import { nb } from 'azdata';
-import { workbenchInstantiationService, TestFileService } from 'vs/workbench/test/browser/workbenchTestServices';
+import { workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices';
import { Schemas } from 'vs/base/common/network';
import { URI } from 'vs/base/common/uri';
import { UntitledNotebookInput } from 'sql/workbench/contrib/notebook/common/models/untitledNotebookInput';
@@ -19,7 +19,7 @@ import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/u
import { IExtensionService, NullExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { INotebookService, IProviderInfo } from 'sql/workbench/services/notebook/browser/notebookService';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
-import { LabelService } from 'vs/workbench/services/label/common/labelService';
+import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService';
suite('Notebook Input', function (): void {
const instantiationService = workbenchInstantiationService();
@@ -48,7 +48,9 @@ suite('Notebook Input', function (): void {
let untitledNotebookInput: UntitledNotebookInput;
setup(() => {
- untitledTextInput = new UntitledTextEditorInput(untitledUri, false, undefined, undefined, undefined, instantiationService, undefined, new LabelService(undefined, undefined), undefined, undefined, new TestFileService(), undefined);
+ const accessor = instantiationService.createInstance(ServiceAccessor);
+ const service = accessor.untitledTextEditorService;
+ untitledTextInput = instantiationService.createInstance(UntitledTextEditorInput, service.create({ associatedResource: untitledUri }));
untitledNotebookInput = new UntitledNotebookInput(
testTitle, untitledUri, untitledTextInput,
undefined, instantiationService, mockNotebookService.object, mockExtensionService.object);
@@ -169,9 +171,17 @@ suite('Notebook Input', function (): void {
assert.ok(untitledNotebookInput.matches(untitledNotebookInput), 'Input should match itself.');
let otherTestUri = URI.from({ scheme: Schemas.untitled, path: 'OtherTestPath' });
- let otherTextInput = new UntitledTextEditorInput(otherTestUri, false, undefined, undefined, undefined, instantiationService, undefined, new LabelService(undefined, undefined), undefined, undefined, new TestFileService(), undefined);
+ const accessor = instantiationService.createInstance(ServiceAccessor);
+ const service = accessor.untitledTextEditorService;
+ let otherTextInput = instantiationService.createInstance(UntitledTextEditorInput, service.create({ associatedResource: otherTestUri }));
let otherInput = instantiationService.createInstance(UntitledNotebookInput, 'OtherTestInput', otherTestUri, otherTextInput);
assert.strictEqual(untitledNotebookInput.matches(otherInput), false, 'Input should not match different input.');
});
});
+
+class ServiceAccessor {
+ constructor(
+ @IUntitledTextEditorService public readonly untitledTextEditorService: IUntitledTextEditorService
+ ) { }
+}
diff --git a/src/sql/workbench/contrib/profiler/browser/profilerEditor.ts b/src/sql/workbench/contrib/profiler/browser/profilerEditor.ts
index 9b3bb61254..9f252981c9 100644
--- a/src/sql/workbench/contrib/profiler/browser/profilerEditor.ts
+++ b/src/sql/workbench/contrib/profiler/browser/profilerEditor.ts
@@ -53,6 +53,7 @@ import { ITextResourcePropertiesService } from 'vs/editor/common/services/textRe
import { find } from 'vs/base/common/arrays';
import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput';
import { attachTabbedPanelStyler } from 'sql/workbench/common/styler';
+import { UntitledTextEditorModel } from 'vs/workbench/services/untitled/common/untitledTextEditorModel';
class BasicView implements IView {
public get element(): HTMLElement {
@@ -433,7 +434,8 @@ export class ProfilerEditor extends BaseEditor {
editorContainer.className = 'profiler-editor';
this._editor.create(editorContainer);
this._editor.setVisible(true);
- this._editorInput = this._instantiationService.createInstance(UntitledTextEditorInput, URI.from({ scheme: Schemas.untitled }), false, 'sql', '', '');
+ const model = this._instantiationService.createInstance(UntitledTextEditorModel, URI.from({ scheme: Schemas.untitled }), false, undefined, 'sql', undefined);
+ this._editorInput = this._instantiationService.createInstance(UntitledTextEditorInput, model);
this._editor.setInput(this._editorInput, undefined);
this._editorInput.resolve().then(model => this._editorModel = model.textEditorModel);
return editorContainer;
diff --git a/src/sql/workbench/contrib/query/browser/gridPanel.ts b/src/sql/workbench/contrib/query/browser/gridPanel.ts
index f1d5dfb127..d14f49af22 100644
--- a/src/sql/workbench/contrib/query/browser/gridPanel.ts
+++ b/src/sql/workbench/contrib/query/browser/gridPanel.ts
@@ -580,8 +580,7 @@ export abstract class GridTableBase extends Disposable implements IView {
let content = value.displayValue;
const input = this.untitledEditorService.create({ mode: column.isXml ? 'xml' : 'json', initialValue: content });
- const model = await input.resolve();
- await this.instantiationService.invokeFunction(formatDocumentWithSelectedProvider, model.textEditorModel, FormattingMode.Explicit, CancellationToken.None);
+ await this.instantiationService.invokeFunction(formatDocumentWithSelectedProvider, input.textEditorModel, FormattingMode.Explicit, CancellationToken.None);
return this.editorService.openEditor(input);
});
}
diff --git a/src/sql/workbench/contrib/query/common/untitledQueryEditorInput.ts b/src/sql/workbench/contrib/query/common/untitledQueryEditorInput.ts
index 8bf22aae15..31e3612bf1 100644
--- a/src/sql/workbench/contrib/query/common/untitledQueryEditorInput.ts
+++ b/src/sql/workbench/contrib/query/common/untitledQueryEditorInput.ts
@@ -12,14 +12,12 @@ import { IEncodingSupport, EncodingMode } from 'vs/workbench/common/editor';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService';
import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput';
-import { UntitledTextEditorModel } from 'vs/workbench/services/untitled/common/untitledTextEditorModel';
+import { IUntitledTextEditorModel } from 'vs/workbench/services/untitled/common/untitledTextEditorModel';
export class UntitledQueryEditorInput extends QueryEditorInput implements IEncodingSupport {
public static readonly ID = 'workbench.editorInput.untitledQueryInput';
- public readonly onDidModelChangeEncoding = this.text.onDidModelChangeEncoding;
-
constructor(
description: string,
text: UntitledTextEditorInput,
@@ -31,7 +29,7 @@ export class UntitledQueryEditorInput extends QueryEditorInput implements IEncod
super(description, text, results, connectionManagementService, queryModelService, configurationService);
}
- public resolve(): Promise {
+ public resolve(): Promise {
return this.text.resolve();
}
@@ -40,7 +38,7 @@ export class UntitledQueryEditorInput extends QueryEditorInput implements IEncod
}
public get hasAssociatedFilePath(): boolean {
- return this.text.hasAssociatedFilePath;
+ return this.text.model.hasAssociatedFilePath;
}
public setMode(mode: string): void {
diff --git a/src/sql/workbench/contrib/query/test/browser/queryActions.test.ts b/src/sql/workbench/contrib/query/test/browser/queryActions.test.ts
index 11746b3dd7..a6e2ded637 100644
--- a/src/sql/workbench/contrib/query/test/browser/queryActions.test.ts
+++ b/src/sql/workbench/contrib/query/test/browser/queryActions.test.ts
@@ -23,17 +23,16 @@ import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import * as TypeMoq from 'typemoq';
import * as assert from 'assert';
-import { TestStorageService, TestFileService } from 'vs/workbench/test/browser/workbenchTestServices';
+import { TestStorageService, TestFileService, workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices';
import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService';
import { UntitledQueryEditorInput } from 'sql/workbench/contrib/query/common/untitledQueryEditorInput';
import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService';
import { TestQueryModelService } from 'sql/workbench/services/query/test/common/testQueryModelService';
import { URI } from 'vs/base/common/uri';
-import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { TestConnectionManagementService } from 'sql/platform/connection/test/common/testConnectionManagementService';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput';
-import { LabelService } from 'vs/workbench/services/label/common/labelService';
+import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService';
suite('SQL QueryAction Tests', () => {
@@ -69,8 +68,10 @@ suite('SQL QueryAction Tests', () => {
queryModelService.setup(q => q.onRunQueryComplete).returns(() => Event.None);
connectionManagementService = TypeMoq.Mock.ofType(TestConnectionManagementService);
connectionManagementService.setup(q => q.onDisconnect).returns(() => Event.None);
- const instantiationService = new TestInstantiationService();
- let fileInput = new UntitledTextEditorInput(URI.parse('file://testUri'), false, undefined, undefined, undefined, instantiationService, undefined, new LabelService(undefined, undefined), undefined, undefined, new TestFileService(), undefined);
+ const workbenchinstantiationService = workbenchInstantiationService();
+ const accessor = workbenchinstantiationService.createInstance(ServiceAccessor);
+ const service = accessor.untitledTextEditorService;
+ let fileInput = workbenchinstantiationService.createInstance(UntitledTextEditorInput, service.create({ associatedResource: URI.parse('file://testUri') }));
// Setup a reusable mock QueryInput
testQueryInput = TypeMoq.Mock.ofType(UntitledQueryEditorInput, TypeMoq.MockBehavior.Strict, undefined, fileInput, undefined, connectionManagementService.object, queryModelService.object, configurationService.object);
testQueryInput.setup(x => x.uri).returns(() => testUri);
@@ -174,8 +175,10 @@ suite('SQL QueryAction Tests', () => {
let queryModelService = TypeMoq.Mock.ofType(QueryModelService, TypeMoq.MockBehavior.Loose);
queryModelService.setup(x => x.onRunQueryStart).returns(() => Event.None);
queryModelService.setup(x => x.onRunQueryComplete).returns(() => Event.None);
- const instantiationService = new TestInstantiationService();
- let fileInput = new UntitledTextEditorInput(URI.parse('file://testUri'), false, undefined, undefined, undefined, instantiationService, undefined, new LabelService(undefined, undefined), undefined, undefined, new TestFileService(), undefined);
+ const workbenchinstantiationService = workbenchInstantiationService();
+ const accessor = workbenchinstantiationService.createInstance(ServiceAccessor);
+ const service = accessor.untitledTextEditorService;
+ let fileInput = workbenchinstantiationService.createInstance(UntitledTextEditorInput, service.create({ associatedResource: URI.parse('file://testUri') }));
// ... Mock "isSelectionEmpty" in QueryEditor
let queryInput = TypeMoq.Mock.ofType(UntitledQueryEditorInput, TypeMoq.MockBehavior.Strict, undefined, fileInput, undefined, connectionManagementService.object, queryModelService.object, configurationService.object);
@@ -223,8 +226,10 @@ suite('SQL QueryAction Tests', () => {
let predefinedSelection: ISelectionData = { startLine: 1, startColumn: 2, endLine: 3, endColumn: 4 };
// ... Mock "getSelection" in QueryEditor
- const instantiationService = new TestInstantiationService();
- let fileInput = new UntitledTextEditorInput(URI.parse('file://testUri'), false, undefined, undefined, undefined, instantiationService, undefined, new LabelService(undefined, undefined), undefined, undefined, new TestFileService(), undefined);
+ const workbenchinstantiationService = workbenchInstantiationService();
+ const accessor = workbenchinstantiationService.createInstance(ServiceAccessor);
+ const service = accessor.untitledTextEditorService;
+ let fileInput = workbenchinstantiationService.createInstance(UntitledTextEditorInput, service.create({ associatedResource: URI.parse('file://testUri') }));
let queryInput = TypeMoq.Mock.ofType(UntitledQueryEditorInput, TypeMoq.MockBehavior.Loose, undefined, fileInput, undefined, connectionManagementService.object, queryModelService.object, configurationService.object);
queryInput.setup(x => x.uri).returns(() => testUri);
@@ -556,3 +561,9 @@ suite('SQL QueryAction Tests', () => {
assert.equal(listItem.currentDatabaseName, eventParams.connectionProfile.databaseName);
});
});
+
+class ServiceAccessor {
+ constructor(
+ @IUntitledTextEditorService public readonly untitledTextEditorService: IUntitledTextEditorService
+ ) { }
+}
diff --git a/src/sql/workbench/contrib/query/test/browser/queryEditor.test.ts b/src/sql/workbench/contrib/query/test/browser/queryEditor.test.ts
index 5370a7d7c8..1ec51ba3d1 100644
--- a/src/sql/workbench/contrib/query/test/browser/queryEditor.test.ts
+++ b/src/sql/workbench/contrib/query/test/browser/queryEditor.test.ts
@@ -17,15 +17,15 @@ import * as TypeMoq from 'typemoq';
import * as assert from 'assert';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
-import { TestFileService, TestStorageService } from 'vs/workbench/test/browser/workbenchTestServices';
+import { TestStorageService, workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput';
import { UntitledQueryEditorInput } from 'sql/workbench/contrib/query/common/untitledQueryEditorInput';
import { TestQueryModelService } from 'sql/workbench/services/query/test/common/testQueryModelService';
import { Event } from 'vs/base/common/event';
-import { LabelService } from 'vs/workbench/services/label/common/labelService';
-import { TestCapabilitiesService } from 'sql/platform/capabilities/test/common/testCapabilitiesService';
+import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
+import { TestCapabilitiesService } from 'sql/platform/capabilities/test/common/testCapabilitiesService';
import { IStorageService } from 'vs/platform/storage/common/storage';
suite('SQL QueryEditor Tests', () => {
@@ -302,7 +302,10 @@ suite('SQL QueryEditor Tests', () => {
return new RunQueryAction(undefined, undefined, undefined);
});
- let fileInput = new UntitledTextEditorInput(URI.parse('file://testUri'), false, undefined, undefined, undefined, instantiationService.object, undefined, new LabelService(undefined, undefined), undefined, undefined, new TestFileService(), undefined);
+ const workbenchinstantiationService = workbenchInstantiationService();
+ const accessor = workbenchinstantiationService.createInstance(ServiceAccessor);
+ const service = accessor.untitledTextEditorService;
+ let fileInput = workbenchinstantiationService.createInstance(UntitledTextEditorInput, service.create({ associatedResource: URI.parse('file://testUri') }));
queryModelService = TypeMoq.Mock.ofType(TestQueryModelService, TypeMoq.MockBehavior.Strict);
queryModelService.setup(x => x.disposeQuery(TypeMoq.It.isAny()));
queryModelService.setup(x => x.onRunQueryComplete).returns(() => Event.None);
@@ -343,3 +346,9 @@ suite('SQL QueryEditor Tests', () => {
});
});
});
+
+class ServiceAccessor {
+ constructor(
+ @IUntitledTextEditorService public readonly untitledTextEditorService: IUntitledTextEditorService
+ ) { }
+}
diff --git a/src/sql/workbench/contrib/query/test/browser/queryInputFactory.test.ts b/src/sql/workbench/contrib/query/test/browser/queryInputFactory.test.ts
index 0b10557aa2..b5645ae52e 100644
--- a/src/sql/workbench/contrib/query/test/browser/queryInputFactory.test.ts
+++ b/src/sql/workbench/contrib/query/test/browser/queryInputFactory.test.ts
@@ -20,6 +20,7 @@ import { IConnectionManagementService, IConnectionCompletionOptions, IConnection
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { UntitledQueryEditorInput } from 'sql/workbench/contrib/query/common/untitledQueryEditorInput';
import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput';
+import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService';
suite('Query Input Factory', () => {
@@ -63,13 +64,22 @@ suite('Query Input Factory', () => {
});
+class ServiceAccessor {
+ constructor(
+ @IUntitledTextEditorService public readonly untitledTextEditorService: IUntitledTextEditorService
+ ) { }
+}
+
class MockEditorService extends TestEditorService {
public readonly activeEditor: IEditorInput | undefined = undefined;
constructor(instantiationService?: IInstantiationService) {
super();
if (instantiationService) {
- const untitledInput = instantiationService.createInstance(UntitledTextEditorInput, URI.file('/test/file'), false, undefined, undefined, undefined);
+ const workbenchinstantiationService = workbenchInstantiationService();
+ const accessor = workbenchinstantiationService.createInstance(ServiceAccessor);
+ const service = accessor.untitledTextEditorService;
+ const untitledInput = instantiationService.createInstance(UntitledTextEditorInput, service.create({ associatedResource: URI.file('/test/file') }));
this.activeEditor = instantiationService.createInstance(UntitledQueryEditorInput, '', untitledInput, undefined);
}
}
diff --git a/src/sql/workbench/services/insights/browser/insightsDialogView.ts b/src/sql/workbench/services/insights/browser/insightsDialogView.ts
index 255cac86f1..d055417808 100644
--- a/src/sql/workbench/services/insights/browser/insightsDialogView.ts
+++ b/src/sql/workbench/services/insights/browser/insightsDialogView.ts
@@ -45,6 +45,7 @@ import { onUnexpectedError } from 'vs/base/common/errors';
import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer';
import { attachPanelStyler, attachModalDialogStyler } from 'sql/workbench/common/styler';
import { IViewDescriptorService } from 'vs/workbench/common/views';
+import { IOpenerService } from 'vs/platform/opener/common/opener';
const labelDisplay = nls.localize("insights.item", "Item");
const valueDisplay = nls.localize("insights.value", "Value");
@@ -66,9 +67,11 @@ class InsightTableView extends ViewPane {
@IConfigurationService configurationService: IConfigurationService,
@IContextKeyService contextKeyService: IContextKeyService,
@IInstantiationService instantiationService: IInstantiationService,
- @IViewDescriptorService viewDescriptorService: IViewDescriptorService
+ @IViewDescriptorService viewDescriptorService: IViewDescriptorService,
+ @IOpenerService openerService: IOpenerService,
+ @IThemeService themeService: IThemeService,
) {
- super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService);
+ super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, opener, themeService);
}
protected renderBody(container: HTMLElement): void {
diff --git a/src/sql/workbench/services/queryEditor/browser/queryEditorService.ts b/src/sql/workbench/services/queryEditor/browser/queryEditorService.ts
index 0dd139e36b..d71d5e7110 100644
--- a/src/sql/workbench/services/queryEditor/browser/queryEditorService.ts
+++ b/src/sql/workbench/services/queryEditor/browser/queryEditorService.ts
@@ -21,6 +21,8 @@ import { replaceConnection } from 'sql/workbench/browser/taskUtilities';
import { EditDataResultsInput } from 'sql/workbench/contrib/editData/browser/editDataResultsInput';
import { ILogService } from 'vs/platform/log/common/log';
import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService';
+import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput';
+import { UntitledTextEditorModel } from 'vs/workbench/services/untitled/common/untitledTextEditorModel';
/**
* Service wrapper for opening and creating SQL documents as sql editor inputs
@@ -52,8 +54,8 @@ export class QueryEditorService implements IQueryEditorService {
let docUri: URI = URI.from({ scheme: Schemas.untitled, path: filePath });
// Create a sql document pane with accoutrements
- const fileInput = this._untitledEditorService.create({ associatedResource: docUri, mode: 'sql' });
- let untitledEditorModel = await fileInput.resolve();
+ const fileInput = this._editorService.createInput({ forceUntitled: true, resource: docUri, mode: 'sql' }) as UntitledTextEditorInput;
+ let untitledEditorModel = await fileInput.resolve() as UntitledTextEditorModel;
if (sqlContent) {
untitledEditorModel.textEditorModel.setValue(sqlContent);
if (isDirty === false || (isDirty === undefined && !this._configurationService.getValue('sql.promptToSaveGeneratedFiles'))) {
@@ -87,8 +89,8 @@ export class QueryEditorService implements IQueryEditorService {
let docUri: URI = URI.from({ scheme: Schemas.untitled, path: filePath });
// Create a sql document pane with accoutrements
- const fileInput = this._untitledEditorService.create({ associatedResource: docUri, mode: 'sql' });
- const m = await fileInput.resolve();
+ const fileInput = this._editorService.createInput({ forceUntitled: true, resource: docUri, mode: 'sql' }) as UntitledTextEditorInput;
+ const m = await fileInput.resolve() as UntitledTextEditorModel;
//when associatedResource editor is created it is dirty, this must be set to false to be able to detect changes to the editor.
m.setDirty(false);
// Create an EditDataInput for editing
diff --git a/src/vs/base/browser/linkedText.ts b/src/vs/base/browser/linkedText.ts
new file mode 100644
index 0000000000..764b078eca
--- /dev/null
+++ b/src/vs/base/browser/linkedText.ts
@@ -0,0 +1,44 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the Source EULA. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+export interface ILink {
+ readonly label: string;
+ readonly href: string;
+ readonly title?: string;
+}
+
+export type LinkedTextNode = string | ILink;
+export type LinkedText = LinkedTextNode[];
+
+const LINK_REGEX = /\[([^\]]+)\]\(((?:https?:\/\/|command:)[^\)\s]+)(?: "([^"]+)")?\)/gi;
+
+export function parseLinkedText(text: string): LinkedText {
+ const result: LinkedTextNode[] = [];
+
+ let index = 0;
+ let match: RegExpExecArray | null;
+
+ while (match = LINK_REGEX.exec(text)) {
+ if (match.index - index > 0) {
+ result.push(text.substring(index, match.index));
+ }
+
+ const [, label, href, title] = match;
+
+ if (title) {
+ result.push({ label, href, title });
+ } else {
+ result.push({ label, href });
+ }
+
+ index = match.index + match[0].length;
+ }
+
+ if (index < text.length) {
+ result.push(text.substring(index));
+ }
+
+ return result;
+}
diff --git a/src/vs/base/browser/ui/button/button.ts b/src/vs/base/browser/ui/button/button.ts
index a443546c8d..05696da41b 100644
--- a/src/vs/base/browser/ui/button/button.ts
+++ b/src/vs/base/browser/ui/button/button.ts
@@ -14,7 +14,7 @@ import { Disposable } from 'vs/base/common/lifecycle';
import { Gesture, EventType } from 'vs/base/browser/touch';
export interface IButtonOptions extends IButtonStyles {
- title?: boolean;
+ title?: boolean | string;
}
export interface IButtonStyles {
@@ -151,10 +151,10 @@ export class Button extends Disposable {
DOM.addClass(this._element, 'monaco-text-button');
}
this._element.textContent = value;
- //{{SQL CARBON EDIT}}
- this._element.setAttribute('aria-label', value);
- //{{END}}
- if (this.options.title) {
+ this._element.setAttribute('aria-label', value); // {{SQL CARBON EDIT}}
+ if (typeof this.options.title === 'string') {
+ this._element.title = this.options.title;
+ } else if (this.options.title) {
this._element.title = value;
}
}
diff --git a/src/vs/base/browser/ui/centered/centeredViewLayout.ts b/src/vs/base/browser/ui/centered/centeredViewLayout.ts
index 64eabc1900..22e24a823b 100644
--- a/src/vs/base/browser/ui/centered/centeredViewLayout.ts
+++ b/src/vs/base/browser/ui/centered/centeredViewLayout.ts
@@ -9,6 +9,7 @@ import { Event } from 'vs/base/common/event';
import { IView, IViewSize } from 'vs/base/browser/ui/grid/grid';
import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { Color } from 'vs/base/common/color';
+import { IBoundarySashes } from 'vs/base/browser/ui/grid/gridview';
export interface CenteredViewState {
leftMarginRatio: number;
@@ -72,6 +73,19 @@ export class CenteredViewLayout implements IDisposable {
get maximumHeight(): number { return this.view.maximumHeight; }
get onDidChange(): Event { return this.view.onDidChange; }
+ private _boundarySashes: IBoundarySashes = {};
+ get boundarySashes(): IBoundarySashes { return this._boundarySashes; }
+ set boundarySashes(boundarySashes: IBoundarySashes) {
+ this._boundarySashes = boundarySashes;
+
+ if (!this.splitView) {
+ return;
+ }
+
+ this.splitView.orthogonalStartSash = boundarySashes.top;
+ this.splitView.orthogonalEndSash = boundarySashes.bottom;
+ }
+
layout(width: number, height: number): void {
this.width = width;
this.height = height;
@@ -119,6 +133,8 @@ export class CenteredViewLayout implements IDisposable {
orientation: Orientation.HORIZONTAL,
styles: this.style
});
+ this.splitView.orthogonalStartSash = this.boundarySashes.top;
+ this.splitView.orthogonalEndSash = this.boundarySashes.bottom;
this.splitViewDisposables.add(this.splitView.onDidSashChange(() => {
if (this.splitView) {
diff --git a/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css b/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css
index 986a7808c0..03fa06167d 100644
--- a/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css
+++ b/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css
@@ -5,7 +5,7 @@
@font-face {
font-family: "codicon";
- src: url("./codicon.ttf?be537a78617db0869caa4b4cc683a24a") format("truetype");
+ src: url("./codicon.ttf?6caeeccc06315e827f3bff83885456fb") format("truetype");
}
.codicon[class*='codicon-'] {
@@ -413,4 +413,5 @@
.codicon-feedback:before { content: "\eb96" }
.codicon-group-by-ref-type:before { content: "\eb97" }
.codicon-ungroup-by-ref-type:before { content: "\eb98" }
-.codicon-debug-alt:before { content: "\f101" }
+.codicon-debug-alt-2:before { content: "\f101" }
+.codicon-debug-alt:before { content: "\f102" }
diff --git a/src/vs/base/browser/ui/codiconLabel/codicon/codicon.ttf b/src/vs/base/browser/ui/codiconLabel/codicon/codicon.ttf
index 90ace76ff7..a62bc6bd74 100644
Binary files a/src/vs/base/browser/ui/codiconLabel/codicon/codicon.ttf and b/src/vs/base/browser/ui/codiconLabel/codicon/codicon.ttf differ
diff --git a/src/vs/base/browser/ui/grid/grid.ts b/src/vs/base/browser/ui/grid/grid.ts
index 3dd57bf12a..178c26093a 100644
--- a/src/vs/base/browser/ui/grid/grid.ts
+++ b/src/vs/base/browser/ui/grid/grid.ts
@@ -7,7 +7,7 @@ import 'vs/css!./gridview';
import { Orientation } from 'vs/base/browser/ui/sash/sash';
import { Disposable } from 'vs/base/common/lifecycle';
import { tail2 as tail, equals } from 'vs/base/common/arrays';
-import { orthogonal, IView as IGridViewView, GridView, Sizing as GridViewSizing, Box, IGridViewStyles, IViewSize, IGridViewOptions } from './gridview';
+import { orthogonal, IView as IGridViewView, GridView, Sizing as GridViewSizing, Box, IGridViewStyles, IViewSize, IGridViewOptions, IBoundarySashes } from './gridview';
import { Event } from 'vs/base/common/event';
export { Orientation, Sizing as GridViewSizing, IViewSize, orthogonal, LayoutPriority } from './gridview';
@@ -212,6 +212,9 @@ export class Grid extends Disposable {
get maximumHeight(): number { return this.gridview.maximumHeight; }
get onDidChange(): Event<{ width: number; height: number; } | undefined> { return this.gridview.onDidChange; }
+ get boundarySashes(): IBoundarySashes { return this.gridview.boundarySashes; }
+ set boundarySashes(boundarySashes: IBoundarySashes) { this.gridview.boundarySashes = boundarySashes; }
+
get element(): HTMLElement { return this.gridview.element; }
private didLayout = false;
diff --git a/src/vs/base/browser/ui/grid/gridview.ts b/src/vs/base/browser/ui/grid/gridview.ts
index 83bce75206..6a22c4fdf4 100644
--- a/src/vs/base/browser/ui/grid/gridview.ts
+++ b/src/vs/base/browser/ui/grid/gridview.ts
@@ -21,6 +21,20 @@ export interface IViewSize {
readonly height: number;
}
+interface IRelativeBoundarySashes {
+ readonly start?: Sash;
+ readonly end?: Sash;
+ readonly orthogonalStart?: Sash;
+ readonly orthogonalEnd?: Sash;
+}
+
+export interface IBoundarySashes {
+ readonly top?: Sash;
+ readonly right?: Sash;
+ readonly bottom?: Sash;
+ readonly left?: Sash;
+}
+
export interface IView {
readonly element: HTMLElement;
readonly minimumWidth: number;
@@ -32,6 +46,7 @@ export interface IView {
readonly snap?: boolean;
layout(width: number, height: number, top: number, left: number): void;
setVisible?(visible: boolean): void;
+ setBoundarySashes?(sashes: IBoundarySashes): void;
}
export interface ISerializableView extends IView {
@@ -125,6 +140,22 @@ interface ILayoutContext {
readonly absoluteOrthogonalSize: number;
}
+function toAbsoluteBoundarySashes(sashes: IRelativeBoundarySashes, orientation: Orientation): IBoundarySashes {
+ if (orientation === Orientation.HORIZONTAL) {
+ return { left: sashes.start, right: sashes.end, top: sashes.orthogonalStart, bottom: sashes.orthogonalEnd };
+ } else {
+ return { top: sashes.start, bottom: sashes.end, left: sashes.orthogonalStart, right: sashes.orthogonalEnd };
+ }
+}
+
+function fromAbsoluteBoundarySashes(sashes: IBoundarySashes, orientation: Orientation): IRelativeBoundarySashes {
+ if (orientation === Orientation.HORIZONTAL) {
+ return { start: sashes.left, end: sashes.right, orthogonalStart: sashes.top, orthogonalEnd: sashes.bottom };
+ } else {
+ return { start: sashes.top, end: sashes.bottom, orthogonalStart: sashes.left, orthogonalEnd: sashes.right };
+ }
+}
+
class BranchNode implements ISplitView, IDisposable {
readonly element: HTMLElement;
@@ -217,10 +248,27 @@ class BranchNode implements ISplitView, IDisposable {
private splitviewSashResetDisposable: IDisposable = Disposable.None;
private childrenSashResetDisposable: IDisposable = Disposable.None;
- get orthogonalStartSash(): Sash | undefined { return this.splitview.orthogonalStartSash; }
- set orthogonalStartSash(sash: Sash | undefined) { this.splitview.orthogonalStartSash = sash; }
- get orthogonalEndSash(): Sash | undefined { return this.splitview.orthogonalEndSash; }
- set orthogonalEndSash(sash: Sash | undefined) { this.splitview.orthogonalEndSash = sash; }
+ private _boundarySashes: IRelativeBoundarySashes = {};
+ get boundarySashes(): IRelativeBoundarySashes { return this._boundarySashes; }
+ set boundarySashes(boundarySashes: IRelativeBoundarySashes) {
+ this._boundarySashes = boundarySashes;
+
+ this.splitview.orthogonalStartSash = boundarySashes.orthogonalStart;
+ this.splitview.orthogonalEndSash = boundarySashes.orthogonalEnd;
+
+ for (let index = 0; index < this.children.length; index++) {
+ const child = this.children[index];
+ const first = index === 0;
+ const last = index === this.children.length - 1;
+
+ child.boundarySashes = {
+ start: boundarySashes.orthogonalStart,
+ end: boundarySashes.orthogonalEnd,
+ orthogonalStart: first ? boundarySashes.start : child.boundarySashes.orthogonalStart,
+ orthogonalEnd: last ? boundarySashes.end : child.boundarySashes.orthogonalEnd,
+ };
+ }
+ }
constructor(
readonly orientation: Orientation,
@@ -260,9 +308,15 @@ class BranchNode implements ISplitView, IDisposable {
this.splitview = new SplitView(this.element, { ...options, descriptor });
this.children.forEach((node, index) => {
- // Set up orthogonal sashes for children
- node.orthogonalStartSash = this.splitview.sashes[index - 1];
- node.orthogonalEndSash = this.splitview.sashes[index];
+ const first = index === 0;
+ const last = index === this.children.length;
+
+ node.boundarySashes = {
+ start: this.boundarySashes.orthogonalStart,
+ end: this.boundarySashes.orthogonalEnd,
+ orthogonalStart: first ? this.boundarySashes.start : this.splitview.sashes[index - 1],
+ orthogonalEnd: last ? this.boundarySashes.end : this.splitview.sashes[index],
+ };
});
}
@@ -335,15 +389,26 @@ class BranchNode implements ISplitView, IDisposable {
const first = index === 0;
const last = index === this.children.length;
this.children.splice(index, 0, node);
- node.orthogonalStartSash = this.splitview.sashes[index - 1];
- node.orthogonalEndSash = this.splitview.sashes[index];
+
+ node.boundarySashes = {
+ start: this.boundarySashes.orthogonalStart,
+ end: this.boundarySashes.orthogonalEnd,
+ orthogonalStart: first ? this.boundarySashes.start : this.splitview.sashes[index - 1],
+ orthogonalEnd: last ? this.boundarySashes.end : this.splitview.sashes[index],
+ };
if (!first) {
- this.children[index - 1].orthogonalEndSash = this.splitview.sashes[index - 1];
+ this.children[index - 1].boundarySashes = {
+ ...this.children[index - 1].boundarySashes,
+ orthogonalEnd: this.splitview.sashes[index - 1]
+ };
}
if (!last) {
- this.children[index + 1].orthogonalStartSash = this.splitview.sashes[index];
+ this.children[index + 1].boundarySashes = {
+ ...this.children[index + 1].boundarySashes,
+ orthogonalStart: this.splitview.sashes[index]
+ };
}
}
@@ -363,11 +428,17 @@ class BranchNode implements ISplitView, IDisposable {
const [child] = this.children.splice(index, 1);
if (!first) {
- this.children[index - 1].orthogonalEndSash = this.splitview.sashes[index - 1];
+ this.children[index - 1].boundarySashes = {
+ ...this.children[index - 1].boundarySashes,
+ orthogonalEnd: this.splitview.sashes[index - 1]
+ };
}
if (!last) { // [0,1,2,3] (2) => [0,1,3]
- this.children[index].orthogonalStartSash = this.splitview.sashes[Math.max(index - 1, 0)];
+ this.children[index].boundarySashes = {
+ ...this.children[index].boundarySashes,
+ orthogonalStart: this.splitview.sashes[Math.max(index - 1, 0)]
+ };
}
return child;
@@ -408,7 +479,12 @@ class BranchNode implements ISplitView, IDisposable {
to = clamp(to, 0, this.children.length);
this.splitview.swapViews(from, to);
- [this.children[from].orthogonalStartSash, this.children[from].orthogonalEndSash, this.children[to].orthogonalStartSash, this.children[to].orthogonalEndSash] = [this.children[to].orthogonalStartSash, this.children[to].orthogonalEndSash, this.children[from].orthogonalStartSash, this.children[from].orthogonalEndSash];
+
+ // swap boundary sashes
+ [this.children[from].boundarySashes, this.children[to].boundarySashes]
+ = [this.children[from].boundarySashes, this.children[to].boundarySashes];
+
+ // swap children
[this.children[from], this.children[to]] = [this.children[to], this.children[from]];
this.onDidChildrenChange();
@@ -655,12 +731,14 @@ class LeafNode implements ISplitView, IDisposable {
return this.orientation === Orientation.HORIZONTAL ? this.maximumWidth : this.maximumHeight;
}
- set orthogonalStartSash(sash: Sash) {
- // noop
- }
+ private _boundarySashes: IRelativeBoundarySashes = {};
+ get boundarySashes(): IRelativeBoundarySashes { return this._boundarySashes; }
+ set boundarySashes(boundarySashes: IRelativeBoundarySashes) {
+ this._boundarySashes = boundarySashes;
- set orthogonalEndSash(sash: Sash) {
- // noop
+ if (this.view.setBoundarySashes) {
+ this.view.setBoundarySashes(toAbsoluteBoundarySashes(boundarySashes, this.orientation));
+ }
}
layout(size: number, offset: number, ctx: ILayoutContext | undefined): void {
@@ -764,6 +842,7 @@ export class GridView implements IDisposable {
const { size, orthogonalSize } = this._root;
this.root = flipNode(this._root, orthogonalSize, size);
this.root.layout(size, 0, { orthogonalSize, absoluteOffset: 0, absoluteOrthogonalOffset: 0, absoluteSize: size, absoluteOrthogonalSize: orthogonalSize });
+ this.boundarySashes = this.boundarySashes;
}
get width(): number { return this.root.width; }
@@ -777,6 +856,13 @@ export class GridView implements IDisposable {
private _onDidChange = new Relay();
readonly onDidChange = this._onDidChange.event;
+ private _boundarySashes: IBoundarySashes = {};
+ get boundarySashes(): IBoundarySashes { return this._boundarySashes; }
+ set boundarySashes(boundarySashes: IBoundarySashes) {
+ this._boundarySashes = boundarySashes;
+ this.root.boundarySashes = fromAbsoluteBoundarySashes(boundarySashes, this.orientation);
+ }
+
/**
* The first layout controller makes sure layout only propagates
* to the views after the very first call to gridview.layout()
@@ -898,6 +984,7 @@ export class GridView implements IDisposable {
// we must promote sibling to be the new root
parent.removeChild(0);
this.root = sibling;
+ this.boundarySashes = this.boundarySashes;
return node.view;
}
diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts
index c2670776ea..daa30e2c20 100644
--- a/src/vs/base/browser/ui/tree/asyncDataTree.ts
+++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts
@@ -128,14 +128,14 @@ class AsyncDataTreeRenderer implements IT
}
}
-function asTreeEvent(e: ITreeEvent>): ITreeEvent {
+function asTreeEvent(e: ITreeEvent | null>): ITreeEvent {
return {
browserEvent: e.browserEvent,
- elements: e.elements.map(e => e.element as T)
+ elements: e.elements.map(e => e!.element as T)
};
}
-function asTreeMouseEvent(e: ITreeMouseEvent>): ITreeMouseEvent {
+function asTreeMouseEvent(e: ITreeMouseEvent | null>): ITreeMouseEvent {
return {
browserEvent: e.browserEvent,
element: e.element && e.element.element as T,
@@ -143,7 +143,7 @@ function asTreeMouseEvent(e: ITreeMouseEvent(e: ITreeContextMenuEvent>): ITreeContextMenuEvent {
+function asTreeContextMenuEvent(e: ITreeContextMenuEvent | null>): ITreeContextMenuEvent {
return {
browserEvent: e.browserEvent,
element: e.element && e.element.element as T,
@@ -793,7 +793,11 @@ export class AsyncDataTree implements IDisposable
return result.finally(() => this.refreshPromises.delete(node));
}
- private _onDidChangeCollapseState({ node, deep }: ICollapseStateChangeEvent, any>): void {
+ private _onDidChangeCollapseState({ node, deep }: ICollapseStateChangeEvent | null, any>): void {
+ if (node.element === null) {
+ return;
+ }
+
if (!node.collapsed && node.element.stale) {
if (deep) {
this.collapse(node.element.element as T);
diff --git a/src/vs/base/browser/ui/tree/dataTree.ts b/src/vs/base/browser/ui/tree/dataTree.ts
index 766628925b..1eaf31141c 100644
--- a/src/vs/base/browser/ui/tree/dataTree.ts
+++ b/src/vs/base/browser/ui/tree/dataTree.ts
@@ -37,7 +37,7 @@ export class DataTree extends AbstractTree,
options: IDataTreeOptions = {}
) {
- super(user, container, delegate, renderers, options);
+ super(user, container, delegate, renderers, options as IDataTreeOptions);
this.identityProvider = options.identityProvider;
}
@@ -182,7 +182,7 @@ export class DataTree extends AbstractTree this.identityProvider!.getId(element).toString();
+ const getId = (element: T | null) => this.identityProvider!.getId(element!).toString();
const focus = this.getFocus().map(getId);
const selection = this.getSelection().map(getId);
diff --git a/src/vs/base/browser/ui/tree/objectTree.ts b/src/vs/base/browser/ui/tree/objectTree.ts
index a21f9b8be4..be20a0181a 100644
--- a/src/vs/base/browser/ui/tree/objectTree.ts
+++ b/src/vs/base/browser/ui/tree/objectTree.ts
@@ -30,7 +30,7 @@ export class ObjectTree, TFilterData = void> extends
renderers: ITreeRenderer[],
options: IObjectTreeOptions = {}
) {
- super(user, container, delegate, renderers, options);
+ super(user, container, delegate, renderers, options as IObjectTreeOptions);
}
setChildren(element: T | null, children?: ISequence>): void {
@@ -181,7 +181,7 @@ export class CompressibleObjectTree, TFilterData = vo
) {
const compressedTreeNodeProvider = () => this;
const compressibleRenderers = renderers.map(r => new CompressibleRenderer(compressedTreeNodeProvider, r));
- super(user, container, delegate, compressibleRenderers, asObjectTreeOptions(compressedTreeNodeProvider, options));
+ super(user, container, delegate, compressibleRenderers, asObjectTreeOptions(compressedTreeNodeProvider, options));
}
setChildren(element: T | null, children?: ISequence>): void {
diff --git a/src/vs/base/browser/ui/tree/objectTreeModel.ts b/src/vs/base/browser/ui/tree/objectTreeModel.ts
index f0e0abdd47..d319ccc219 100644
--- a/src/vs/base/browser/ui/tree/objectTreeModel.ts
+++ b/src/vs/base/browser/ui/tree/objectTreeModel.ts
@@ -79,35 +79,47 @@ export class ObjectTreeModel, TFilterData extends Non
const insertedElements = new Set();
const insertedElementIds = new Set();
- const _onDidCreateNode = (node: ITreeNode) => {
- insertedElements.add(node.element);
- this.nodes.set(node.element, node);
+ const _onDidCreateNode = (node: ITreeNode) => {
+ if (node.element === null) {
+ return;
+ }
+
+ const tnode = node as ITreeNode;
+
+ insertedElements.add(tnode.element);
+ this.nodes.set(tnode.element, tnode);
if (this.identityProvider) {
- const id = this.identityProvider.getId(node.element).toString();
+ const id = this.identityProvider.getId(tnode.element).toString();
insertedElementIds.add(id);
- this.nodesByIdentity.set(id, node);
+ this.nodesByIdentity.set(id, tnode);
}
if (onDidCreateNode) {
- onDidCreateNode(node);
+ onDidCreateNode(tnode);
}
};
- const _onDidDeleteNode = (node: ITreeNode) => {
- if (!insertedElements.has(node.element)) {
- this.nodes.delete(node.element);
+ const _onDidDeleteNode = (node: ITreeNode) => {
+ if (node.element === null) {
+ return;
+ }
+
+ const tnode = node as ITreeNode;
+
+ if (!insertedElements.has(tnode.element)) {
+ this.nodes.delete(tnode.element);
}
if (this.identityProvider) {
- const id = this.identityProvider.getId(node.element).toString();
+ const id = this.identityProvider.getId(tnode.element).toString();
if (!insertedElementIds.has(id)) {
this.nodesByIdentity.delete(id);
}
}
if (onDidDeleteNode) {
- onDidDeleteNode(node);
+ onDidDeleteNode(tnode);
}
};
diff --git a/src/vs/base/common/cancellation.ts b/src/vs/base/common/cancellation.ts
index 5a38066025..7570c124ee 100644
--- a/src/vs/base/common/cancellation.ts
+++ b/src/vs/base/common/cancellation.ts
@@ -7,18 +7,27 @@ import { Emitter, Event } from 'vs/base/common/event';
import { IDisposable } from 'vs/base/common/lifecycle';
export interface CancellationToken {
- readonly isCancellationRequested: boolean;
+
/**
- * An event emitted when cancellation is requested
+ * A flag signalling is cancellation has been requested.
+ */
+ readonly isCancellationRequested: boolean;
+
+ /**
+ * An event which fires when cancellation is requested. This event
+ * only ever fires `once` as cancellation can only happen once. Listeners
+ * that are registered after cancellation will be called (next event loop run),
+ * but also only once.
+ *
* @event
*/
- readonly onCancellationRequested: Event;
+ readonly onCancellationRequested: (listener: (e: any) => any, thisArgs?: any, disposables?: IDisposable[]) => IDisposable;
}
-const shortcutEvent = Object.freeze(function (callback, context?): IDisposable {
+const shortcutEvent: Event = Object.freeze(function (callback, context?): IDisposable {
const handle = setTimeout(callback.bind(context), 0);
return { dispose() { clearTimeout(handle); } };
-} as Event);
+});
export namespace CancellationToken {
diff --git a/src/vs/base/common/event.ts b/src/vs/base/common/event.ts
index 0c16242740..32992e5523 100644
--- a/src/vs/base/common/event.ts
+++ b/src/vs/base/common/event.ts
@@ -85,6 +85,8 @@ export namespace Event {
* Given a collection of events, returns a single event which emits
* whenever any of the provided events emit.
*/
+ export function any(...events: Event[]): Event;
+ export function any(...events: Event[]): Event;
export function any(...events: Event[]): Event {
return (listener, thisArgs = null, disposables?) => combinedDisposable(...events.map(event => event(e => listener.call(thisArgs, e), null, disposables)));
}
@@ -271,6 +273,7 @@ export namespace Event {
map(fn: (i: T) => O): IChainableEvent;
forEach(fn: (i: T) => void): IChainableEvent;
filter(fn: (e: T) => boolean): IChainableEvent;
+ filter(fn: (e: T | R) => e is R): IChainableEvent;
reduce(merge: (last: R | undefined, event: T) => R, initial?: R): IChainableEvent;
latch(): IChainableEvent;
debounce(merge: (last: T | undefined, event: T) => T, delay?: number, leading?: boolean, leakWarningThreshold?: number): IChainableEvent;
@@ -291,6 +294,8 @@ export namespace Event {
return new ChainableEvent(forEach(this.event, fn));
}
+ filter(fn: (e: T) => boolean): IChainableEvent;
+ filter(fn: (e: T | R) => e is R): IChainableEvent;
filter(fn: (e: T) => boolean): IChainableEvent {
return new ChainableEvent(filter(this.event, fn));
}
diff --git a/src/vs/base/node/config.ts b/src/vs/base/node/config.ts
deleted file mode 100644
index 41dac28ee2..0000000000
--- a/src/vs/base/node/config.ts
+++ /dev/null
@@ -1,189 +0,0 @@
-/*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the Source EULA. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
-
-import * as fs from 'fs';
-import { dirname } from 'vs/base/common/path';
-import * as objects from 'vs/base/common/objects';
-import { Disposable } from 'vs/base/common/lifecycle';
-import { Event, Emitter } from 'vs/base/common/event';
-import * as json from 'vs/base/common/json';
-import { statLink } from 'vs/base/node/pfs';
-import { realpath } from 'vs/base/node/extpath';
-import { watchFolder, watchFile } from 'vs/base/node/watcher';
-
-export interface IConfigurationChangeEvent {
- config: T;
-}
-
-export interface IConfigWatcher {
- path: string;
- hasParseErrors: boolean;
-
- reload(callback: (config: T) => void): void;
- getConfig(): T;
-}
-
-export interface IConfigOptions {
- onError: (error: Error | string) => void;
- defaultConfig: T;
- changeBufferDelay?: number;
- parse?: (content: string, errors: any[]) => T;
- initCallback?: (config: T) => void;
-}
-
-/**
- * A simple helper to watch a configured file for changes and process its contents as JSON object.
- * Supports:
- * - comments in JSON files and errors
- * - symlinks for the config file itself
- * - delayed processing of changes to accomodate for lots of changes
- * - configurable defaults
- */
-export class ConfigWatcher extends Disposable implements IConfigWatcher {
- private cache: T | undefined;
- private parseErrors: json.ParseError[] | undefined;
- private disposed: boolean | undefined;
- private loaded: boolean | undefined;
- private timeoutHandle: NodeJS.Timer | null | undefined;
- private readonly _onDidUpdateConfiguration: Emitter>;
-
- constructor(private _path: string, private options: IConfigOptions = { defaultConfig: Object.create(null), onError: error => console.error(error) }) {
- super();
- this._onDidUpdateConfiguration = this._register(new Emitter>());
-
- this.registerWatcher();
- this.initAsync();
- }
-
- get path(): string {
- return this._path;
- }
-
- get hasParseErrors(): boolean {
- return !!this.parseErrors && this.parseErrors.length > 0;
- }
-
- get onDidUpdateConfiguration(): Event> {
- return this._onDidUpdateConfiguration.event;
- }
-
- private initAsync(): void {
- this.loadAsync(config => {
- if (!this.loaded) {
- this.updateCache(config); // prevent race condition if config was loaded sync already
- }
- if (this.options.initCallback) {
- this.options.initCallback(this.getConfig());
- }
- });
- }
-
- private updateCache(value: T): void {
- this.cache = value;
- this.loaded = true;
- }
-
- private loadSync(): T {
- try {
- return this.parse(fs.readFileSync(this._path).toString());
- } catch (error) {
- return this.options.defaultConfig;
- }
- }
-
- private loadAsync(callback: (config: T) => void): void {
- fs.readFile(this._path, (error, raw) => {
- if (error) {
- return callback(this.options.defaultConfig);
- }
-
- return callback(this.parse(raw.toString()));
- });
- }
-
- private parse(raw: string): T {
- let res: T;
- try {
- this.parseErrors = [];
- res = this.options.parse ? this.options.parse(raw, this.parseErrors) : json.parse(raw, this.parseErrors);
-
- return res || this.options.defaultConfig;
- } catch (error) {
- return this.options.defaultConfig; // Ignore parsing errors
- }
- }
-
- private registerWatcher(): void {
-
- // Watch the parent of the path so that we detect ADD and DELETES
- const parentFolder = dirname(this._path);
- this.watch(parentFolder, true);
-
- // Check if the path is a symlink and watch its target if so
- this.handleSymbolicLink().then(undefined, () => { /* ignore error */ });
- }
-
- private async handleSymbolicLink(): Promise {
- const { stat, symbolicLink } = await statLink(this._path);
- if (symbolicLink && !stat.isDirectory()) {
- const realPath = await realpath(this._path);
-
- this.watch(realPath, false);
- }
- }
-
- private watch(path: string, isFolder: boolean): void {
- if (this.disposed) {
- return; // avoid watchers that will never get disposed by checking for being disposed
- }
-
- if (isFolder) {
- this._register(watchFolder(path, (type, path) => path === this._path ? this.onConfigFileChange() : undefined, error => this.options.onError(error)));
- } else {
- this._register(watchFile(path, () => this.onConfigFileChange(), error => this.options.onError(error)));
- }
- }
-
- private onConfigFileChange(): void {
- if (this.timeoutHandle) {
- global.clearTimeout(this.timeoutHandle);
- this.timeoutHandle = null;
- }
-
- // we can get multiple change events for one change, so we buffer through a timeout
- this.timeoutHandle = global.setTimeout(() => this.reload(), this.options.changeBufferDelay || 0);
- }
-
- reload(callback?: (config: T) => void): void {
- this.loadAsync(currentConfig => {
- if (!objects.equals(currentConfig, this.cache)) {
- this.updateCache(currentConfig);
-
- this._onDidUpdateConfiguration.fire({ config: currentConfig });
- }
-
- if (callback) {
- return callback(currentConfig);
- }
- });
- }
-
- getConfig(): T {
- this.ensureLoaded();
-
- return this.cache!;
- }
-
- private ensureLoaded(): void {
- if (!this.loaded) {
- this.updateCache(this.loadSync());
- }
- }
-
- dispose(): void {
- this.disposed = true;
- super.dispose();
- }
-}
diff --git a/src/vs/base/node/encoding.ts b/src/vs/base/node/encoding.ts
index 08fc6ca613..499c75b28c 100644
--- a/src/vs/base/node/encoding.ts
+++ b/src/vs/base/node/encoding.ts
@@ -52,7 +52,7 @@ export function toDecodeStream(readable: Readable, options: IDecodeStreamOptions
private bufferedChunks: Buffer[] = [];
private bytesBuffered = 0;
- _write(chunk: Buffer, encoding: string, callback: (error: Error | null) => void): void {
+ _write(chunk: Buffer, encoding: string, callback: (error: Error | null | undefined) => void): void {
if (!Buffer.isBuffer(chunk)) {
return callback(new Error('toDecodeStream(): data must be a buffer'));
}
@@ -84,7 +84,7 @@ export function toDecodeStream(readable: Readable, options: IDecodeStreamOptions
}
}
- _startDecodeStream(callback: (error: Error | null) => void): void {
+ _startDecodeStream(callback: (error: Error | null | undefined) => void): void {
// detect encoding from buffer
this.decodeStreamPromise = Promise.resolve(detectEncodingFromBuffer({
diff --git a/src/vs/base/node/zip.ts b/src/vs/base/node/zip.ts
index 30724dc30f..c9b993643f 100644
--- a/src/vs/base/node/zip.ts
+++ b/src/vs/base/node/zip.ts
@@ -12,7 +12,6 @@ import { mkdirp, rimraf } from 'vs/base/node/pfs';
import { open as _openZip, Entry, ZipFile } from 'yauzl';
import * as yazl from 'yazl';
import { CancellationToken } from 'vs/base/common/cancellation';
-import { Event } from 'vs/base/common/event';
export interface IExtractOptions {
overwrite?: boolean;
@@ -80,7 +79,7 @@ function extractEntry(stream: Readable, fileName: string, mode: number, targetPa
let istream: WriteStream;
- Event.once(token.onCancellationRequested)(() => {
+ token.onCancellationRequested(() => {
if (istream) {
istream.destroy();
}
@@ -107,7 +106,7 @@ function extractZip(zipfile: ZipFile, targetPath: string, options: IOptions, tok
let last = createCancelablePromise(() => Promise.resolve());
let extractedEntriesCount = 0;
- Event.once(token.onCancellationRequested)(() => {
+ token.onCancellationRequested(() => {
last.cancel();
zipfile.close();
});
diff --git a/src/vs/base/parts/tree/browser/treeModel.ts b/src/vs/base/parts/tree/browser/treeModel.ts
index d29542bf51..dd1fb978f0 100644
--- a/src/vs/base/parts/tree/browser/treeModel.ts
+++ b/src/vs/base/parts/tree/browser/treeModel.ts
@@ -141,8 +141,8 @@ export class ItemRegistry {
readonly onDidCollapseItem: Event = this._onDidCollapseItem.event;
private _onDidAddTraitItem = new EventMultiplexer();
readonly onDidAddTraitItem: Event = this._onDidAddTraitItem.event;
- private _onDidRemoveTraitItem = new EventMultiplexer();
- readonly onDidRemoveTraitItem: Event = this._onDidRemoveTraitItem.event;
+ private _onDidRemoveTraitItem = new EventMultiplexer();
+ readonly onDidRemoveTraitItem: Event = this._onDidRemoveTraitItem.event;
private _onDidRefreshItem = new EventMultiplexer- ();
readonly onDidRefreshItem: Event
- = this._onDidRefreshItem.event;
private _onRefreshItemChildren = new EventMultiplexer();
@@ -273,8 +273,8 @@ export class Item {
readonly onDidCollapse: Event = this._onDidCollapse.event;
private readonly _onDidAddTrait = new Emitter();
readonly onDidAddTrait: Event = this._onDidAddTrait.event;
- private readonly _onDidRemoveTrait = new Emitter();
- readonly onDidRemoveTrait: Event = this._onDidRemoveTrait.event;
+ private readonly _onDidRemoveTrait = new Emitter();
+ readonly onDidRemoveTrait: Event = this._onDidRemoveTrait.event;
private readonly _onDidRefresh = new Emitter
- ();
readonly onDidRefresh: Event
- = this._onDidRefresh.event;
private readonly _onRefreshChildren = new Emitter();
@@ -895,8 +895,8 @@ export class TreeModel {
readonly onDidCollapseItem: Event = this._onDidCollapseItem.event;
private _onDidAddTraitItem = new Relay();
readonly onDidAddTraitItem: Event = this._onDidAddTraitItem.event;
- private _onDidRemoveTraitItem = new Relay();
- readonly onDidRemoveTraitItem: Event = this._onDidRemoveTraitItem.event;
+ private _onDidRemoveTraitItem = new Relay();
+ readonly onDidRemoveTraitItem: Event = this._onDidRemoveTraitItem.event;
private _onDidRefreshItem = new Relay
- ();
readonly onDidRefreshItem: Event
- = this._onDidRefreshItem.event;
private _onRefreshItemChildren = new Relay();
diff --git a/src/vs/base/parts/tree/browser/treeView.ts b/src/vs/base/parts/tree/browser/treeView.ts
index c1e1efa294..b70335d62a 100644
--- a/src/vs/base/parts/tree/browser/treeView.ts
+++ b/src/vs/base/parts/tree/browser/treeView.ts
@@ -557,7 +557,7 @@ export class TreeView extends HeightMap {
this.viewListeners.push(DOM.addDisposableListener(this.wrapper, 'MSGestureTap', (e) => this.onMsGestureTap(e)));
// these events come too fast, we throttle them
- this.viewListeners.push(DOM.addDisposableThrottledListener(this.wrapper, 'MSGestureChange', (e) => this.onThrottledMsGestureChange(e), (lastEvent: IThrottledGestureEvent, event: MSGestureEvent): IThrottledGestureEvent => {
+ this.viewListeners.push(DOM.addDisposableThrottledListener(this.wrapper, 'MSGestureChange', e => this.onThrottledMsGestureChange(e), (lastEvent, event) => {
event.stopPropagation();
event.preventDefault();
diff --git a/src/vs/base/test/browser/linkedText.test.ts b/src/vs/base/test/browser/linkedText.test.ts
new file mode 100644
index 0000000000..d1e3bbef14
--- /dev/null
+++ b/src/vs/base/test/browser/linkedText.test.ts
@@ -0,0 +1,58 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the Source EULA. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import * as assert from 'assert';
+import { parseLinkedText } from 'vs/base/browser/linkedText';
+
+suite('LinkedText', () => {
+ test('parses correctly', () => {
+ assert.deepEqual(parseLinkedText(''), []);
+ assert.deepEqual(parseLinkedText('hello'), ['hello']);
+ assert.deepEqual(parseLinkedText('hello there'), ['hello there']);
+ assert.deepEqual(parseLinkedText('Some message with [link text](http://link.href).'), [
+ 'Some message with ',
+ { label: 'link text', href: 'http://link.href' },
+ '.'
+ ]);
+ assert.deepEqual(parseLinkedText('Some message with [link text](http://link.href "and a title").'), [
+ 'Some message with ',
+ { label: 'link text', href: 'http://link.href', title: 'and a title' },
+ '.'
+ ]);
+ assert.deepEqual(parseLinkedText('Some message with [link text](random stuff).'), [
+ 'Some message with [link text](random stuff).'
+ ]);
+ assert.deepEqual(parseLinkedText('Some message with [https link](https://link.href).'), [
+ 'Some message with ',
+ { label: 'https link', href: 'https://link.href' },
+ '.'
+ ]);
+ assert.deepEqual(parseLinkedText('Some message with [https link](https:).'), [
+ 'Some message with [https link](https:).'
+ ]);
+ assert.deepEqual(parseLinkedText('Some message with [a command](command:foobar).'), [
+ 'Some message with ',
+ { label: 'a command', href: 'command:foobar' },
+ '.'
+ ]);
+ assert.deepEqual(parseLinkedText('Some message with [a command](command:).'), [
+ 'Some message with [a command](command:).'
+ ]);
+ assert.deepEqual(parseLinkedText('link [one](command:foo "nice") and link [two](http://foo)...'), [
+ 'link ',
+ { label: 'one', href: 'command:foo', title: 'nice' },
+ ' and link ',
+ { label: 'two', href: 'http://foo' },
+ '...'
+ ]);
+ assert.deepEqual(parseLinkedText('link\n[one](command:foo "nice")\nand link [two](http://foo)...'), [
+ 'link\n',
+ { label: 'one', href: 'command:foo', title: 'nice' },
+ '\nand link ',
+ { label: 'two', href: 'http://foo' },
+ '...'
+ ]);
+ });
+});
diff --git a/src/vs/base/test/common/resources.test.ts b/src/vs/base/test/common/resources.test.ts
index 449d4cac11..39d91ed1f5 100644
--- a/src/vs/base/test/common/resources.test.ts
+++ b/src/vs/base/test/common/resources.test.ts
@@ -108,6 +108,7 @@ suite('Resources', () => {
assert.equal(joinPath(URI.file('/foo/bar'), '/./file.js').toString(), 'file:///foo/bar/file.js');
assert.equal(joinPath(URI.file('/foo/bar'), '../file.js').toString(), 'file:///foo/file.js');
}
+ assert.equal(joinPath(URI.parse('foo://a/foo/bar')).toString(), 'foo://a/foo/bar');
assert.equal(joinPath(URI.parse('foo://a/foo/bar'), '/file.js').toString(), 'foo://a/foo/bar/file.js');
assert.equal(joinPath(URI.parse('foo://a/foo/bar'), 'file.js').toString(), 'foo://a/foo/bar/file.js');
assert.equal(joinPath(URI.parse('foo://a/foo/bar/'), '/file.js').toString(), 'foo://a/foo/bar/file.js');
diff --git a/src/vs/base/test/node/config.test.ts b/src/vs/base/test/node/config.test.ts
deleted file mode 100644
index 98743764b3..0000000000
--- a/src/vs/base/test/node/config.test.ts
+++ /dev/null
@@ -1,163 +0,0 @@
-/*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the Source EULA. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
-
-import * as assert from 'assert';
-import * as os from 'os';
-
-import * as path from 'vs/base/common/path';
-import * as fs from 'fs';
-import * as uuid from 'vs/base/common/uuid';
-import { ConfigWatcher } from 'vs/base/node/config';
-import { testFile } from 'vs/base/test/node/utils';
-
-suite('Config', () => {
-
- test('defaults', () => {
- const id = uuid.generateUuid();
- const parentDir = path.join(os.tmpdir(), 'vsctests', id);
- const newDir = path.join(parentDir, 'config', id);
- const testFile = path.join(newDir, 'config.json');
-
- let watcher = new ConfigWatcher<{}>(testFile);
-
- let config = watcher.getConfig();
- assert.ok(config);
- assert.equal(Object.keys(config), 0);
-
- watcher.dispose();
-
- let watcher2 = new ConfigWatcher(testFile, { defaultConfig: ['foo'], onError: console.error });
-
- let config2 = watcher2.getConfig();
- assert.ok(Array.isArray(config2));
- assert.equal(config2.length, 1);
-
- watcher.dispose();
- });
-
- test('getConfig / getValue', function () {
- return testFile('config', 'config.json').then(res => {
- fs.writeFileSync(res.testFile, '// my comment\n{ "foo": "bar" }');
-
- let watcher = new ConfigWatcher<{ foo: string; }>(res.testFile);
-
- let config = watcher.getConfig();
- assert.ok(config);
- assert.equal(config.foo, 'bar');
- assert.ok(!watcher.hasParseErrors);
-
- watcher.dispose();
-
- return res.cleanUp();
- });
- });
-
- test('getConfig / getValue - broken JSON', function () {
- return testFile('config', 'config.json').then(res => {
- fs.writeFileSync(res.testFile, '// my comment\n "foo": "bar ... ');
-
- let watcher = new ConfigWatcher<{ foo: string; }>(res.testFile);
-
- let config = watcher.getConfig();
- assert.ok(config);
- assert.ok(!config.foo);
-
- assert.ok(watcher.hasParseErrors);
-
- watcher.dispose();
-
- return res.cleanUp();
- });
- });
-
- // test('watching', function (done) {
- // this.timeout(10000); // watching is timing intense
-
- // testFile('config', 'config.json').then(res => {
- // fs.writeFileSync(res.testFile, '// my comment\n{ "foo": "bar" }');
-
- // let watcher = new ConfigWatcher<{ foo: string; }>(res.testFile);
- // watcher.getConfig(); // ensure we are in sync
-
- // fs.writeFileSync(res.testFile, '// my comment\n{ "foo": "changed" }');
-
- // watcher.onDidUpdateConfiguration(event => {
- // assert.ok(event);
- // assert.equal(event.config.foo, 'changed');
- // assert.equal(watcher.getValue('foo'), 'changed');
-
- // watcher.dispose();
-
- // res.cleanUp().then(done, done);
- // });
- // }, done);
- // });
-
- // test('watching also works when file created later', function (done) {
- // this.timeout(10000); // watching is timing intense
-
- // testFile('config', 'config.json').then(res => {
- // let watcher = new ConfigWatcher<{ foo: string; }>(res.testFile);
- // watcher.getConfig(); // ensure we are in sync
-
- // fs.writeFileSync(res.testFile, '// my comment\n{ "foo": "changed" }');
-
- // watcher.onDidUpdateConfiguration(event => {
- // assert.ok(event);
- // assert.equal(event.config.foo, 'changed');
- // assert.equal(watcher.getValue('foo'), 'changed');
-
- // watcher.dispose();
-
- // res.cleanUp().then(done, done);
- // });
- // }, done);
- // });
-
- // test('watching detects the config file getting deleted', function (done) {
- // this.timeout(10000); // watching is timing intense
-
- // testFile('config', 'config.json').then(res => {
- // fs.writeFileSync(res.testFile, '// my comment\n{ "foo": "bar" }');
-
- // let watcher = new ConfigWatcher<{ foo: string; }>(res.testFile);
- // watcher.getConfig(); // ensure we are in sync
-
- // watcher.onDidUpdateConfiguration(event => {
- // assert.ok(event);
-
- // watcher.dispose();
-
- // res.cleanUp().then(done, done);
- // });
-
- // fs.unlinkSync(res.testFile);
- // }, done);
- // });
-
- test('reload', function (done) {
- testFile('config', 'config.json').then(res => {
- fs.writeFileSync(res.testFile, '// my comment\n{ "foo": "bar" }');
-
- let watcher = new ConfigWatcher<{ foo: string; }>(res.testFile, { changeBufferDelay: 100, onError: console.error, defaultConfig: { foo: 'bar' } });
- watcher.getConfig(); // ensure we are in sync
-
- fs.writeFileSync(res.testFile, '// my comment\n{ "foo": "changed" }');
-
- // still old values because change is not bubbling yet
- assert.equal(watcher.getConfig().foo, 'bar');
-
- // force a load from disk
- watcher.reload(config => {
- assert.equal(config.foo, 'changed');
- assert.equal(watcher.getConfig().foo, 'changed');
-
- watcher.dispose();
-
- res.cleanUp().then(done, done);
- });
- }, done);
- });
-});
\ No newline at end of file
diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts
index 4864b9255e..47773b730c 100644
--- a/src/vs/code/browser/workbench/workbench.ts
+++ b/src/vs/code/browser/workbench/workbench.ts
@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import { IWorkbenchConstructionOptions, create, URI, Event, Emitter, UriComponents, ICredentialsProvider, IURLCallbackProvider, IWorkspaceProvider, IWorkspace } from 'vs/workbench/workbench.web.api';
+import { IWorkbenchConstructionOptions, create, URI, Event, Emitter, UriComponents, ICredentialsProvider, IURLCallbackProvider, IWorkspaceProvider, IWorkspace, IApplicationLinkProvider, IApplicationLink } from 'vs/workbench/workbench.web.api';
import { generateUuid } from 'vs/base/common/uuid';
import { CancellationToken } from 'vs/base/common/cancellation';
import { streamToBuffer } from 'vs/base/common/buffer';
@@ -12,6 +12,10 @@ import { request } from 'vs/base/parts/request/browser/request';
import { isFolderToOpen, isWorkspaceToOpen } from 'vs/platform/windows/common/windows';
import { isEqual } from 'vs/base/common/resources';
import { isStandalone } from 'vs/base/browser/browser';
+import product from 'vs/platform/product/common/product';
+import { Schemas } from 'vs/base/common/network';
+import { posix } from 'vs/base/common/path';
+import { localize } from 'vs/nls';
interface ICredential {
service: string;
@@ -275,6 +279,39 @@ class WorkspaceProvider implements IWorkspaceProvider {
}
}
+class ApplicationLinkProvider {
+
+ private links: IApplicationLink[] | undefined = undefined;
+
+ constructor(workspace: IWorkspace) {
+ this.computeLink(workspace);
+ }
+
+ private computeLink(workspace: IWorkspace): void {
+ if (!workspace) {
+ return; // not for empty workspaces
+ }
+
+ const workspaceUri = isWorkspaceToOpen(workspace) ? workspace.workspaceUri : isFolderToOpen(workspace) ? workspace.folderUri : undefined;
+ if (workspaceUri) {
+ this.links = [{
+ uri: URI.from({
+ scheme: product.quality === 'stable' ? 'vscode' : 'vscode-insiders',
+ authority: Schemas.vscodeRemote,
+ path: posix.join(posix.sep, workspaceUri.authority, workspaceUri.path),
+ query: workspaceUri.query,
+ fragment: workspaceUri.fragment,
+ }),
+ label: localize('openInDesktop', "Open in Desktop")
+ }];
+ }
+ }
+
+ get provider(): IApplicationLinkProvider {
+ return () => this.links;
+ }
+}
+
(function () {
// Find config by checking for DOM
@@ -343,6 +380,7 @@ class WorkspaceProvider implements IWorkspaceProvider {
...config,
workspaceProvider: new WorkspaceProvider(workspace, payload),
urlCallbackProvider: new PollingURLCallbackProvider(),
- credentialsProvider: new LocalStorageCredentialsProvider()
+ credentialsProvider: new LocalStorageCredentialsProvider(),
+ applicationLinkProvider: new ApplicationLinkProvider(workspace).provider
});
})();
diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts
index d2457db13a..00ac8234cb 100644
--- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts
+++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts
@@ -120,10 +120,20 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat
const mainProcessService = new MainProcessService(server, mainRouter);
services.set(IMainProcessService, mainProcessService);
- const configurationService = new ConfigurationService(environmentService.settingsResource);
+ // Files
+ const fileService = new FileService(logService);
+ services.set(IFileService, fileService);
+ disposables.add(fileService);
+ const diskFileSystemProvider = new DiskFileSystemProvider(logService);
+ disposables.add(diskFileSystemProvider);
+ fileService.registerProvider(Schemas.file, diskFileSystemProvider);
+
+ // Configuration
+ const configurationService = new ConfigurationService(environmentService.settingsResource, fileService);
disposables.add(configurationService);
await configurationService.initialize();
+ // Storage
const storageService = new NativeStorageService(new GlobalStorageDatabaseChannelClient(mainProcessService.getChannel('storage')), logService, environmentService);
await storageService.initialize();
services.set(IStorageService, storageService);
@@ -136,19 +146,9 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat
services.set(IRequestService, new SyncDescriptor(RequestService));
services.set(ILoggerService, new SyncDescriptor(LoggerService));
-
const electronService = createChannelSender(mainProcessService.getChannel('electron'), { context: configuration.windowId });
services.set(IElectronService, electronService);
- // Files
- const fileService = new FileService(logService);
- services.set(IFileService, fileService);
- disposables.add(fileService);
-
- const diskFileSystemProvider = new DiskFileSystemProvider(logService);
- disposables.add(diskFileSystemProvider);
- fileService.registerProvider(Schemas.file, diskFileSystemProvider);
-
services.set(IDownloadService, new SyncDescriptor(DownloadService));
const instantiationService = new InstantiationService(services);
diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts
index 28911ff396..2a927b74be 100644
--- a/src/vs/code/electron-main/app.ts
+++ b/src/vs/code/electron-main/app.ts
@@ -55,7 +55,7 @@ import { MenubarMainService } from 'vs/platform/menubar/electron-main/menubarMai
import { RunOnceScheduler } from 'vs/base/common/async';
import { registerContextMenuListener } from 'vs/base/parts/contextmenu/electron-main/contextmenu';
import { homedir } from 'os';
-import { join, sep } from 'vs/base/common/path';
+import { join, sep, posix } from 'vs/base/common/path';
import { localize } from 'vs/nls';
import { Schemas } from 'vs/base/common/network';
import { SnapUpdateService } from 'vs/platform/update/electron-main/updateService.snap';
@@ -70,9 +70,6 @@ import { WorkspacesMainService, IWorkspacesMainService } from 'vs/platform/works
import { statSync } from 'fs';
import { DiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsIpc';
import { IDiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsService';
-import { FileService } from 'vs/platform/files/common/fileService';
-import { IFileService } from 'vs/platform/files/common/files';
-import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider';
import { ExtensionHostDebugBroadcastChannel } from 'vs/platform/debug/common/extensionHostDebugIpc';
import { IElectronMainService, ElectronMainService } from 'vs/platform/electron/electron-main/electronMainService';
import { ISharedProcessMainService, SharedProcessMainService } from 'vs/platform/ipc/electron-main/sharedProcessMainService';
@@ -434,12 +431,6 @@ export class CodeApplication extends Disposable {
private async createServices(machineId: string, trueMachineId: string | undefined, sharedProcess: SharedProcess, sharedProcessReady: Promise>): Promise {
const services = new ServiceCollection();
- const fileService = this._register(new FileService(this.logService));
- services.set(IFileService, fileService);
-
- const diskFileSystemProvider = this._register(new DiskFileSystemProvider(this.logService));
- fileService.registerProvider(Schemas.file, diskFileSystemProvider);
-
switch (process.platform) {
case 'win32':
services.set(IUpdateService, new SyncDescriptor(Win32UpdateService));
@@ -600,14 +591,42 @@ export class CodeApplication extends Disposable {
urlService.registerHandler({
async handleURL(uri: URI, options?: IOpenURLOptions): Promise {
- // Catch file URLs
- if (uri.authority === Schemas.file && !!uri.path) {
+ // Catch file/remote URLs
+ if ((uri.authority === Schemas.file || uri.authority === Schemas.vscodeRemote) && !!uri.path) {
const cli = assign(Object.create(null), environmentService.args);
- const urisToOpen = [{ fileUri: URI.file(uri.fsPath) }];
+ const urisToOpen: IWindowOpenable[] = [];
- windowsMainService.open({ context: OpenContext.API, cli, urisToOpen, gotoLineMode: true });
+ // File path
+ if (uri.authority === Schemas.file) {
+ // we configure as fileUri, but later validation will
+ // make sure to open as folder or workspace if possible
+ urisToOpen.push({ fileUri: URI.file(uri.fsPath) });
+ }
- return true;
+ // Remote path
+ else {
+ // Example conversion:
+ // From: vscode://vscode-remote/wsl+ubuntu/mnt/c/GitDevelopment/monaco
+ // To: vscode-remote://wsl+ubuntu/mnt/c/GitDevelopment/monaco
+ const secondSlash = uri.path.indexOf(posix.sep, 1 /* skip over the leading slash */);
+ if (secondSlash !== -1) {
+ const authority = uri.path.substring(1, secondSlash);
+ const path = uri.path.substring(secondSlash);
+ const remoteUri = URI.from({ scheme: Schemas.vscodeRemote, authority, path, query: uri.query, fragment: uri.fragment });
+
+ if (hasWorkspaceFileExtension(path)) {
+ urisToOpen.push({ workspaceUri: remoteUri });
+ } else {
+ urisToOpen.push({ folderUri: remoteUri });
+ }
+ }
+ }
+
+ if (urisToOpen.length > 0) {
+ windowsMainService.open({ context: OpenContext.API, cli, urisToOpen, gotoLineMode: true });
+
+ return true;
+ }
}
return false;
diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts
index 79f9874681..5681e2c83a 100644
--- a/src/vs/code/electron-main/main.ts
+++ b/src/vs/code/electron-main/main.ts
@@ -42,6 +42,10 @@ import { once } from 'vs/base/common/functional';
import { ISignService } from 'vs/platform/sign/common/sign';
import { SignService } from 'vs/platform/sign/node/signService';
import { DiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsIpc';
+import { FileService } from 'vs/platform/files/common/fileService';
+import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider';
+import { Schemas } from 'vs/base/common/network';
+import { IFileService } from 'vs/platform/files/common/files';
class ExpectedError extends Error {
readonly isExpected = true;
@@ -118,12 +122,16 @@ class CodeMain {
const environmentService = accessor.get(IEnvironmentService);
const logService = accessor.get(ILogService);
const lifecycleMainService = accessor.get(ILifecycleMainService);
+ const fileService = accessor.get(IFileService);
const configurationService = accessor.get(IConfigurationService);
const mainIpcServer = await this.doStartup(logService, environmentService, lifecycleMainService, instantiationService, true);
bufferLogService.logger = new SpdLogService('main', environmentService.logsPath, bufferLogService.getLevel());
- once(lifecycleMainService.onWillShutdown)(() => (configurationService as ConfigurationService).dispose());
+ once(lifecycleMainService.onWillShutdown)(() => {
+ fileService.dispose();
+ (configurationService as ConfigurationService).dispose();
+ });
return instantiationService.createInstance(CodeApplication, mainIpcServer, instanceEnvironment).startup();
});
@@ -143,7 +151,12 @@ class CodeMain {
process.once('exit', () => logService.dispose());
services.set(ILogService, logService);
- services.set(IConfigurationService, new ConfigurationService(environmentService.settingsResource));
+ const fileService = new FileService(logService);
+ services.set(IFileService, fileService);
+ const diskFileSystemProvider = new DiskFileSystemProvider(logService);
+ fileService.registerProvider(Schemas.file, diskFileSystemProvider);
+
+ services.set(IConfigurationService, new ConfigurationService(environmentService.settingsResource, fileService));
services.set(ILifecycleMainService, new SyncDescriptor(LifecycleMainService));
services.set(IStateService, new SyncDescriptor(StateService));
services.set(IRequestService, new SyncDescriptor(RequestMainService));
diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts
index 3d8474347b..ec69823e65 100644
--- a/src/vs/code/node/cliProcessMain.ts
+++ b/src/vs/code/node/cliProcessMain.ts
@@ -306,16 +306,6 @@ export async function main(argv: ParsedArgs): Promise {
await Promise.all([environmentService.appSettingsHome.fsPath, environmentService.extensionsPath]
.map((path): undefined | Promise => path ? mkdirp(path) : undefined));
- const configurationService = new ConfigurationService(environmentService.settingsResource);
- disposables.add(configurationService);
- await configurationService.initialize();
-
- services.set(IEnvironmentService, environmentService);
- services.set(ILogService, logService);
- services.set(IConfigurationService, configurationService);
- services.set(IStateService, new SyncDescriptor(StateService));
- services.set(IProductService, { _serviceBrand: undefined, ...product });
-
// Files
const fileService = new FileService(logService);
disposables.add(fileService);
@@ -325,6 +315,16 @@ export async function main(argv: ParsedArgs): Promise {
disposables.add(diskFileSystemProvider);
fileService.registerProvider(Schemas.file, diskFileSystemProvider);
+ const configurationService = new ConfigurationService(environmentService.settingsResource, fileService);
+ disposables.add(configurationService);
+ await configurationService.initialize();
+
+ services.set(IEnvironmentService, environmentService);
+ services.set(ILogService, logService);
+ services.set(IConfigurationService, configurationService);
+ services.set(IStateService, new SyncDescriptor(StateService));
+ services.set(IProductService, { _serviceBrand: undefined, ...product });
+
const instantiationService: IInstantiationService = new InstantiationService(services);
return instantiationService.invokeFunction(async accessor => {
diff --git a/src/vs/editor/browser/services/openerService.ts b/src/vs/editor/browser/services/openerService.ts
index 772e75ae42..16cffd848d 100644
--- a/src/vs/editor/browser/services/openerService.ts
+++ b/src/vs/editor/browser/services/openerService.ts
@@ -103,7 +103,15 @@ export class OpenerService implements IOpenerService {
// Default external opener is going through window.open()
this._externalOpener = {
openExternal: href => {
- dom.windowOpenNoOpener(href);
+ // ensure to open HTTP/HTTPS links into new windows
+ // to not trigger a navigation. Any other link is
+ // safe to be set as HREF to prevent a blank window
+ // from opening.
+ if (matchesScheme(href, Schemas.http) || matchesScheme(href, Schemas.https)) {
+ dom.windowOpenNoOpener(href);
+ } else {
+ window.location.href = href;
+ }
return Promise.resolve(true);
}
};
diff --git a/src/vs/editor/browser/view/viewImpl.ts b/src/vs/editor/browser/view/viewImpl.ts
index 77c6b2d307..0330103d59 100644
--- a/src/vs/editor/browser/view/viewImpl.ts
+++ b/src/vs/editor/browser/view/viewImpl.ts
@@ -118,7 +118,7 @@ export class View extends ViewEventHandler {
this._context = new ViewContext(configuration, themeService.getTheme(), model, this.eventDispatcher);
this._register(themeService.onThemeChange(theme => {
- this._context.theme = theme;
+ this._context.theme.update(theme);
this.eventDispatcher.emit(new viewEvents.ViewThemeChangedEvent());
this.render(true, false);
}));
diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts
index bf3cea82f9..a743f70487 100644
--- a/src/vs/editor/browser/viewParts/minimap/minimap.ts
+++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts
@@ -22,11 +22,11 @@ import { MinimapCharRenderer } from 'vs/editor/browser/viewParts/minimap/minimap
import { Constants } from 'vs/editor/browser/viewParts/minimap/minimapCharSheet';
import { MinimapTokensColorTracker } from 'vs/editor/common/viewModel/minimapTokensColorTracker';
import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext';
-import { ViewContext } from 'vs/editor/common/view/viewContext';
+import { ViewContext, EditorTheme } from 'vs/editor/common/view/viewContext';
import * as viewEvents from 'vs/editor/common/view/viewEvents';
import { ViewLineData } from 'vs/editor/common/viewModel/viewModel';
import { minimapSelection, scrollbarShadow, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, minimapBackground } from 'vs/platform/theme/common/colorRegistry';
-import { registerThemingParticipant, ITheme } from 'vs/platform/theme/common/themeService';
+import { registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { ModelDecorationMinimapOptions } from 'vs/editor/common/model/textModel';
import { Selection } from 'vs/editor/common/core/selection';
import { Color } from 'vs/base/common/color';
@@ -109,7 +109,7 @@ class MinimapOptions {
public readonly backgroundColor: RGBA8;
- constructor(configuration: IConfiguration, theme: ITheme, tokensColorTracker: MinimapTokensColorTracker) {
+ constructor(configuration: IConfiguration, theme: EditorTheme, tokensColorTracker: MinimapTokensColorTracker) {
const options = configuration.options;
const pixelRatio = options.get(EditorOption.pixelRatio);
const layoutInfo = options.get(EditorOption.layoutInfo);
@@ -137,7 +137,7 @@ class MinimapOptions {
this.backgroundColor = MinimapOptions._getMinimapBackground(theme, tokensColorTracker);
}
- private static _getMinimapBackground(theme: ITheme, tokensColorTracker: MinimapTokensColorTracker): RGBA8 {
+ private static _getMinimapBackground(theme: EditorTheme, tokensColorTracker: MinimapTokensColorTracker): RGBA8 {
const themeColor = theme.getColor(minimapBackground);
if (themeColor) {
return new RGBA8(themeColor.rgba.r, themeColor.rgba.g, themeColor.rgba.b, themeColor.rgba.a);
diff --git a/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts b/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts
index fe2a89b63c..efedcc7411 100644
--- a/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts
+++ b/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts
@@ -12,9 +12,8 @@ import { IConfiguration } from 'vs/editor/common/editorCommon';
import { TokenizationRegistry } from 'vs/editor/common/modes';
import { editorCursorForeground, editorOverviewRulerBorder } from 'vs/editor/common/view/editorColorRegistry';
import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext';
-import { ViewContext } from 'vs/editor/common/view/viewContext';
+import { ViewContext, EditorTheme } from 'vs/editor/common/view/viewContext';
import * as viewEvents from 'vs/editor/common/view/viewEvents';
-import { ITheme } from 'vs/platform/theme/common/themeService';
import { EditorOption } from 'vs/editor/common/config/editorOptions';
class Settings {
@@ -42,7 +41,7 @@ class Settings {
public readonly x: number[];
public readonly w: number[];
- constructor(config: IConfiguration, theme: ITheme) {
+ constructor(config: IConfiguration, theme: EditorTheme) {
const options = config.options;
this.lineHeight = options.get(EditorOption.lineHeight);
this.pixelRatio = options.get(EditorOption.pixelRatio);
diff --git a/src/vs/editor/common/model/textModel.ts b/src/vs/editor/common/model/textModel.ts
index dd438c146e..edabe24555 100644
--- a/src/vs/editor/common/model/textModel.ts
+++ b/src/vs/editor/common/model/textModel.ts
@@ -29,12 +29,13 @@ import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageCo
import { NULL_LANGUAGE_IDENTIFIER } from 'vs/editor/common/modes/nullMode';
import { ignoreBracketsInToken } from 'vs/editor/common/modes/supports';
import { BracketsUtils, RichEditBracket, RichEditBrackets } from 'vs/editor/common/modes/supports/richEditBrackets';
-import { ITheme, ThemeColor } from 'vs/platform/theme/common/themeService';
+import { ThemeColor } from 'vs/platform/theme/common/themeService';
import { withUndefinedAsNull } from 'vs/base/common/types';
import { VSBufferReadableStream, VSBuffer } from 'vs/base/common/buffer';
import { TokensStore, MultilineTokens, countEOL, MultilineTokens2, TokensStore2 } from 'vs/editor/common/model/tokensStore';
import { Color } from 'vs/base/common/color';
import { Constants } from 'vs/base/common/uint';
+import { EditorTheme } from 'vs/editor/common/view/viewContext';
function createTextBufferBuilder() {
return new PieceTreeTextBufferBuilder();
@@ -2945,7 +2946,7 @@ export class ModelDecorationOverviewRulerOptions extends DecorationOptions {
this.position = (typeof options.position === 'number' ? options.position : model.OverviewRulerLane.Center);
}
- public getColor(theme: ITheme): string {
+ public getColor(theme: EditorTheme): string {
if (!this._resolvedColor) {
if (theme.type !== 'light' && this.darkColor) {
this._resolvedColor = this._resolveColor(this.darkColor, theme);
@@ -2960,7 +2961,7 @@ export class ModelDecorationOverviewRulerOptions extends DecorationOptions {
this._resolvedColor = null;
}
- private _resolveColor(color: string | ThemeColor, theme: ITheme): string {
+ private _resolveColor(color: string | ThemeColor, theme: EditorTheme): string {
if (typeof color === 'string') {
return color;
}
@@ -2982,7 +2983,7 @@ export class ModelDecorationMinimapOptions extends DecorationOptions {
this.position = options.position;
}
- public getColor(theme: ITheme): Color | undefined {
+ public getColor(theme: EditorTheme): Color | undefined {
if (!this._resolvedColor) {
if (theme.type !== 'light' && this.darkColor) {
this._resolvedColor = this._resolveColor(this.darkColor, theme);
@@ -2998,7 +2999,7 @@ export class ModelDecorationMinimapOptions extends DecorationOptions {
this._resolvedColor = undefined;
}
- private _resolveColor(color: string | ThemeColor, theme: ITheme): Color | undefined {
+ private _resolveColor(color: string | ThemeColor, theme: EditorTheme): Color | undefined {
if (typeof color === 'string') {
return Color.fromHex(color);
}
diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts
index 3beda583a2..282f0641f2 100644
--- a/src/vs/editor/common/modes.ts
+++ b/src/vs/editor/common/modes.ts
@@ -615,7 +615,9 @@ export interface CodeActionProvider {
/**
* Optional list of CodeActionKinds that this provider returns.
*/
- providedCodeActionKinds?: ReadonlyArray;
+ readonly providedCodeActionKinds?: ReadonlyArray;
+
+ readonly documentation?: ReadonlyArray<{ readonly kind: string, readonly command: Command }>;
/**
* @internal
diff --git a/src/vs/editor/common/view/viewContext.ts b/src/vs/editor/common/view/viewContext.ts
index 1d9a57383d..3e31d7a1fc 100644
--- a/src/vs/editor/common/view/viewContext.ts
+++ b/src/vs/editor/common/view/viewContext.ts
@@ -7,7 +7,30 @@ import { IConfiguration } from 'vs/editor/common/editorCommon';
import { ViewEventDispatcher } from 'vs/editor/common/view/viewEventDispatcher';
import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler';
import { IViewLayout, IViewModel } from 'vs/editor/common/viewModel/viewModel';
-import { ITheme } from 'vs/platform/theme/common/themeService';
+import { ITheme, ThemeType } from 'vs/platform/theme/common/themeService';
+import { ColorIdentifier } from 'vs/platform/theme/common/colorRegistry';
+import { Color } from 'vs/base/common/color';
+
+export class EditorTheme {
+
+ private _theme: ITheme;
+
+ public get type(): ThemeType {
+ return this._theme.type;
+ }
+
+ constructor(theme: ITheme) {
+ this._theme = theme;
+ }
+
+ public update(theme: ITheme): void {
+ this._theme = theme;
+ }
+
+ public getColor(color: ColorIdentifier): Color | undefined {
+ return this._theme.getColor(color);
+ }
+}
export class ViewContext {
@@ -15,8 +38,7 @@ export class ViewContext {
public readonly model: IViewModel;
public readonly viewLayout: IViewLayout;
public readonly privateViewEventBus: ViewEventDispatcher;
-
- public theme: ITheme; // will be updated
+ public readonly theme: EditorTheme;
constructor(
configuration: IConfiguration,
@@ -25,7 +47,7 @@ export class ViewContext {
privateViewEventBus: ViewEventDispatcher
) {
this.configuration = configuration;
- this.theme = theme;
+ this.theme = new EditorTheme(theme);
this.model = model;
this.viewLayout = model.viewLayout;
this.privateViewEventBus = privateViewEventBus;
diff --git a/src/vs/editor/common/viewModel/splitLinesCollection.ts b/src/vs/editor/common/viewModel/splitLinesCollection.ts
index edffb78a46..69d268457d 100644
--- a/src/vs/editor/common/viewModel/splitLinesCollection.ts
+++ b/src/vs/editor/common/viewModel/splitLinesCollection.ts
@@ -13,9 +13,9 @@ import { ModelDecorationOptions, ModelDecorationOverviewRulerOptions } from 'vs/
import * as viewEvents from 'vs/editor/common/view/viewEvents';
import { PrefixSumIndexOfResult } from 'vs/editor/common/viewModel/prefixSumComputer';
import { ICoordinatesConverter, IOverviewRulerDecorations, ViewLineData } from 'vs/editor/common/viewModel/viewModel';
-import { ITheme } from 'vs/platform/theme/common/themeService';
import { IDisposable } from 'vs/base/common/lifecycle';
import { FontInfo } from 'vs/editor/common/config/fontInfo';
+import { EditorTheme } from 'vs/editor/common/view/viewContext';
export class OutputPosition {
outputLineIndex: number;
@@ -131,7 +131,7 @@ export interface IViewModelLinesCollection extends IDisposable {
getViewLineData(viewLineNumber: number): ViewLineData;
getViewLinesData(viewStartLineNumber: number, viewEndLineNumber: number, needed: boolean[]): Array;
- getAllOverviewRulerDecorations(ownerId: number, filterOutValidation: boolean, theme: ITheme): IOverviewRulerDecorations;
+ getAllOverviewRulerDecorations(ownerId: number, filterOutValidation: boolean, theme: EditorTheme): IOverviewRulerDecorations;
getDecorationsInRange(range: Range, ownerId: number, filterOutValidation: boolean): IModelDecoration[];
}
@@ -940,7 +940,7 @@ export class SplitLinesCollection implements IViewModelLinesCollection {
return this.lines[lineIndex].getViewLineNumberOfModelPosition(deltaLineNumber, this.model.getLineMaxColumn(lineIndex + 1));
}
- public getAllOverviewRulerDecorations(ownerId: number, filterOutValidation: boolean, theme: ITheme): IOverviewRulerDecorations {
+ public getAllOverviewRulerDecorations(ownerId: number, filterOutValidation: boolean, theme: EditorTheme): IOverviewRulerDecorations {
const decorations = this.model.getOverviewRulerDecorations(ownerId, filterOutValidation);
const result = new OverviewRulerDecorations();
for (const decoration of decorations) {
@@ -1561,7 +1561,7 @@ export class IdentityLinesCollection implements IViewModelLinesCollection {
return result;
}
- public getAllOverviewRulerDecorations(ownerId: number, filterOutValidation: boolean, theme: ITheme): IOverviewRulerDecorations {
+ public getAllOverviewRulerDecorations(ownerId: number, filterOutValidation: boolean, theme: EditorTheme): IOverviewRulerDecorations {
const decorations = this.model.getOverviewRulerDecorations(ownerId, filterOutValidation);
const result = new OverviewRulerDecorations();
for (const decoration of decorations) {
diff --git a/src/vs/editor/common/viewModel/viewModel.ts b/src/vs/editor/common/viewModel/viewModel.ts
index ac08c4318a..edd17ac3e8 100644
--- a/src/vs/editor/common/viewModel/viewModel.ts
+++ b/src/vs/editor/common/viewModel/viewModel.ts
@@ -14,7 +14,7 @@ import { EndOfLinePreference, IActiveIndentGuideInfo, IModelDecorationOptions, T
import { IViewEventListener } from 'vs/editor/common/view/viewEvents';
import { IPartialViewLinesViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData';
import { IEditorWhitespace, IWhitespaceChangeAccessor } from 'vs/editor/common/viewLayout/linesLayout';
-import { ITheme } from 'vs/platform/theme/common/themeService';
+import { EditorTheme } from 'vs/editor/common/view/viewContext';
export interface IViewWhitespaceViewportData {
readonly id: string;
@@ -127,7 +127,7 @@ export interface IViewModel {
getLineMaxColumn(lineNumber: number): number;
getLineFirstNonWhitespaceColumn(lineNumber: number): number;
getLineLastNonWhitespaceColumn(lineNumber: number): number;
- getAllOverviewRulerDecorations(theme: ITheme): IOverviewRulerDecorations;
+ getAllOverviewRulerDecorations(theme: EditorTheme): IOverviewRulerDecorations;
invalidateOverviewRulerColorCache(): void;
invalidateMinimapColorCache(): void;
getValueInRange(range: Range, eol: EndOfLinePreference): string;
diff --git a/src/vs/editor/common/viewModel/viewModelImpl.ts b/src/vs/editor/common/viewModel/viewModelImpl.ts
index dcb77051f9..bca4ad2bd9 100644
--- a/src/vs/editor/common/viewModel/viewModelImpl.ts
+++ b/src/vs/editor/common/viewModel/viewModelImpl.ts
@@ -21,9 +21,9 @@ import { ViewLayout } from 'vs/editor/common/viewLayout/viewLayout';
import { IViewModelLinesCollection, IdentityLinesCollection, SplitLinesCollection, ILineBreaksComputerFactory } from 'vs/editor/common/viewModel/splitLinesCollection';
import { ICoordinatesConverter, IOverviewRulerDecorations, IViewModel, MinimapLinesRenderingData, ViewLineData, ViewLineRenderingData, ViewModelDecoration } from 'vs/editor/common/viewModel/viewModel';
import { ViewModelDecorations } from 'vs/editor/common/viewModel/viewModelDecorations';
-import { ITheme } from 'vs/platform/theme/common/themeService';
import { RunOnceScheduler } from 'vs/base/common/async';
import * as platform from 'vs/base/common/platform';
+import { EditorTheme } from 'vs/editor/common/view/viewContext';
const USE_IDENTITY_LINES_COLLECTION = true;
@@ -595,7 +595,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
);
}
- public getAllOverviewRulerDecorations(theme: ITheme): IOverviewRulerDecorations {
+ public getAllOverviewRulerDecorations(theme: EditorTheme): IOverviewRulerDecorations {
return this.lines.getAllOverviewRulerDecorations(this.editorId, filterValidationDecorations(this.configuration.options), theme);
}
diff --git a/src/vs/editor/contrib/codeAction/codeAction.ts b/src/vs/editor/contrib/codeAction/codeAction.ts
index 87a66792e6..d4aa4d0fd1 100644
--- a/src/vs/editor/contrib/codeAction/codeAction.ts
+++ b/src/vs/editor/contrib/codeAction/codeAction.ts
@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import { equals, flatten, isNonEmptyArray, mergeSort } from 'vs/base/common/arrays';
+import { equals, flatten, isNonEmptyArray, mergeSort, coalesce } from 'vs/base/common/arrays';
import { CancellationToken } from 'vs/base/common/cancellation';
import { illegalArgument, isPromiseCanceledError, onUnexpectedExternalError } from 'vs/base/common/errors';
import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
@@ -27,6 +27,8 @@ export interface CodeActionSet extends IDisposable {
readonly validActions: readonly modes.CodeAction[];
readonly allActions: readonly modes.CodeAction[];
readonly hasAutoFix: boolean;
+
+ readonly documentation: readonly modes.Command[];
}
class ManagedCodeActionSet extends Disposable implements CodeActionSet {
@@ -48,7 +50,11 @@ class ManagedCodeActionSet extends Disposable implements CodeActionSet {
public readonly validActions: readonly modes.CodeAction[];
public readonly allActions: readonly modes.CodeAction[];
- public constructor(actions: readonly modes.CodeAction[], disposables: DisposableStore) {
+ public constructor(
+ actions: readonly modes.CodeAction[],
+ public readonly documentation: readonly modes.Command[],
+ disposables: DisposableStore,
+ ) {
super();
this._register(disposables);
this.allActions = mergeSort([...actions], ManagedCodeActionSet.codeActionsComparator);
@@ -80,17 +86,23 @@ export function getCodeActions(
const promises = providers.map(async provider => {
try {
const providedCodeActions = await provider.provideCodeActions(model, rangeOrSelection, codeActionContext, cts.token);
- if (cts.token.isCancellationRequested || !providedCodeActions) {
- return [];
+ if (providedCodeActions) {
+ disposables.add(providedCodeActions);
}
- disposables.add(providedCodeActions);
- return providedCodeActions.actions.filter(action => action && filtersAction(filter, action));
+
+ if (cts.token.isCancellationRequested) {
+ return { actions: [] as modes.CodeAction[], documentation: undefined };
+ }
+
+ const filteredActions = (providedCodeActions?.actions || []).filter(action => action && filtersAction(filter, action));
+ const documentation = getDocumentation(provider, filteredActions, filter.include);
+ return { actions: filteredActions, documentation };
} catch (err) {
if (isPromiseCanceledError(err)) {
throw err;
}
onUnexpectedExternalError(err);
- return [];
+ return { actions: [] as modes.CodeAction[], documentation: undefined };
}
});
@@ -101,9 +113,11 @@ export function getCodeActions(
}
});
- return Promise.all(promises)
- .then(flatten)
- .then(actions => new ManagedCodeActionSet(actions, disposables))
+ return Promise.all(promises).then(actions => {
+ const allActions = flatten(actions.map(x => x.actions));
+ const allDocumentation = coalesce(actions.map(x => x.documentation));
+ return new ManagedCodeActionSet(allActions, allDocumentation, disposables);
+ })
.finally(() => {
listener.dispose();
cts.dispose();
@@ -125,6 +139,52 @@ function getCodeActionProviders(
});
}
+function getDocumentation(
+ provider: modes.CodeActionProvider,
+ providedCodeActions: readonly modes.CodeAction[],
+ only?: CodeActionKind
+): modes.Command | undefined {
+ if (!provider.documentation) {
+ return undefined;
+ }
+
+ const documentation = provider.documentation.map(entry => ({ kind: new CodeActionKind(entry.kind), command: entry.command }));
+
+ if (only) {
+ let currentBest: { readonly kind: CodeActionKind, readonly command: modes.Command } | undefined;
+ for (const entry of documentation) {
+ if (entry.kind.contains(only)) {
+ if (!currentBest) {
+ currentBest = entry;
+ } else {
+ // Take best match
+ if (currentBest.kind.contains(entry.kind)) {
+ currentBest = entry;
+ }
+ }
+ }
+ }
+ if (currentBest) {
+ return currentBest?.command;
+ }
+ }
+
+ // Otherwise, check to see if any of the provided actions match.
+ for (const action of providedCodeActions) {
+ if (!action.kind) {
+ continue;
+ }
+
+ for (const entry of documentation) {
+ if (entry.kind.contains(new CodeActionKind(action.kind))) {
+ return entry.command;
+ }
+ }
+ }
+
+ return undefined;
+}
+
registerLanguageCommand('_executeCodeActionProvider', async function (accessor, args): Promise> {
const { resource, rangeOrSelection, kind } = args;
if (!(resource instanceof URI)) {
diff --git a/src/vs/editor/contrib/codeAction/codeActionMenu.ts b/src/vs/editor/contrib/codeAction/codeActionMenu.ts
index dbe87f123d..a073034b41 100644
--- a/src/vs/editor/contrib/codeAction/codeActionMenu.ts
+++ b/src/vs/editor/contrib/codeAction/codeActionMenu.ts
@@ -14,9 +14,9 @@ import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { IPosition, Position } from 'vs/editor/common/core/position';
import { ScrollType } from 'vs/editor/common/editorCommon';
-import { CodeAction, CodeActionProviderRegistry } from 'vs/editor/common/modes';
+import { CodeAction, CodeActionProviderRegistry, Command } from 'vs/editor/common/modes';
import { codeActionCommandId, CodeActionSet, fixAllCommandId, organizeImportsCommandId, refactorCommandId, sourceActionCommandId } from 'vs/editor/contrib/codeAction/codeAction';
-import { CodeActionAutoApply, CodeActionCommandArgs, CodeActionKind, CodeActionTrigger } from 'vs/editor/contrib/codeAction/types';
+import { CodeActionAutoApply, CodeActionCommandArgs, CodeActionTrigger, CodeActionKind } from 'vs/editor/contrib/codeAction/types';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem';
@@ -84,7 +84,7 @@ export class CodeActionMenu extends Disposable {
this._visible = true;
this._showingActions.value = codeActions;
- const menuActions = this.getMenuActions(trigger, actionsToShow);
+ const menuActions = this.getMenuActions(trigger, actionsToShow, codeActions.documentation);
const anchor = Position.isIPosition(at) ? this._toCoords(at) : at || { x: 0, y: 0 };
const resolver = this._keybindingResolver.getResolver();
@@ -101,28 +101,34 @@ export class CodeActionMenu extends Disposable {
});
}
- private getMenuActions(trigger: CodeActionTrigger, actionsToShow: readonly CodeAction[]): IAction[] {
+ private getMenuActions(
+ trigger: CodeActionTrigger,
+ actionsToShow: readonly CodeAction[],
+ documentation: readonly Command[]
+ ): IAction[] {
const toCodeActionAction = (action: CodeAction): CodeActionAction => new CodeActionAction(action, () => this._delegate.onSelectCodeAction(action));
const result: IAction[] = actionsToShow
.map(toCodeActionAction);
+ const allDocumentation: Command[] = [...documentation];
const model = this._editor.getModel();
if (model && result.length) {
for (const provider of CodeActionProviderRegistry.all(model)) {
if (provider._getAdditionalMenuItems) {
- const items = provider._getAdditionalMenuItems({ trigger: trigger.type, only: trigger.filter?.include?.value }, actionsToShow);
- if (items.length) {
- result.push(new Separator(), ...items.map(command => toCodeActionAction({
- title: command.title,
- command: command,
- })));
- }
+ allDocumentation.push(...provider._getAdditionalMenuItems({ trigger: trigger.type, only: trigger.filter?.include?.value }, actionsToShow));
}
}
}
+ if (allDocumentation.length) {
+ result.push(new Separator(), ...allDocumentation.map(command => toCodeActionAction({
+ title: command.title,
+ command: command,
+ })));
+ }
+
return result;
}
diff --git a/src/vs/editor/contrib/gotoSymbol/goToSymbol.ts b/src/vs/editor/contrib/gotoSymbol/goToSymbol.ts
index a4249e9f42..c3a4eeb2b1 100644
--- a/src/vs/editor/contrib/gotoSymbol/goToSymbol.ts
+++ b/src/vs/editor/contrib/gotoSymbol/goToSymbol.ts
@@ -3,7 +3,6 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import { flatten, coalesce } from 'vs/base/common/arrays';
import { CancellationToken } from 'vs/base/common/cancellation';
import { onUnexpectedExternalError } from 'vs/base/common/errors';
import { registerModelAndPositionCommand } from 'vs/editor/browser/editorExtensions';
@@ -28,9 +27,18 @@ function getLocationLinks(
return undefined;
});
});
- return Promise.all(promises)
- .then(flatten)
- .then(coalesce);
+
+ return Promise.all(promises).then(values => {
+ const result: LocationLink[] = [];
+ for (let value of values) {
+ if (Array.isArray(value)) {
+ result.push(...value);
+ } else if (value) {
+ result.push(value);
+ }
+ }
+ return result;
+ });
}
diff --git a/src/vs/editor/contrib/gotoSymbol/peek/referencesWidget.ts b/src/vs/editor/contrib/gotoSymbol/peek/referencesWidget.ts
index 318f804d27..6248016047 100644
--- a/src/vs/editor/contrib/gotoSymbol/peek/referencesWidget.ts
+++ b/src/vs/editor/contrib/gotoSymbol/peek/referencesWidget.ts
@@ -181,6 +181,8 @@ export interface SelectionEvent {
readonly element?: Location;
}
+class ReferencesTree extends WorkbenchAsyncDataTree { }
+
/**
* ZoneWidget that is shown inside the editor
*/
@@ -195,7 +197,7 @@ export class ReferenceWidget extends peekView.PeekViewWidget {
private readonly _onDidSelectReference = new Emitter();
readonly onDidSelectReference = this._onDidSelectReference.event;
- private _tree!: WorkbenchAsyncDataTree;
+ private _tree!: ReferencesTree;
private _treeContainer!: HTMLElement;
private _splitView!: SplitView;
private _preview!: ICodeEditor;
@@ -316,8 +318,8 @@ export class ReferenceWidget extends peekView.PeekViewWidget {
listBackground: peekView.peekViewResultsBackground
}
};
- this._tree = this._instantiationService.createInstance>(
- WorkbenchAsyncDataTree,
+ this._tree = this._instantiationService.createInstance(
+ ReferencesTree,
'ReferencesWidget',
this._treeContainer,
new Delegate(),
diff --git a/src/vs/editor/contrib/parameterHints/parameterHints.css b/src/vs/editor/contrib/parameterHints/parameterHints.css
index 9d3c535eaa..adb6ec6b11 100644
--- a/src/vs/editor/contrib/parameterHints/parameterHints.css
+++ b/src/vs/editor/contrib/parameterHints/parameterHints.css
@@ -33,6 +33,7 @@
.monaco-editor .parameter-hints-widget .monaco-scrollable-element,
.monaco-editor .parameter-hints-widget .body {
display: flex;
+ flex: 1;
flex-direction: column;
min-height: 100%;
}
diff --git a/src/vs/editor/contrib/suggest/media/suggest.css b/src/vs/editor/contrib/suggest/media/suggest.css
index 852b9f65d4..aff07a2b39 100644
--- a/src/vs/editor/contrib/suggest/media/suggest.css
+++ b/src/vs/editor/contrib/suggest/media/suggest.css
@@ -137,8 +137,6 @@
border-bottom-style: solid;
padding: 0 8px 0 4px;
-
- box-shadow: 0 -.5px 3px #ddd;
}
.monaco-editor .suggest-widget.list-right.docs-side > .suggest-status-bar {
@@ -177,6 +175,11 @@
opacity: 0.7;
}
+.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .left > .signature-label {
+ overflow: auto;
+ text-overflow: ellipsis;
+}
+
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .left > .qualifier-label {
margin-left: 4px;
opacity: 0.4;
@@ -213,8 +216,8 @@
/** Details: if using CompletionItemLabel#details, always show **/
-.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right.always-show-details > .details-label,
-.monaco-editor .suggest-widget.docs-side .monaco-list .monaco-list-row.focused > .contents > .main > .right.always-show-details > .details-label {
+.monaco-editor .suggest-widget .monaco-list .monaco-list-row:not(.string-label) > .contents > .main > .right > .details-label,
+.monaco-editor .suggest-widget.docs-side .monaco-list .monaco-list-row.focused:not(.string-label) > .contents > .main > .right > .details-label {
display: inline;
}
@@ -228,6 +231,12 @@
overflow: hidden;
}
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .left > .monaco-icon-label {
+ flex-shrink: 0;
+}
+.monaco-editor .suggest-widget .monaco-list .monaco-list-row:not(.string-label) > .contents > .main > .left > .monaco-icon-label {
+ max-width: 80%;
+}
+.monaco-editor .suggest-widget .monaco-list .monaco-list-row.string-label > .contents > .main > .left > .monaco-icon-label {
flex-shrink: 1;
}
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right {
@@ -253,11 +262,11 @@
}
/** Do NOT display ReadMore when using plain CompletionItemLabel (details/documentation might not be resolved) **/
-.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right:not(.always-show-details) > .readMore {
+.monaco-editor .suggest-widget .monaco-list .monaco-list-row.string-label > .contents > .main > .right > .readMore {
display: none;
}
/** Focused item can show ReadMore, but can't when docs is side/below **/
-.monaco-editor .suggest-widget .monaco-list .monaco-list-row.focused > .contents > .main > .right:not(.always-show-details) > .readMore {
+.monaco-editor .suggest-widget .monaco-list .monaco-list-row.focused.string-label > .contents > .main > .right > .readMore {
display: inline-block;
}
diff --git a/src/vs/editor/contrib/suggest/media/suggestStatusBar.css b/src/vs/editor/contrib/suggest/media/suggestStatusBar.css
index eb05ba733a..b21d5ecc6f 100644
--- a/src/vs/editor/contrib/suggest/media/suggestStatusBar.css
+++ b/src/vs/editor/contrib/suggest/media/suggestStatusBar.css
@@ -26,7 +26,7 @@
}
.monaco-editor .suggest-widget.with-status-bar .monaco-list .monaco-list-row > .contents > .main > .right > .readMore,
-.monaco-editor .suggest-widget.with-status-bar .monaco-list .monaco-list-row.focused > .contents > .main > .right:not(.always-show-details) > .readMore {
+.monaco-editor .suggest-widget.with-status-bar .monaco-list .monaco-list-row.focused.string-label > .contents > .main > .right > .readMore {
display: none;
}
diff --git a/src/vs/editor/contrib/suggest/suggest.ts b/src/vs/editor/contrib/suggest/suggest.ts
index da63de0b90..b19693cbe4 100644
--- a/src/vs/editor/contrib/suggest/suggest.ts
+++ b/src/vs/editor/contrib/suggest/suggest.ts
@@ -26,6 +26,7 @@ export const Context = {
MakesTextEdit: new RawContextKey('suggestionMakesTextEdit', true),
AcceptSuggestionsOnEnter: new RawContextKey('acceptSuggestionOnEnter', true),
HasInsertAndReplaceRange: new RawContextKey('suggestionHasInsertAndReplaceRange', false),
+ CanResolve: new RawContextKey('suggestionCanResolve', false),
};
export const suggestWidgetStatusbarMenu = new MenuId('suggestWidgetStatusBar');
@@ -34,9 +35,12 @@ export class CompletionItem {
_brand!: 'ISuggestionItem';
+ private static readonly _defaultResolve = () => Promise.resolve();
+
readonly resolve: (token: CancellationToken) => Promise;
isResolved: boolean = false;
+
//
readonly editStart: IPosition;
readonly editInsertEnd: IPosition;
@@ -87,13 +91,13 @@ export class CompletionItem {
// create the suggestion resolver
const { resolveCompletionItem } = provider;
if (typeof resolveCompletionItem !== 'function') {
- this.resolve = () => Promise.resolve();
+ this.resolve = CompletionItem._defaultResolve;
this.isResolved = true;
} else {
let cached: Promise | undefined;
this.resolve = (token) => {
if (!cached) {
- cached = Promise.resolve(resolveCompletionItem.call(provider, model, position, completion, token)).then(value => {
+ cached = Promise.resolve(resolveCompletionItem.call(provider, model, Position.lift(position), completion, token)).then(value => {
assign(completion, value);
this.isResolved = true;
}, err => {
diff --git a/src/vs/editor/contrib/suggest/suggestController.ts b/src/vs/editor/contrib/suggest/suggestController.ts
index 691ae91a3f..6ceb0eb162 100644
--- a/src/vs/editor/contrib/suggest/suggestController.ts
+++ b/src/vs/editor/contrib/suggest/suggestController.ts
@@ -139,10 +139,12 @@ export class SuggestController implements IEditorContribution {
// Wire up makes text edit context key
const ctxMakesTextEdit = SuggestContext.MakesTextEdit.bindTo(this._contextKeyService);
const ctxHasInsertAndReplace = SuggestContext.HasInsertAndReplaceRange.bindTo(this._contextKeyService);
+ const ctxCanResolve = SuggestContext.CanResolve.bindTo(this._contextKeyService);
this._toDispose.add(toDisposable(() => {
ctxMakesTextEdit.reset();
ctxHasInsertAndReplace.reset();
+ ctxCanResolve.reset();
}));
this._toDispose.add(widget.onDidFocus(({ item }) => {
@@ -172,6 +174,9 @@ export class SuggestController implements IEditorContribution {
// (ctx: hasInsertAndReplaceRange)
ctxHasInsertAndReplace.set(!Position.equals(item.editInsertEnd, item.editReplaceEnd));
+
+ // (ctx: canResolve)
+ ctxCanResolve.set(Boolean(item.provider.resolveCompletionItem) || Boolean(item.completion.documentation) || item.completion.detail !== item.completion.label);
}));
this._toDispose.add(widget.onDetailsKeyDown(e => {
@@ -695,13 +700,13 @@ registerEditorCommand(new SuggestCommand({
menuId: suggestWidgetStatusbarMenu,
group: 'right',
order: 1,
- when: SuggestContext.DetailsVisible,
+ when: ContextKeyExpr.and(SuggestContext.DetailsVisible, SuggestContext.CanResolve),
title: nls.localize('detail.more', "show less")
}, {
menuId: suggestWidgetStatusbarMenu,
group: 'right',
order: 1,
- when: SuggestContext.DetailsVisible.toNegated(),
+ when: ContextKeyExpr.and(SuggestContext.DetailsVisible.toNegated(), SuggestContext.CanResolve),
title: nls.localize('detail.less', "show more")
}]
}));
diff --git a/src/vs/editor/contrib/suggest/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts
index c61ed33c3f..bc5fd02f1c 100644
--- a/src/vs/editor/contrib/suggest/suggestWidget.ts
+++ b/src/vs/editor/contrib/suggest/suggestWidget.ts
@@ -244,12 +244,12 @@ class ItemRenderer implements IListRenderer() {
+ onModelRemoved = Event.None;
+ getModel(uri: URI) {
+ return uri.toString() === model.uri.toString() ? model : null;
+ }
+ };
+
+ let service = new class extends EditorWorkerServiceImpl {
+
+ private _worker = new EditorSimpleWorker(new class extends mock() { }, null);
+
+ constructor() {
+ super(modelService, new class extends mock() { }, new NullLogService());
+ this._worker.acceptNewModel({
+ url: model.uri.toString(),
+ lines: model.getLinesContent(),
+ EOL: model.getEOL(),
+ versionId: model.getVersionId()
+ });
+ model.onDidChangeContent(e => this._worker.acceptModelChanged(model.uri.toString(), e));
+ }
+ computeWordRanges(resource: URI, range: IRange): Promise<{ [word: string]: IRange[] } | null> {
+ return this._worker.computeWordRanges(resource.toString(), range, DEFAULT_WORD_REGEXP.source, DEFAULT_WORD_REGEXP.flags);
+ }
+ };
+
+ distance = await WordDistance.create(service, editor);
+
+ disposables.add(mode);
+ disposables.add(model);
+ disposables.add(editor);
+ });
+
+ function createSuggestItem(label: string, overwriteBefore: number, position: IPosition): CompletionItem {
+ const suggestion: modes.CompletionItem = {
+ label,
+ range: { startLineNumber: position.lineNumber, startColumn: position.column - overwriteBefore, endLineNumber: position.lineNumber, endColumn: position.column },
+ insertText: label,
+ kind: 0
+ };
+ const container: modes.CompletionList = {
+ suggestions: [suggestion]
+ };
+ const provider: modes.CompletionItemProvider = {
+ provideCompletionItems(): any {
+ return;
+ }
+ };
+ return new CompletionItem(position, suggestion, container, provider, undefined!);
+ }
+
+ test('Suggest locality bonus can boost current word #90515', function () {
+ this.skip();
+
+ const pos = { lineNumber: 2, column: 2 };
+ const d1 = distance.distance(pos, createSuggestItem('a', 1, pos).completion);
+ const d2 = distance.distance(pos, createSuggestItem('aa', 1, pos).completion);
+ const d3 = distance.distance(pos, createSuggestItem('ab', 1, pos).completion);
+
+ assert.ok(d1 > d2);
+ assert.ok(d2 === d3);
+ });
+});
diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts
index 6aeac0f5b2..abbad9ba71 100644
--- a/src/vs/monaco.d.ts
+++ b/src/vs/monaco.d.ts
@@ -54,12 +54,19 @@ declare namespace monaco {
}
export interface CancellationToken {
+ /**
+ * A flag signalling is cancellation has been requested.
+ */
readonly isCancellationRequested: boolean;
/**
- * An event emitted when cancellation is requested
+ * An event which fires when cancellation is requested. This event
+ * only ever fires `once` as cancellation can only happen once. Listeners
+ * that are registered after cancellation will be called (next event loop run),
+ * but also only once.
+ *
* @event
*/
- readonly onCancellationRequested: IEvent;
+ readonly onCancellationRequested: (listener: (e: any) => any, thisArgs?: any, disposables?: IDisposable[]) => IDisposable;
}
/**
* Uniform Resource Identifier (Uri) http://tools.ietf.org/html/rfc3986.
diff --git a/src/vs/platform/configuration/common/configurationModels.ts b/src/vs/platform/configuration/common/configurationModels.ts
index 0e0ec3e08f..c41cfb0ceb 100644
--- a/src/vs/platform/configuration/common/configurationModels.ts
+++ b/src/vs/platform/configuration/common/configurationModels.ts
@@ -13,6 +13,10 @@ import { OVERRIDE_PROPERTY_PATTERN, ConfigurationScope, IConfigurationRegistry,
import { IOverrides, overrideIdentifierFromKey, addToValueTree, toValuesTree, IConfigurationModel, getConfigurationValue, IConfigurationOverrides, IConfigurationData, getDefaultValues, getConfigurationKeys, removeFromValueTree, toOverrides, IConfigurationValue, ConfigurationTarget, compare, IConfigurationChangeEvent, IConfigurationChange } from 'vs/platform/configuration/common/configuration';
import { Workspace } from 'vs/platform/workspace/common/workspace';
import { Registry } from 'vs/platform/registry/common/platform';
+import { Disposable } from 'vs/base/common/lifecycle';
+import { Emitter, Event } from 'vs/base/common/event';
+import { IFileService } from 'vs/platform/files/common/files';
+import { dirname } from 'vs/base/common/resources';
export class ConfigurationModel implements IConfigurationModel {
@@ -335,6 +339,40 @@ export class ConfigurationModelParser {
}
}
+export class UserSettings extends Disposable {
+
+ private readonly parser: ConfigurationModelParser;
+ protected readonly _onDidChange: Emitter = this._register(new Emitter());
+ readonly onDidChange: Event = this._onDidChange.event;
+
+ constructor(
+ private readonly userSettingsResource: URI,
+ private readonly scopes: ConfigurationScope[] | undefined,
+ private readonly fileService: IFileService
+ ) {
+ super();
+ this.parser = new ConfigurationModelParser(this.userSettingsResource.toString(), this.scopes);
+ this._register(this.fileService.watch(dirname(this.userSettingsResource)));
+ this._register(Event.filter(this.fileService.onFileChanges, e => e.contains(this.userSettingsResource))(() => this._onDidChange.fire()));
+ }
+
+ async loadConfiguration(): Promise {
+ try {
+ const content = await this.fileService.readFile(this.userSettingsResource);
+ this.parser.parseContent(content.value.toString() || '{}');
+ return this.parser.configurationModel;
+ } catch (e) {
+ return new ConfigurationModel();
+ }
+ }
+
+ reprocess(): ConfigurationModel {
+ this.parser.parse();
+ return this.parser.configurationModel;
+ }
+}
+
+
export class Configuration {
private _workspaceConsolidatedConfiguration: ConfigurationModel | null = null;
diff --git a/src/vs/platform/configuration/node/configurationService.ts b/src/vs/platform/configuration/node/configurationService.ts
index f0e6bd5b71..8fb26a0b87 100644
--- a/src/vs/platform/configuration/node/configurationService.ts
+++ b/src/vs/platform/configuration/node/configurationService.ts
@@ -7,54 +7,39 @@ import { Registry } from 'vs/platform/registry/common/platform';
import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry';
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
import { IConfigurationService, IConfigurationChangeEvent, IConfigurationOverrides, ConfigurationTarget, isConfigurationOverrides, IConfigurationData, IConfigurationValue, IConfigurationChange } from 'vs/platform/configuration/common/configuration';
-import { DefaultConfigurationModel, Configuration, ConfigurationModel, ConfigurationModelParser, ConfigurationChangeEvent } from 'vs/platform/configuration/common/configurationModels';
+import { DefaultConfigurationModel, Configuration, ConfigurationModel, ConfigurationChangeEvent, UserSettings } from 'vs/platform/configuration/common/configurationModels';
import { Event, Emitter } from 'vs/base/common/event';
-import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
-import { ConfigWatcher } from 'vs/base/node/config';
-import { onUnexpectedError } from 'vs/base/common/errors';
import { URI } from 'vs/base/common/uri';
-import { Schemas } from 'vs/base/common/network';
+import { IFileService } from 'vs/platform/files/common/files';
+import { RunOnceScheduler } from 'vs/base/common/async';
export class ConfigurationService extends Disposable implements IConfigurationService, IDisposable {
_serviceBrand: undefined;
private configuration: Configuration;
- private userConfigModelWatcher: ConfigWatcher | undefined;
+ private userConfiguration: UserSettings;
+ private readonly reloadConfigurationScheduler: RunOnceScheduler;
private readonly _onDidChangeConfiguration: Emitter = this._register(new Emitter());
readonly onDidChangeConfiguration: Event = this._onDidChangeConfiguration.event;
constructor(
- private readonly settingsResource: URI
+ private readonly settingsResource: URI,
+ fileService: IFileService
) {
super();
+ this.userConfiguration = this._register(new UserSettings(this.settingsResource, undefined, fileService));
this.configuration = new Configuration(new DefaultConfigurationModel(), new ConfigurationModel());
+
+ this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this.reloadConfiguration(), 50));
this._register(Registry.as(Extensions.Configuration).onDidUpdateConfiguration(configurationProperties => this.onDidDefaultConfigurationChange(configurationProperties)));
+ this._register(this.userConfiguration.onDidChange(() => this.reloadConfigurationScheduler.schedule()));
}
- initialize(): Promise {
- if (this.userConfigModelWatcher) {
- this.userConfigModelWatcher.dispose();
- }
-
- if (this.settingsResource.scheme !== Schemas.file) {
- return Promise.resolve();
- }
- return new Promise((c, e) => {
- this.userConfigModelWatcher = this._register(new ConfigWatcher(this.settingsResource.fsPath, {
- changeBufferDelay: 300, onError: error => onUnexpectedError(error), defaultConfig: new ConfigurationModelParser(this.settingsResource.fsPath), parse: (content: string, parseErrors: any[]) => {
- const userConfigModelParser = new ConfigurationModelParser(this.settingsResource.fsPath);
- userConfigModelParser.parseContent(content);
- parseErrors = [...userConfigModelParser.errors];
- return userConfigModelParser;
- }, initCallback: () => {
- this.configuration = new Configuration(new DefaultConfigurationModel(), this.userConfigModelWatcher!.getConfig().configurationModel);
- this._register(this.userConfigModelWatcher!.onDidUpdateConfiguration(() => this.onDidChangeUserConfiguration(this.userConfigModelWatcher!.getConfig().configurationModel)));
- c();
- }
- }));
- });
+ async initialize(): Promise {
+ const userConfiguration = await this.userConfiguration.loadConfiguration();
+ this.configuration = new Configuration(new DefaultConfigurationModel(), userConfiguration);
}
getConfigurationData(): IConfigurationData {
@@ -92,14 +77,9 @@ export class ConfigurationService extends Disposable implements IConfigurationSe
return this.configuration.keys(undefined);
}
- reloadConfiguration(folder?: IWorkspaceFolder): Promise {
- if (this.userConfigModelWatcher) {
- return new Promise(c => this.userConfigModelWatcher!.reload(userConfigModelParser => {
- this.onDidChangeUserConfiguration(userConfigModelParser.configurationModel);
- c();
- }));
- }
- return this.initialize();
+ async reloadConfiguration(): Promise {
+ const configurationModel = await this.userConfiguration.loadConfiguration();
+ this.onDidChangeUserConfiguration(configurationModel);
}
private onDidChangeUserConfiguration(userConfigurationModel: ConfigurationModel): void {
diff --git a/src/vs/platform/configuration/test/node/configurationService.test.ts b/src/vs/platform/configuration/test/node/configurationService.test.ts
index 31e5b56548..278138bc8d 100644
--- a/src/vs/platform/configuration/test/node/configurationService.test.ts
+++ b/src/vs/platform/configuration/test/node/configurationService.test.ts
@@ -16,14 +16,32 @@ import { testFile } from 'vs/base/test/node/utils';
import { URI } from 'vs/base/common/uri';
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { Event } from 'vs/base/common/event';
+import { NullLogService } from 'vs/platform/log/common/log';
+import { FileService } from 'vs/platform/files/common/fileService';
+import { IDisposable } from 'vs/base/common/lifecycle';
+import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider';
+import { Schemas } from 'vs/base/common/network';
+import { IFileService } from 'vs/platform/files/common/files';
suite('ConfigurationService - Node', () => {
+ let fileService: IFileService;
+ const disposables: IDisposable[] = [];
+
+ setup(() => {
+ const logService = new NullLogService();
+ fileService = new FileService(logService);
+ disposables.push(fileService);
+ const diskFileSystemProvider = new DiskFileSystemProvider(logService);
+ disposables.push(diskFileSystemProvider);
+ fileService.registerProvider(Schemas.file, diskFileSystemProvider);
+ });
+
test('simple', async () => {
const res = await testFile('config', 'config.json');
fs.writeFileSync(res.testFile, '{ "foo": "bar" }');
- const service = new ConfigurationService(URI.file(res.testFile));
+ const service = new ConfigurationService(URI.file(res.testFile), fileService);
await service.initialize();
const config = service.getValue<{
foo: string;
@@ -41,7 +59,7 @@ suite('ConfigurationService - Node', () => {
fs.writeFileSync(res.testFile, '{ "testworkbench.editor.tabs": true }');
- const service = new ConfigurationService(URI.file(res.testFile));
+ const service = new ConfigurationService(URI.file(res.testFile), fileService);
await service.initialize();
const config = service.getValue<{
testworkbench: {
@@ -64,7 +82,7 @@ suite('ConfigurationService - Node', () => {
fs.writeFileSync(res.testFile, ',,,,');
- const service = new ConfigurationService(URI.file(res.testFile));
+ const service = new ConfigurationService(URI.file(res.testFile), fileService);
await service.initialize();
const config = service.getValue<{
foo: string;
@@ -81,7 +99,7 @@ suite('ConfigurationService - Node', () => {
const newDir = path.join(parentDir, 'config', id);
const testFile = path.join(newDir, 'config.json');
- const service = new ConfigurationService(URI.file(testFile));
+ const service = new ConfigurationService(URI.file(testFile), fileService);
await service.initialize();
const config = service.getValue<{ foo: string }>();
@@ -90,10 +108,10 @@ suite('ConfigurationService - Node', () => {
service.dispose();
});
- test('trigger configuration change event', async () => {
+ test('trigger configuration change event when file does not exist', async () => {
const res = await testFile('config', 'config.json');
- const service = new ConfigurationService(URI.file(res.testFile));
+ const service = new ConfigurationService(URI.file(res.testFile), fileService);
await service.initialize();
return new Promise((c, e) => {
const disposable = Event.filter(service.onDidChangeConfiguration, e => e.source === ConfigurationTarget.USER)(async (e) => {
@@ -108,12 +126,31 @@ suite('ConfigurationService - Node', () => {
});
+ test('trigger configuration change event when file exists', async () => {
+ const res = await testFile('config', 'config.json');
+
+ const service = new ConfigurationService(URI.file(res.testFile), fileService);
+ fs.writeFileSync(res.testFile, '{ "foo": "bar" }');
+ await service.initialize();
+ return new Promise((c, e) => {
+ const disposable = Event.filter(service.onDidChangeConfiguration, e => e.source === ConfigurationTarget.USER)(async (e) => {
+ disposable.dispose();
+ assert.equal(service.getValue('foo'), 'barz');
+ service.dispose();
+ await res.cleanUp();
+ c();
+ });
+ fs.writeFileSync(res.testFile, '{ "foo": "barz" }');
+ });
+
+ });
+
test('reloadConfiguration', async () => {
const res = await testFile('config', 'config.json');
fs.writeFileSync(res.testFile, '{ "foo": "bar" }');
- const service = new ConfigurationService(URI.file(res.testFile));
+ const service = new ConfigurationService(URI.file(res.testFile), fileService);
await service.initialize();
let config = service.getValue<{
foo: string;
@@ -162,7 +199,7 @@ suite('ConfigurationService - Node', () => {
}
});
- let serviceWithoutFile = new ConfigurationService(URI.file('__testFile'));
+ let serviceWithoutFile = new ConfigurationService(URI.file('__testFile'), fileService);
await serviceWithoutFile.initialize();
let setting = serviceWithoutFile.getValue();
@@ -172,7 +209,7 @@ suite('ConfigurationService - Node', () => {
return testFile('config', 'config.json').then(async res => {
fs.writeFileSync(res.testFile, '{ "testworkbench.editor.tabs": true }');
- const service = new ConfigurationService(URI.file(res.testFile));
+ const service = new ConfigurationService(URI.file(res.testFile), fileService);
let setting = service.getValue();
@@ -205,7 +242,7 @@ suite('ConfigurationService - Node', () => {
});
const r = await testFile('config', 'config.json');
- const service = new ConfigurationService(URI.file(r.testFile));
+ const service = new ConfigurationService(URI.file(r.testFile), fileService);
service.initialize();
let res = service.inspect('something.missing');
@@ -243,7 +280,7 @@ suite('ConfigurationService - Node', () => {
});
const r = await testFile('config', 'config.json');
- const service = new ConfigurationService(URI.file(r.testFile));
+ const service = new ConfigurationService(URI.file(r.testFile), fileService);
service.initialize();
let res = service.inspect('lookup.service.testNullSetting');
diff --git a/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts b/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts
index 327cb41aaf..d72eea3654 100644
--- a/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts
+++ b/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts
@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import { ipcMain as ipc, app } from 'electron';
+import { ipcMain as ipc, app, BrowserWindow } from 'electron';
import { ILogService } from 'vs/platform/log/common/log';
import { IStateService } from 'vs/platform/state/node/state';
import { Event, Emitter } from 'vs/base/common/event';
@@ -12,7 +12,7 @@ import { ICodeWindow } from 'vs/platform/windows/electron-main/windows';
import { handleVetos } from 'vs/platform/lifecycle/common/lifecycle';
import { isMacintosh, isWindows } from 'vs/base/common/platform';
import { Disposable } from 'vs/base/common/lifecycle';
-import { Barrier } from 'vs/base/common/async';
+import { Barrier, timeout } from 'vs/base/common/async';
import { ParsedArgs } from 'vs/platform/environment/common/environment';
export const ILifecycleMainService = createDecorator('lifecycleMainService');
@@ -106,7 +106,7 @@ export interface ILifecycleMainService {
/**
* Forcefully shutdown the application. No livecycle event handlers are triggered.
*/
- kill(code?: number): void;
+ kill(code?: number): Promise;
/**
* Returns a promise that resolves when a certain lifecycle phase
@@ -550,9 +550,45 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe
this.quit().then(veto => quitVetoed = veto);
}
- kill(code?: number): void {
+ async kill(code?: number): Promise {
this.logService.trace('Lifecycle#kill()');
+ // The kill() method is only used in 2 situations:
+ // - when an instance fails to start at all
+ // - when extension tests run from CLI to report proper exit code
+ //
+ // From extension tests we have seen issues where calling app.exit()
+ // with an opened window can lead to native crashes (Linux) when webviews
+ // are involved. As such, we should make sure to destroy any opened
+ // window before calling app.exit().
+ //
+ // Note: Electron implements a similar logic here:
+ // https://github.com/electron/electron/blob/fe5318d753637c3903e23fc1ed1b263025887b6a/spec-main/window-helpers.ts#L5
+
+ await Promise.race([
+
+ // still do not block more than 1s
+ timeout(1000),
+
+ // destroy any opened window
+ (async () => {
+ for (const window of BrowserWindow.getAllWindows()) {
+ if (window && !window.isDestroyed()) {
+ let whenWindowClosed: Promise;
+ if (window.webContents && !window.webContents.isDestroyed()) {
+ whenWindowClosed = new Promise(c => window.once('closed', c));
+ } else {
+ whenWindowClosed = Promise.resolve();
+ }
+
+ window.destroy();
+ await whenWindowClosed;
+ }
+ }
+ })()
+ ]);
+
+ // Now exit either after 1s or all windows destroyed
app.exit(code);
}
}
diff --git a/src/vs/platform/list/browser/listService.ts b/src/vs/platform/list/browser/listService.ts
index eb462c2cf6..535e5e23cb 100644
--- a/src/vs/platform/list/browser/listService.ts
+++ b/src/vs/platform/list/browser/listService.ts
@@ -243,11 +243,13 @@ export class WorkbenchList extends List {
readonly contextKeyService: IContextKeyService;
private readonly configurationService: IConfigurationService;
+ private readonly themeService: IThemeService;
private listHasSelectionOrFocus: IContextKey;
private listDoubleSelection: IContextKey;
private listMultiSelection: IContextKey;
+ private _styler: IDisposable | undefined;
private _useAltAsMultipleSelectionModifier: boolean;
constructor(
@@ -278,6 +280,7 @@ export class WorkbenchList extends List {
this.contextKeyService = createScopedContextKeyService(contextKeyService, this);
this.configurationService = configurationService;
+ this.themeService = themeService;
const listSupportsMultiSelect = WorkbenchListSupportsMultiSelectContextKey.bindTo(this.contextKeyService);
listSupportsMultiSelect.set(!(options.multipleSelectionSupport === false));
@@ -292,7 +295,7 @@ export class WorkbenchList extends List {
this.disposables.add((listService as ListService).register(this));
if (options.overrideStyles) {
- this.disposables.add(attachListStyler(this, themeService, options.overrideStyles));
+ this.updateStyles(options.overrideStyles);
}
this.disposables.add(this.onSelectionChange(() => {
@@ -313,6 +316,29 @@ export class WorkbenchList extends List {
this.registerListeners();
}
+ updateOptions(options: IWorkbenchListOptions): void {
+ super.updateOptions(options);
+
+ if (options.overrideStyles) {
+ this.updateStyles(options.overrideStyles);
+ }
+ }
+
+ dispose(): void {
+ super.dispose();
+ if (this._styler) {
+ this._styler.dispose();
+ }
+ }
+
+ private updateStyles(styles: IColorMapping): void {
+ if (this._styler) {
+ this._styler.dispose();
+ }
+
+ this._styler = attachListStyler(this, this.themeService, styles);
+ }
+
private registerListeners(): void {
this.disposables.add(this.configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration(multiSelectModifierSettingKey)) {
@@ -738,7 +764,7 @@ export class WorkbenchObjectTree, TFilterData = void>
@IKeybindingService keybindingService: IKeybindingService,
@IAccessibilityService accessibilityService: IAccessibilityService
) {
- const { options: treeOptions, getAutomaticKeyboardNavigation, disposable } = workbenchTreeDataPreamble(container, options, contextKeyService, configurationService, keybindingService, accessibilityService);
+ const { options: treeOptions, getAutomaticKeyboardNavigation, disposable } = workbenchTreeDataPreamble>(container, options, contextKeyService, configurationService, keybindingService, accessibilityService);
super(user, container, delegate, renderers, treeOptions);
this.disposables.add(disposable);
this.internals = new WorkbenchTreeInternals(this, treeOptions, getAutomaticKeyboardNavigation, options.overrideStyles, contextKeyService, listService, themeService, configurationService, accessibilityService);
@@ -769,12 +795,20 @@ export class WorkbenchCompressibleObjectTree, TFilter
@IKeybindingService keybindingService: IKeybindingService,
@IAccessibilityService accessibilityService: IAccessibilityService
) {
- const { options: treeOptions, getAutomaticKeyboardNavigation, disposable } = workbenchTreeDataPreamble(container, options, contextKeyService, configurationService, keybindingService, accessibilityService);
+ const { options: treeOptions, getAutomaticKeyboardNavigation, disposable } = workbenchTreeDataPreamble>(container, options, contextKeyService, configurationService, keybindingService, accessibilityService);
super(user, container, delegate, renderers, treeOptions);
this.disposables.add(disposable);
this.internals = new WorkbenchTreeInternals(this, treeOptions, getAutomaticKeyboardNavigation, options.overrideStyles, contextKeyService, listService, themeService, configurationService, accessibilityService);
this.disposables.add(this.internals);
}
+
+ updateOptions(options: IWorkbenchAsyncDataTreeOptions = {}): void {
+ super.updateOptions(options);
+
+ if (options.overrideStyles) {
+ this.internals.updateStyleOverrides(options.overrideStyles);
+ }
+ }
}
export interface IWorkbenchDataTreeOptions extends IDataTreeOptions {
@@ -801,7 +835,7 @@ export class WorkbenchDataTree extends DataTree>(container, options, contextKeyService, configurationService, keybindingService, accessibilityService);
super(user, container, delegate, renderers, dataSource, treeOptions);
this.disposables.add(disposable);
this.internals = new WorkbenchTreeInternals(this, treeOptions, getAutomaticKeyboardNavigation, options.overrideStyles, contextKeyService, listService, themeService, configurationService, accessibilityService);
@@ -841,7 +875,7 @@ export class WorkbenchAsyncDataTree extends Async
@IKeybindingService keybindingService: IKeybindingService,
@IAccessibilityService accessibilityService: IAccessibilityService
) {
- const { options: treeOptions, getAutomaticKeyboardNavigation, disposable } = workbenchTreeDataPreamble(container, options, contextKeyService, configurationService, keybindingService, accessibilityService);
+ const { options: treeOptions, getAutomaticKeyboardNavigation, disposable } = workbenchTreeDataPreamble>(container, options, contextKeyService, configurationService, keybindingService, accessibilityService);
super(user, container, delegate, renderers, dataSource, treeOptions);
this.disposables.add(disposable);
this.internals = new WorkbenchTreeInternals(this, treeOptions, getAutomaticKeyboardNavigation, options.overrideStyles, contextKeyService, listService, themeService, configurationService, accessibilityService);
@@ -882,7 +916,7 @@ export class WorkbenchCompressibleAsyncDataTree e
@IKeybindingService keybindingService: IKeybindingService,
@IAccessibilityService accessibilityService: IAccessibilityService
) {
- const { options: treeOptions, getAutomaticKeyboardNavigation, disposable } = workbenchTreeDataPreamble(container, options, contextKeyService, configurationService, keybindingService, accessibilityService);
+ const { options: treeOptions, getAutomaticKeyboardNavigation, disposable } = workbenchTreeDataPreamble>(container, options, contextKeyService, configurationService, keybindingService, accessibilityService);
super(user, container, virtualDelegate, compressionDelegate, renderers, dataSource, treeOptions);
this.disposables.add(disposable);
this.internals = new WorkbenchTreeInternals(this, treeOptions, getAutomaticKeyboardNavigation, options.overrideStyles, contextKeyService, listService, themeService, configurationService, accessibilityService);
diff --git a/src/vs/platform/markers/common/markerService.ts b/src/vs/platform/markers/common/markerService.ts
index 9bb9e6f115..151e2bc548 100644
--- a/src/vs/platform/markers/common/markerService.ts
+++ b/src/vs/platform/markers/common/markerService.ts
@@ -64,7 +64,7 @@ class MarkerStats implements MarkerStatistics {
this._data = undefined;
}
- private _update(resources: URI[]): void {
+ private _update(resources: readonly URI[]): void {
if (!this._data) {
return;
}
@@ -343,7 +343,7 @@ export class MarkerService implements IMarkerService {
private static _dedupeMap: { [uri: string]: boolean };
- private static _debouncer(last: URI[], event: URI[]): URI[] {
+ private static _debouncer(last: URI[] | undefined, event: readonly URI[]): URI[] {
if (!last) {
MarkerService._dedupeMap = Object.create(null);
last = [];
diff --git a/src/vs/platform/opener/browser/link.ts b/src/vs/platform/opener/browser/link.ts
new file mode 100644
index 0000000000..b9987fddd1
--- /dev/null
+++ b/src/vs/platform/opener/browser/link.ts
@@ -0,0 +1,64 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the Source EULA. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { Event } from 'vs/base/common/event';
+import { IOpenerService } from 'vs/platform/opener/common/opener';
+import { $ } from 'vs/base/browser/dom';
+import { domEvent } from 'vs/base/browser/event';
+import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
+import { KeyCode } from 'vs/base/common/keyCodes';
+import { Disposable } from 'vs/base/common/lifecycle';
+import { Color } from 'vs/base/common/color';
+
+export interface ILinkDescriptor {
+ readonly label: string;
+ readonly href: string;
+ readonly title?: string;
+}
+
+export interface ILinkStyles {
+ readonly textLinkForeground?: Color;
+}
+
+export class Link extends Disposable {
+
+ readonly el: HTMLAnchorElement;
+ private styles: ILinkStyles = {
+ textLinkForeground: Color.fromHex('#006AB1')
+ };
+
+ constructor(
+ link: ILinkDescriptor,
+ @IOpenerService openerService: IOpenerService
+ ) {
+ super();
+
+ this.el = $('a', {
+ tabIndex: 0,
+ href: link.href,
+ title: link.title
+ }, link.label);
+
+ const onClick = domEvent(this.el, 'click');
+ const onEnterPress = Event.chain(domEvent(this.el, 'keypress'))
+ .map(e => new StandardKeyboardEvent(e))
+ .filter(e => e.keyCode === KeyCode.Enter)
+ .event;
+ const onOpen = Event.any(onClick, onEnterPress);
+
+ this._register(onOpen(_ => openerService.open(link.href)));
+
+ this.applyStyles();
+ }
+
+ style(styles: ILinkStyles): void {
+ this.styles = styles;
+ this.applyStyles();
+ }
+
+ private applyStyles(): void {
+ this.el.style.color = this.styles.textLinkForeground?.toString() || '';
+ }
+}
diff --git a/src/vs/platform/theme/common/styler.ts b/src/vs/platform/theme/common/styler.ts
index b030c31162..02e8e1783b 100644
--- a/src/vs/platform/theme/common/styler.ts
+++ b/src/vs/platform/theme/common/styler.ts
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { ITheme, IThemeService } from 'vs/platform/theme/common/themeService';
-import { focusBorder, inputBackground, inputForeground, ColorIdentifier, selectForeground, selectBackground, selectListBackground, selectBorder, inputBorder, foreground, editorBackground, contrastBorder, inputActiveOptionBorder, inputActiveOptionBackground, listFocusBackground, listFocusForeground, listActiveSelectionBackground, listActiveSelectionForeground, listInactiveSelectionForeground, listInactiveSelectionBackground, listInactiveFocusBackground, listHoverBackground, listHoverForeground, listDropBackground, pickerGroupBorder, pickerGroupForeground, widgetShadow, inputValidationInfoBorder, inputValidationInfoBackground, inputValidationWarningBorder, inputValidationWarningBackground, inputValidationErrorBorder, inputValidationErrorBackground, activeContrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, ColorFunction, badgeBackground, badgeForeground, progressBarBackground, breadcrumbsForeground, breadcrumbsFocusForeground, breadcrumbsActiveSelectionForeground, breadcrumbsBackground, editorWidgetBorder, inputValidationInfoForeground, inputValidationWarningForeground, inputValidationErrorForeground, menuForeground, menuBackground, menuSelectionForeground, menuSelectionBackground, menuSelectionBorder, menuBorder, menuSeparatorBackground, darken, listFilterWidgetOutline, listFilterWidgetNoMatchesOutline, listFilterWidgetBackground, editorWidgetBackground, treeIndentGuidesStroke, editorWidgetForeground, simpleCheckboxBackground, simpleCheckboxBorder, simpleCheckboxForeground, ColorValue, resolveColorValue } from 'vs/platform/theme/common/colorRegistry';
+import { focusBorder, inputBackground, inputForeground, ColorIdentifier, selectForeground, selectBackground, selectListBackground, selectBorder, inputBorder, foreground, editorBackground, contrastBorder, inputActiveOptionBorder, inputActiveOptionBackground, listFocusBackground, listFocusForeground, listActiveSelectionBackground, listActiveSelectionForeground, listInactiveSelectionForeground, listInactiveSelectionBackground, listInactiveFocusBackground, listHoverBackground, listHoverForeground, listDropBackground, pickerGroupBorder, pickerGroupForeground, widgetShadow, inputValidationInfoBorder, inputValidationInfoBackground, inputValidationWarningBorder, inputValidationWarningBackground, inputValidationErrorBorder, inputValidationErrorBackground, activeContrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, ColorFunction, badgeBackground, badgeForeground, progressBarBackground, breadcrumbsForeground, breadcrumbsFocusForeground, breadcrumbsActiveSelectionForeground, breadcrumbsBackground, editorWidgetBorder, inputValidationInfoForeground, inputValidationWarningForeground, inputValidationErrorForeground, menuForeground, menuBackground, menuSelectionForeground, menuSelectionBackground, menuSelectionBorder, menuBorder, menuSeparatorBackground, darken, listFilterWidgetOutline, listFilterWidgetNoMatchesOutline, listFilterWidgetBackground, editorWidgetBackground, treeIndentGuidesStroke, editorWidgetForeground, simpleCheckboxBackground, simpleCheckboxBorder, simpleCheckboxForeground, ColorValue, resolveColorValue, textLinkForeground } from 'vs/platform/theme/common/colorRegistry';
import { IDisposable } from 'vs/base/common/lifecycle';
import { Color } from 'vs/base/common/color';
import { IThemable, styleFn } from 'vs/base/common/styler';
@@ -269,6 +269,16 @@ export function attachButtonStyler(widget: IThemable, themeService: IThemeServic
} as IButtonStyleOverrides, widget);
}
+export interface ILinkStyleOverrides extends IStyleOverrides {
+ textLinkForeground?: ColorIdentifier;
+}
+
+export function attachLinkStyler(widget: IThemable, themeService: IThemeService, style?: ILinkStyleOverrides): IDisposable {
+ return attachStyler(themeService, {
+ textLinkForeground: (style && style.textLinkForeground) || textLinkForeground,
+ } as ILinkStyleOverrides, widget);
+}
+
export interface IProgressBarStyleOverrides extends IStyleOverrides {
progressBarBackground?: ColorIdentifier;
}
diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts
index 3e9fa9d8e9..b508bdc40e 100644
--- a/src/vs/vscode.d.ts
+++ b/src/vs/vscode.d.ts
@@ -4930,6 +4930,21 @@ declare module 'vscode' {
*/
readonly creationOptions: Readonly;
+ /**
+ * The exit status of the terminal, this will be undefined while the terminal is active.
+ *
+ * **Example:** Show a notification with the exit code when the terminal exits with a
+ * non-zero exit code.
+ * ```typescript
+ * window.onDidCloseTerminal(t => {
+ * if (t.exitStatus && t.exitStatus.code) {
+ * vscode.window.showInformationMessage(`Exit code: ${t.exitStatus.code}`);
+ * }
+ * });
+ * ```
+ */
+ readonly exitStatus: TerminalExitStatus | undefined;
+
/**
* Send text to the terminal. The text is written to the stdin of the underlying pty process
* (shell) of the terminal.
@@ -7746,6 +7761,20 @@ declare module 'vscode' {
readonly rows: number;
}
+ /**
+ * Represents how a terminal exited.
+ */
+ export interface TerminalExitStatus {
+ /**
+ * The exit code that a terminal exited with, it can have the following values:
+ * - Zero: the terminal process or custom execution succeeded.
+ * - Non-zero: the terminal process or custom execution failed.
+ * - `undefined`: the user forcibly closed the terminal or a custom execution exited
+ * without providing an exit code.
+ */
+ readonly code: number | undefined;
+ }
+
/**
* A location in the editor at which progress information can be shown. It depends on the
* location how progress is visually represented.
@@ -8406,7 +8435,7 @@ declare module 'vscode' {
* List of workspace folders or `undefined` when no folder is open.
* *Note* that the first entry corresponds to the value of `rootPath`.
*/
- export const workspaceFolders: WorkspaceFolder[] | undefined;
+ export const workspaceFolders: ReadonlyArray | undefined;
/**
* The name of the workspace. `undefined` when no folder
@@ -8580,7 +8609,7 @@ declare module 'vscode' {
/**
* All text documents currently known to the system.
*/
- export const textDocuments: TextDocument[];
+ export const textDocuments: ReadonlyArray;
/**
* Opens a document. Will return early if this document is already open. Otherwise
diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts
index 72fe5ec72a..55986bcf4a 100644
--- a/src/vs/vscode.proposed.d.ts
+++ b/src/vs/vscode.proposed.d.ts
@@ -1025,38 +1025,6 @@ declare module 'vscode' {
//#endregion
- //#region Terminal exit status https://github.com/microsoft/vscode/issues/62103
-
- export interface TerminalExitStatus {
- /**
- * The exit code that a terminal exited with, it can have the following values:
- * - Zero: the terminal process or custom execution succeeded.
- * - Non-zero: the terminal process or custom execution failed.
- * - `undefined`: the user forcibly closed the terminal or a custom execution exited
- * without providing an exit code.
- */
- readonly code: number | undefined;
- }
-
- export interface Terminal {
- /**
- * The exit status of the terminal, this will be undefined while the terminal is active.
- *
- * **Example:** Show a notification with the exit code when the terminal exits with a
- * non-zero exit code.
- * ```typescript
- * window.onDidCloseTerminal(t => {
- * if (t.exitStatus && t.exitStatus.code) {
- * vscode.window.showInformationMessage(`Exit code: ${t.exitStatus.code}`);
- * }
- * });
- * ```
- */
- readonly exitStatus: TerminalExitStatus | undefined;
- }
-
- //#endregion
-
//#region Terminal dimensions property and change event https://github.com/microsoft/vscode/issues/55718
/**
@@ -1643,5 +1611,34 @@ declare module 'vscode' {
asExtensionUri(relativePath: string): Uri;
}
+ export interface Extension {
+ /**
+ * Get the uri of a resource contained in the extension.
+ *
+ * @param relativePath A relative path to a resource contained in the extension.
+ * @return The uri of the resource.
+ */
+ asExtensionUri(relativePath: string): Uri;
+ }
+
+ //#endregion
+
+ //#region https://github.com/microsoft/vscode/issues/86788
+
+ export interface CodeActionProviderMetadata {
+ /**
+ * Static documentation for a class of code actions.
+ *
+ * The documentation is shown in the code actions menu if either:
+ *
+ * - Code actions of `kind` are requested by VS Code. Note that in this case, we always pick the most specific
+ * documentation. For example, if documentation for both `Refactor` and `RefactorExtract` is provided, and we
+ * request code actions for `RefactorExtract`, we prefer the more specific documentation for `RefactorExtract`.
+ *
+ * - Any code actions of `kind` are returned by the provider.
+ */
+ readonly documentation?: ReadonlyArray<{ readonly kind: CodeActionKind, readonly command: Command }>;
+ }
+
//#endregion
}
diff --git a/src/vs/workbench/api/browser/mainThreadComments.ts b/src/vs/workbench/api/browser/mainThreadComments.ts
index 55f37a6c3f..204830ae74 100644
--- a/src/vs/workbench/api/browser/mainThreadComments.ts
+++ b/src/vs/workbench/api/browser/mainThreadComments.ts
@@ -14,12 +14,13 @@ import * as modes from 'vs/editor/common/modes';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { Registry } from 'vs/platform/registry/common/platform';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
-import { Extensions as PanelExtensions, PanelDescriptor, PanelRegistry } from 'vs/workbench/browser/panel';
import { ICommentInfo, ICommentService } from 'vs/workbench/contrib/comments/browser/commentService';
-import { CommentsPanel } from 'vs/workbench/contrib/comments/browser/commentsPanel';
-import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
+import { CommentsPanel } from 'vs/workbench/contrib/comments/browser/commentsView';
import { CommentProviderFeatures, ExtHostCommentsShape, ExtHostContext, IExtHostContext, MainContext, MainThreadCommentsShape, CommentThreadChanges } from '../common/extHost.protocol';
-import { COMMENTS_PANEL_ID, COMMENTS_PANEL_TITLE } from 'vs/workbench/contrib/comments/browser/commentsTreeViewer';
+import { COMMENTS_VIEW_ID, COMMENTS_VIEW_TITLE } from 'vs/workbench/contrib/comments/browser/commentsTreeViewer';
+import { ViewContainer, IViewContainersRegistry, Extensions as ViewExtensions, ViewContainerLocation, IViewsRegistry, IViewsService, IViewDescriptorService } from 'vs/workbench/common/views';
+import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
+import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer';
export class MainThreadCommentThread implements modes.CommentThread {
@@ -343,13 +344,14 @@ export class MainThreadComments extends Disposable implements MainThreadComments
private _activeCommentThread?: MainThreadCommentThread;
private readonly _activeCommentThreadDisposables = this._register(new DisposableStore());
- private _openPanelListener: IDisposable | null = null;
+ private _openViewListener: IDisposable | null = null;
constructor(
extHostContext: IExtHostContext,
@ICommentService private readonly _commentService: ICommentService,
- @IPanelService private readonly _panelService: IPanelService
+ @IViewsService private readonly _viewsService: IViewsService,
+ @IViewDescriptorService private readonly _viewDescriptorService: IViewDescriptorService
) {
super();
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostComments);
@@ -376,10 +378,10 @@ export class MainThreadComments extends Disposable implements MainThreadComments
this._commentService.registerCommentController(providerId, provider);
this._commentControllers.set(handle, provider);
- const commentsPanelAlreadyConstructed = this._panelService.getPanels().some(panel => panel.id === COMMENTS_PANEL_ID);
+ const commentsPanelAlreadyConstructed = !!this._viewDescriptorService.getViewDescriptor(COMMENTS_VIEW_ID);
if (!commentsPanelAlreadyConstructed) {
- this.registerPanel(commentsPanelAlreadyConstructed);
- this.registerOpenPanelListener(commentsPanelAlreadyConstructed);
+ this.registerView(commentsPanelAlreadyConstructed);
+ this.registerViewOpenedListener(commentsPanelAlreadyConstructed);
}
this._commentService.setWorkspaceComments(String(handle), []);
}
@@ -444,27 +446,36 @@ export class MainThreadComments extends Disposable implements MainThreadComments
return provider.deleteCommentThread(commentThreadHandle);
}
- private registerPanel(commentsPanelAlreadyConstructed: boolean) {
- if (!commentsPanelAlreadyConstructed) {
- Registry.as(PanelExtensions.Panels).registerPanel(PanelDescriptor.create(
- CommentsPanel,
- COMMENTS_PANEL_ID,
- COMMENTS_PANEL_TITLE,
- 'commentsPanel',
- 10
- ));
+ private registerView(commentsViewAlreadyRegistered: boolean) {
+ if (!commentsViewAlreadyRegistered) {
+ const VIEW_CONTAINER: ViewContainer = Registry.as(ViewExtensions.ViewContainersRegistry).registerViewContainer({
+ id: COMMENTS_VIEW_ID,
+ name: COMMENTS_VIEW_TITLE,
+ ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [COMMENTS_VIEW_ID, COMMENTS_VIEW_TITLE, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }]),
+ order: 10,
+ }, ViewContainerLocation.Panel);
+
+ Registry.as(ViewExtensions.ViewsRegistry).registerViews([{
+ id: COMMENTS_VIEW_ID,
+ name: COMMENTS_VIEW_TITLE,
+ canToggleVisibility: false,
+ ctorDescriptor: new SyncDescriptor(CommentsPanel),
+ focusCommand: {
+ id: 'workbench.action.focusCommentsPanel'
+ }
+ }], VIEW_CONTAINER);
}
}
/**
- * If the comments panel has never been opened, the constructor for it has not yet run so it has
- * no listeners for comment threads being set or updated. Listen for the panel opening for the
+ * If the comments view has never been opened, the constructor for it has not yet run so it has
+ * no listeners for comment threads being set or updated. Listen for the view opening for the
* first time and send it comments then.
*/
- private registerOpenPanelListener(commentsPanelAlreadyConstructed: boolean) {
- if (!commentsPanelAlreadyConstructed && !this._openPanelListener) {
- this._openPanelListener = this._panelService.onDidPanelOpen(e => {
- if (e.panel.getId() === COMMENTS_PANEL_ID) {
+ private registerViewOpenedListener(commentsPanelAlreadyConstructed: boolean) {
+ if (!commentsPanelAlreadyConstructed && !this._openViewListener) {
+ this._openViewListener = this._viewsService.onDidChangeViewVisibility(e => {
+ if (e.id === COMMENTS_VIEW_ID && e.visible) {
keys(this._commentControllers).forEach(handle => {
let threads = this._commentControllers.get(handle)!.getAllComments();
@@ -474,9 +485,9 @@ export class MainThreadComments extends Disposable implements MainThreadComments
}
});
- if (this._openPanelListener) {
- this._openPanelListener.dispose();
- this._openPanelListener = null;
+ if (this._openViewListener) {
+ this._openViewListener.dispose();
+ this._openViewListener = null;
}
}
});
diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts
index 62dc156ef0..31d5117fad 100644
--- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts
+++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts
@@ -11,7 +11,7 @@ import * as search from 'vs/workbench/contrib/search/common/search';
import { CancellationToken } from 'vs/base/common/cancellation';
import { Position as EditorPosition } from 'vs/editor/common/core/position';
import { Range as EditorRange, IRange } from 'vs/editor/common/core/range';
-import { ExtHostContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, MainContext, IExtHostContext, ILanguageConfigurationDto, IRegExpDto, IIndentationRuleDto, IOnEnterRuleDto, ILocationDto, IWorkspaceSymbolDto, reviveWorkspaceEditDto, IDocumentFilterDto, IDefinitionLinkDto, ISignatureHelpProviderMetadataDto, ILinkDto, ICallHierarchyItemDto, ISuggestDataDto, ICodeActionDto, ISuggestDataDtoField, ISuggestResultDtoField } from '../common/extHost.protocol';
+import { ExtHostContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, MainContext, IExtHostContext, ILanguageConfigurationDto, IRegExpDto, IIndentationRuleDto, IOnEnterRuleDto, ILocationDto, IWorkspaceSymbolDto, reviveWorkspaceEditDto, IDocumentFilterDto, IDefinitionLinkDto, ISignatureHelpProviderMetadataDto, ILinkDto, ICallHierarchyItemDto, ISuggestDataDto, ICodeActionDto, ISuggestDataDtoField, ISuggestResultDtoField, ICodeActionProviderMetadataDto } from '../common/extHost.protocol';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { LanguageConfiguration, IndentationRule, OnEnterRule } from 'vs/editor/common/modes/languageConfiguration';
import { IModeService } from 'vs/editor/common/services/modeService';
@@ -55,11 +55,11 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
//#region --- revive functions
- private static _reviveLocationDto(data: ILocationDto): modes.Location;
- private static _reviveLocationDto(data: ILocationDto[]): modes.Location[];
- private static _reviveLocationDto(data: ILocationDto | ILocationDto[]): modes.Location | modes.Location[] {
+ private static _reviveLocationDto(data?: ILocationDto): modes.Location;
+ private static _reviveLocationDto(data?: ILocationDto[]): modes.Location[];
+ private static _reviveLocationDto(data: ILocationDto | ILocationDto[] | undefined): modes.Location | modes.Location[] | undefined {
if (!data) {
- return data;
+ return undefined; // {{SQL CARBON EDIT}} strict-null-checks
} else if (Array.isArray(data)) {
data.forEach(l => MainThreadLanguageFeatures._reviveLocationDto(l));
return data;
@@ -235,7 +235,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
// --- quick fix
- $registerQuickFixSupport(handle: number, selector: IDocumentFilterDto[], providedCodeActionKinds?: string[]): void {
+ $registerQuickFixSupport(handle: number, selector: IDocumentFilterDto[], metadata: ICodeActionProviderMetadataDto): void {
this._registrations.set(handle, modes.CodeActionProviderRegistry.register(selector, {
provideCodeActions: async (model: ITextModel, rangeOrSelection: EditorRange | Selection, context: modes.CodeActionContext, token: CancellationToken): Promise => {
const listDto = await this._proxy.$provideCodeActions(handle, model.uri, rangeOrSelection, context, token);
@@ -251,7 +251,8 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
}
};
},
- providedCodeActionKinds
+ providedCodeActionKinds: metadata.providedKinds,
+ documentation: metadata.documentation
}));
}
diff --git a/src/vs/workbench/api/browser/mainThreadTerminalService.ts b/src/vs/workbench/api/browser/mainThreadTerminalService.ts
index 06403d89af..74e929e4ce 100644
--- a/src/vs/workbench/api/browser/mainThreadTerminalService.ts
+++ b/src/vs/workbench/api/browser/mainThreadTerminalService.ts
@@ -309,15 +309,16 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
return true;
}
- private _onRequestAvailableShells(request: IAvailableShellsRequest): void {
+ private async _onRequestAvailableShells(req: IAvailableShellsRequest): Promise {
if (this._isPrimaryExtHost()) {
- this._proxy.$requestAvailableShells().then(e => request(e));
+ req.callback(await this._proxy.$getAvailableShells());
}
}
- private _onRequestDefaultShellAndArgs(request: IDefaultShellAndArgsRequest): void {
+ private async _onRequestDefaultShellAndArgs(req: IDefaultShellAndArgsRequest): Promise {
if (this._isPrimaryExtHost()) {
- this._proxy.$requestDefaultShellAndArgs(request.useAutomationShell).then(e => request.callback(e.shell, e.args));
+ const res = await this._proxy.$getDefaultShellAndArgs(req.useAutomationShell);
+ req.callback(res.shell, res.args);
}
}
diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts
index 06d512be16..f8e2c8dbfe 100644
--- a/src/vs/workbench/api/common/extHost.api.impl.ts
+++ b/src/vs/workbench/api/common/extHost.api.impl.ts
@@ -53,7 +53,7 @@ import { ProxyIdentifier } from 'vs/workbench/services/extensions/common/proxyId
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
import * as vscode from 'vscode';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
-import { originalFSPath } from 'vs/base/common/resources';
+import { originalFSPath, joinPath } from 'vs/base/common/resources';
import { values } from 'vs/base/common/collections';
import { ExtHostEditorInsets } from 'vs/workbench/api/common/extHostCodeInsets';
import { ExtHostLabelService } from 'vs/workbench/api/common/extHostLabelService';
@@ -478,22 +478,19 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
onDidChangeWindowState(listener, thisArg?, disposables?) {
return extHostWindow.onDidChangeWindowState(listener, thisArg, disposables);
},
- // {{SQL CARBON EDIT}} Typing needs to be disabled; enabled strict null checks allows the typing once we get there
- showInformationMessage(message, first, ...rest) {
- return extHostMessageService.showMessage(extension, Severity.Info, message, first, rest);
+ showInformationMessage(message: string, ...rest: Array) {
+ return >extHostMessageService.showMessage(extension, Severity.Info, message, rest[0], >rest.slice(1));
},
- // {{SQL CARBON EDIT}} Typing needs to be disabled; enabled strict null checks allows the typing once we get there
- showWarningMessage(message, first, ...rest) {
- return extHostMessageService.showMessage(extension, Severity.Warning, message, first, rest);
+ showWarningMessage(message: string, ...rest: Array) {
+ return >extHostMessageService.showMessage(extension, Severity.Warning, message, rest[0], >rest.slice(1));
},
- // {{SQL CARBON EDIT}} Typing needs to be disabled; enabled strict null checks allows the typing once we get there
- showErrorMessage(message, first, ...rest) {
- return extHostMessageService.showMessage(extension, Severity.Error, message, first, rest);
+ showErrorMessage(message: string, ...rest: Array) {
+ return >extHostMessageService.showMessage(extension, Severity.Error, message, rest[0], >rest.slice(1));
},
- showQuickPick(items: any, options: vscode.QuickPickOptions, token?: vscode.CancellationToken): any {
+ showQuickPick(items: any, options?: vscode.QuickPickOptions, token?: vscode.CancellationToken): any {
return extHostQuickOpen.showQuickPick(items, !!extension.enableProposedApi, options, token);
},
- showWorkspaceFolderPick(options: vscode.WorkspaceFolderPickOptions) {
+ showWorkspaceFolderPick(options?: vscode.WorkspaceFolderPickOptions) {
return extHostQuickOpen.showWorkspaceFolderPick(options);
},
showInputBox(options?: vscode.InputBoxOptions, token?: vscode.CancellationToken) {
@@ -539,10 +536,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
createOutputChannel(name: string): vscode.OutputChannel {
return extHostOutputService.createOutputChannel(name);
},
- createWebviewPanel(viewType: string, title: string, showOptions: vscode.ViewColumn | { viewColumn: vscode.ViewColumn, preserveFocus?: boolean }, options: vscode.WebviewPanelOptions & vscode.WebviewOptions): vscode.WebviewPanel {
+ createWebviewPanel(viewType: string, title: string, showOptions: vscode.ViewColumn | { viewColumn: vscode.ViewColumn, preserveFocus?: boolean }, options?: vscode.WebviewPanelOptions & vscode.WebviewOptions): vscode.WebviewPanel {
return extHostWebviews.createWebviewPanel(extension, viewType, title, showOptions, options);
},
- createWebviewTextEditorInset(editor: vscode.TextEditor, line: number, height: number, options: vscode.WebviewOptions): vscode.WebviewEditorInset {
+ createWebviewTextEditorInset(editor: vscode.TextEditor, line: number, height: number, options?: vscode.WebviewOptions): vscode.WebviewEditorInset {
checkProposedApiEnabled(extension);
return extHostEditorInsets.createWebviewEditorInset(editor, line, height, options, extension);
},
@@ -754,7 +751,12 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
},
openTunnel: (forward: vscode.TunnelOptions) => {
checkProposedApiEnabled(extension);
- return extHostTunnelService.openTunnel(forward);
+ return extHostTunnelService.openTunnel(forward).then(value => {
+ if (!value) {
+ throw new Error('cannot open tunnel');
+ }
+ return value;
+ });
},
get tunnels() {
checkProposedApiEnabled(extension);
@@ -1025,6 +1027,11 @@ class Extension implements vscode.Extension {
this.extensionKind = kind;
}
+ asExtensionUri(relativePath: string): URI {
+ checkProposedApiEnabled(this.packageJSON);
+ return joinPath(this.packageJSON.extensionLocation, relativePath);
+ }
+
get isActive(): boolean {
return this._extensionService.isActivated(this._identifier);
}
diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts
index 7b8a6656c9..1c9c9e640d 100644
--- a/src/vs/workbench/api/common/extHost.protocol.ts
+++ b/src/vs/workbench/api/common/extHost.protocol.ts
@@ -359,7 +359,7 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable {
$registerHoverProvider(handle: number, selector: IDocumentFilterDto[]): void;
$registerDocumentHighlightProvider(handle: number, selector: IDocumentFilterDto[]): void;
$registerReferenceSupport(handle: number, selector: IDocumentFilterDto[]): void;
- $registerQuickFixSupport(handle: number, selector: IDocumentFilterDto[], supportedKinds?: string[]): void;
+ $registerQuickFixSupport(handle: number, selector: IDocumentFilterDto[], metadata: ICodeActionProviderMetadataDto): void;
$registerDocumentFormattingSupport(handle: number, selector: IDocumentFilterDto[], extensionId: ExtensionIdentifier, displayName: string): void;
$registerRangeFormattingSupport(handle: number, selector: IDocumentFilterDto[], extensionId: ExtensionIdentifier, displayName: string): void;
$registerOnTypeFormattingSupport(handle: number, selector: IDocumentFilterDto[], autoFormatTriggerCharacters: string[], extensionId: ExtensionIdentifier): void;
@@ -1151,6 +1151,11 @@ export interface ICodeActionListDto {
actions: ReadonlyArray;
}
+export interface ICodeActionProviderMetadataDto {
+ readonly providedKinds?: readonly string[];
+ readonly documentation?: ReadonlyArray<{ readonly kind: string, readonly command: ICommandDto }>;
+}
+
export type CacheId = number;
export type ChainedCacheId = [CacheId, CacheId];
@@ -1293,8 +1298,8 @@ export interface ExtHostTerminalServiceShape {
$acceptProcessRequestCwd(id: number): void;
$acceptProcessRequestLatency(id: number): number;
$acceptWorkspacePermissionsChanged(isAllowed: boolean): void;
- $requestAvailableShells(): Promise;
- $requestDefaultShellAndArgs(useAutomationShell: boolean): Promise;
+ $getAvailableShells(): Promise;
+ $getDefaultShellAndArgs(useAutomationShell: boolean): Promise;
}
export interface ExtHostSCMShape {
diff --git a/src/vs/workbench/api/common/extHostApiCommands.ts b/src/vs/workbench/api/common/extHostApiCommands.ts
index d66c0b2acb..6e782ce878 100644
--- a/src/vs/workbench/api/common/extHostApiCommands.ts
+++ b/src/vs/workbench/api/common/extHostApiCommands.ts
@@ -9,7 +9,6 @@ import type * as vscode from 'vscode';
import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters';
import * as types from 'vs/workbench/api/common/extHostTypes';
import { IRawColorInfo, IWorkspaceEditDto, ICallHierarchyItemDto, IIncomingCallDto, IOutgoingCallDto } from 'vs/workbench/api/common/extHost.protocol';
-import { ISingleEditOperation } from 'vs/editor/common/model';
import * as modes from 'vs/editor/common/modes';
import * as search from 'vs/workbench/contrib/search/common/search';
import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands';
@@ -128,17 +127,17 @@ const newCommands: ApiCommand[] = [
new ApiCommand(
'vscode.executeFormatDocumentProvider', '_executeFormatDocumentProvider', 'Execute document format provider.',
[ApiCommandArgument.Uri, new ApiCommandArgument('options', 'Formatting options', _ => true, v => v)],
- new ApiCommandResult('A promise that resolves to an array of TextEdits.', tryMapWith(typeConverters.TextEdit.to))
+ new ApiCommandResult('A promise that resolves to an array of TextEdits.', tryMapWith(typeConverters.TextEdit.to))
),
new ApiCommand(
'vscode.executeFormatRangeProvider', '_executeFormatRangeProvider', 'Execute range format provider.',
[ApiCommandArgument.Uri, ApiCommandArgument.Range, new ApiCommandArgument('options', 'Formatting options', _ => true, v => v)],
- new ApiCommandResult('A promise that resolves to an array of TextEdits.', tryMapWith(typeConverters.TextEdit.to))
+ new ApiCommandResult