This commit is contained in:
ADS Merger 2020-02-13 02:56:02 +00:00
Родитель 9af1f3b0eb
Коммит 73ea8b79b2
229 изменённых файлов: 3192 добавлений и 2103 удалений

2
.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": [

Просмотреть файл

@ -8,5 +8,7 @@
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
<true/>
</dict>
</plist>

Просмотреть файл

@ -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: |

Просмотреть файл

@ -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

Просмотреть файл

@ -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: |

Просмотреть файл

@ -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"

Просмотреть файл

@ -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

Просмотреть файл

@ -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 }

Просмотреть файл

@ -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"

Просмотреть файл

@ -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;
}

Просмотреть файл

@ -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;
}

Просмотреть файл

@ -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<vscode.CompletionItem[]> {
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<vscode.CompletionItem[]> {
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([]);
}

Просмотреть файл

@ -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;
}

Просмотреть файл

@ -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);

Просмотреть файл

@ -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,

Просмотреть файл

@ -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)(:)$",

Просмотреть файл

@ -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",

Просмотреть файл

@ -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",

Просмотреть файл

@ -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",

Просмотреть файл

@ -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==

Просмотреть файл

@ -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"

Просмотреть файл

@ -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

Просмотреть файл

@ -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

Просмотреть файл

@ -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<NotebookContentChange>();
@ -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));

Просмотреть файл

@ -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

Просмотреть файл

@ -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;

Просмотреть файл

@ -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 } = (<ITreeViewDescriptor>Registry.as<IViewsRegistry>(Extensions.ViewsRegistry).getView(options.id));
this.treeView = treeView as ITreeView;
this._register(this.treeView.onDidChangeActions(() => this.updateActions(), this));

Просмотреть файл

@ -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);

Просмотреть файл

@ -86,7 +86,7 @@ function createInstantiationService(addAccountFailureEmitter?: Emitter<string>):
.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; });

Просмотреть файл

@ -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 => {

Просмотреть файл

@ -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<IQueryModelService>(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<IEditorService> = TypeMoq.Mock.ofType<IEditorService>(TestEditorService, TypeMoq.MockBehavior.Strict);
@ -567,3 +568,9 @@ suite('commandLineService tests', () => {
});
});
class ServiceAccessor {
constructor(
@IUntitledTextEditorService public readonly untitledTextEditorService: IUntitledTextEditorService
) { }
}

Просмотреть файл

@ -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);

Просмотреть файл

@ -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<void> { return this._sql.onDidModelChangeEncoding; }
public resolve(refresh?: boolean): Promise<EditorModel> { return this._sql.resolve(); }
public resolve(refresh?: boolean): Promise<IUntitledTextEditorModel & IResolvedTextEditorModel> { 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);

Просмотреть файл

@ -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<ILanguageAssociationRegistry>(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 = <any>(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
) { }
}

Просмотреть файл

@ -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;

Просмотреть файл

@ -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<void> = this._register(new Emitter<void>());
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<void> = this._register(new Emitter<void>());
private _model: NotebookEditorModel;
private _untitledEditorModel: UntitledTextEditorModel;
private _untitledEditorModel: IUntitledTextEditorModel;
private _contentManager: IContentManager;
private _providersLoaded: Promise<void>;
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();

Просмотреть файл

@ -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;
}

Просмотреть файл

@ -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
) { }
}

Просмотреть файл

@ -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;

Просмотреть файл

@ -580,8 +580,7 @@ export abstract class GridTableBase<T> 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);
});
}

Просмотреть файл

@ -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<UntitledTextEditorModel & IResolvedTextEditorModel> {
public resolve(): Promise<IUntitledTextEditorModel & IResolvedTextEditorModel> {
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 {

Просмотреть файл

@ -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>(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
) { }
}

Просмотреть файл

@ -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
) { }
}

Просмотреть файл

@ -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);
}
}

Просмотреть файл

@ -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<T> 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 {

Просмотреть файл

@ -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<boolean>('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

Просмотреть файл

@ -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;
}

Просмотреть файл

@ -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;
}
}

Просмотреть файл

@ -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<IViewSize | undefined> { 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) {

Просмотреть файл

@ -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" }

Двоичный файл не отображается.

Просмотреть файл

@ -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<T extends IView = IView> 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;

Просмотреть файл

@ -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<ILayoutContext>, IDisposable {
readonly element: HTMLElement;
@ -217,10 +248,27 @@ class BranchNode implements ISplitView<ILayoutContext>, 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<ILayoutContext>, 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<ILayoutContext>, 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<ILayoutContext>, 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<ILayoutContext>, 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<ILayoutContext>, 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<IViewSize | undefined>();
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;
}

Просмотреть файл

@ -128,14 +128,14 @@ class AsyncDataTreeRenderer<TInput, T, TFilterData, TTemplateData> implements IT
}
}
function asTreeEvent<TInput, T>(e: ITreeEvent<IAsyncDataTreeNode<TInput, T>>): ITreeEvent<T> {
function asTreeEvent<TInput, T>(e: ITreeEvent<IAsyncDataTreeNode<TInput, T> | null>): ITreeEvent<T> {
return {
browserEvent: e.browserEvent,
elements: e.elements.map(e => e.element as T)
elements: e.elements.map(e => e!.element as T)
};
}
function asTreeMouseEvent<TInput, T>(e: ITreeMouseEvent<IAsyncDataTreeNode<TInput, T>>): ITreeMouseEvent<T> {
function asTreeMouseEvent<TInput, T>(e: ITreeMouseEvent<IAsyncDataTreeNode<TInput, T> | null>): ITreeMouseEvent<T> {
return {
browserEvent: e.browserEvent,
element: e.element && e.element.element as T,
@ -143,7 +143,7 @@ function asTreeMouseEvent<TInput, T>(e: ITreeMouseEvent<IAsyncDataTreeNode<TInpu
};
}
function asTreeContextMenuEvent<TInput, T>(e: ITreeContextMenuEvent<IAsyncDataTreeNode<TInput, T>>): ITreeContextMenuEvent<T> {
function asTreeContextMenuEvent<TInput, T>(e: ITreeContextMenuEvent<IAsyncDataTreeNode<TInput, T> | null>): ITreeContextMenuEvent<T> {
return {
browserEvent: e.browserEvent,
element: e.element && e.element.element as T,
@ -793,7 +793,11 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
return result.finally(() => this.refreshPromises.delete(node));
}
private _onDidChangeCollapseState({ node, deep }: ICollapseStateChangeEvent<IAsyncDataTreeNode<TInput, T>, any>): void {
private _onDidChangeCollapseState({ node, deep }: ICollapseStateChangeEvent<IAsyncDataTreeNode<TInput, T> | null, any>): void {
if (node.element === null) {
return;
}
if (!node.collapsed && node.element.stale) {
if (deep) {
this.collapse(node.element.element as T);

Просмотреть файл

@ -37,7 +37,7 @@ export class DataTree<TInput, T, TFilterData = void> extends AbstractTree<T | nu
private dataSource: IDataSource<TInput, T>,
options: IDataTreeOptions<T, TFilterData> = {}
) {
super(user, container, delegate, renderers, options);
super(user, container, delegate, renderers, options as IDataTreeOptions<T | null, TFilterData>);
this.identityProvider = options.identityProvider;
}
@ -182,7 +182,7 @@ export class DataTree<TInput, T, TFilterData = void> extends AbstractTree<T | nu
throw new TreeError(this.user, 'Can\'t get tree view state without an identity provider');
}
const getId = (element: T) => 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);

Просмотреть файл

@ -30,7 +30,7 @@ export class ObjectTree<T extends NonNullable<any>, TFilterData = void> extends
renderers: ITreeRenderer<T, TFilterData, any>[],
options: IObjectTreeOptions<T, TFilterData> = {}
) {
super(user, container, delegate, renderers, options);
super(user, container, delegate, renderers, options as IObjectTreeOptions<T | null, TFilterData>);
}
setChildren(element: T | null, children?: ISequence<ITreeElement<T>>): void {
@ -181,7 +181,7 @@ export class CompressibleObjectTree<T extends NonNullable<any>, TFilterData = vo
) {
const compressedTreeNodeProvider = () => this;
const compressibleRenderers = renderers.map(r => new CompressibleRenderer<T, TFilterData, any>(compressedTreeNodeProvider, r));
super(user, container, delegate, compressibleRenderers, asObjectTreeOptions(compressedTreeNodeProvider, options));
super(user, container, delegate, compressibleRenderers, asObjectTreeOptions<T, TFilterData>(compressedTreeNodeProvider, options));
}
setChildren(element: T | null, children?: ISequence<ICompressedTreeElement<T>>): void {

Просмотреть файл

@ -79,35 +79,47 @@ export class ObjectTreeModel<T extends NonNullable<any>, TFilterData extends Non
const insertedElements = new Set<T | null>();
const insertedElementIds = new Set<string>();
const _onDidCreateNode = (node: ITreeNode<T, TFilterData>) => {
insertedElements.add(node.element);
this.nodes.set(node.element, node);
const _onDidCreateNode = (node: ITreeNode<T | null, TFilterData>) => {
if (node.element === null) {
return;
}
const tnode = node as ITreeNode<T, TFilterData>;
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<T, TFilterData>) => {
if (!insertedElements.has(node.element)) {
this.nodes.delete(node.element);
const _onDidDeleteNode = (node: ITreeNode<T | null, TFilterData>) => {
if (node.element === null) {
return;
}
const tnode = node as ITreeNode<T, TFilterData>;
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);
}
};

Просмотреть файл

@ -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<any>;
readonly onCancellationRequested: (listener: (e: any) => any, thisArgs?: any, disposables?: IDisposable[]) => IDisposable;
}
const shortcutEvent = Object.freeze(function (callback, context?): IDisposable {
const shortcutEvent: Event<any> = Object.freeze(function (callback, context?): IDisposable {
const handle = setTimeout(callback.bind(context), 0);
return { dispose() { clearTimeout(handle); } };
} as Event<any>);
});
export namespace CancellationToken {

Просмотреть файл

@ -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<T>(...events: Event<T>[]): Event<T>;
export function any(...events: Event<any>[]): Event<void>;
export function any<T>(...events: Event<T>[]): Event<T> {
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<O>(fn: (i: T) => O): IChainableEvent<O>;
forEach(fn: (i: T) => void): IChainableEvent<T>;
filter(fn: (e: T) => boolean): IChainableEvent<T>;
filter<R>(fn: (e: T | R) => e is R): IChainableEvent<R>;
reduce<R>(merge: (last: R | undefined, event: T) => R, initial?: R): IChainableEvent<R>;
latch(): IChainableEvent<T>;
debounce(merge: (last: T | undefined, event: T) => T, delay?: number, leading?: boolean, leakWarningThreshold?: number): IChainableEvent<T>;
@ -291,6 +294,8 @@ export namespace Event {
return new ChainableEvent(forEach(this.event, fn));
}
filter(fn: (e: T) => boolean): IChainableEvent<T>;
filter<R>(fn: (e: T | R) => e is R): IChainableEvent<R>;
filter(fn: (e: T) => boolean): IChainableEvent<T> {
return new ChainableEvent(filter(this.event, fn));
}

Просмотреть файл

@ -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<T> {
config: T;
}
export interface IConfigWatcher<T> {
path: string;
hasParseErrors: boolean;
reload(callback: (config: T) => void): void;
getConfig(): T;
}
export interface IConfigOptions<T> {
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<T> extends Disposable implements IConfigWatcher<T> {
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<IConfigurationChangeEvent<T>>;
constructor(private _path: string, private options: IConfigOptions<T> = { defaultConfig: Object.create(null), onError: error => console.error(error) }) {
super();
this._onDidUpdateConfiguration = this._register(new Emitter<IConfigurationChangeEvent<T>>());
this.registerWatcher();
this.initAsync();
}
get path(): string {
return this._path;
}
get hasParseErrors(): boolean {
return !!this.parseErrors && this.parseErrors.length > 0;
}
get onDidUpdateConfiguration(): Event<IConfigurationChangeEvent<T>> {
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<void> {
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();
}
}

Просмотреть файл

@ -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({

Просмотреть файл

@ -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<void>(() => Promise.resolve());
let extractedEntriesCount = 0;
Event.once(token.onCancellationRequested)(() => {
token.onCancellationRequested(() => {
last.cancel();
zipfile.close();
});

Просмотреть файл

@ -141,8 +141,8 @@ export class ItemRegistry {
readonly onDidCollapseItem: Event<IItemCollapseEvent> = this._onDidCollapseItem.event;
private _onDidAddTraitItem = new EventMultiplexer<IItemTraitEvent>();
readonly onDidAddTraitItem: Event<IItemTraitEvent> = this._onDidAddTraitItem.event;
private _onDidRemoveTraitItem = new EventMultiplexer<IItemCollapseEvent>();
readonly onDidRemoveTraitItem: Event<IItemCollapseEvent> = this._onDidRemoveTraitItem.event;
private _onDidRemoveTraitItem = new EventMultiplexer<IItemTraitEvent>();
readonly onDidRemoveTraitItem: Event<IItemTraitEvent> = this._onDidRemoveTraitItem.event;
private _onDidRefreshItem = new EventMultiplexer<Item>();
readonly onDidRefreshItem: Event<Item> = this._onDidRefreshItem.event;
private _onRefreshItemChildren = new EventMultiplexer<IItemChildrenRefreshEvent>();
@ -273,8 +273,8 @@ export class Item {
readonly onDidCollapse: Event<IItemCollapseEvent> = this._onDidCollapse.event;
private readonly _onDidAddTrait = new Emitter<IItemTraitEvent>();
readonly onDidAddTrait: Event<IItemTraitEvent> = this._onDidAddTrait.event;
private readonly _onDidRemoveTrait = new Emitter<IItemCollapseEvent>();
readonly onDidRemoveTrait: Event<IItemCollapseEvent> = this._onDidRemoveTrait.event;
private readonly _onDidRemoveTrait = new Emitter<IItemTraitEvent>();
readonly onDidRemoveTrait: Event<IItemTraitEvent> = this._onDidRemoveTrait.event;
private readonly _onDidRefresh = new Emitter<Item>();
readonly onDidRefresh: Event<Item> = this._onDidRefresh.event;
private readonly _onRefreshChildren = new Emitter<IItemChildrenRefreshEvent>();
@ -895,8 +895,8 @@ export class TreeModel {
readonly onDidCollapseItem: Event<IItemCollapseEvent> = this._onDidCollapseItem.event;
private _onDidAddTraitItem = new Relay<IItemTraitEvent>();
readonly onDidAddTraitItem: Event<IItemTraitEvent> = this._onDidAddTraitItem.event;
private _onDidRemoveTraitItem = new Relay<IItemCollapseEvent>();
readonly onDidRemoveTraitItem: Event<IItemCollapseEvent> = this._onDidRemoveTraitItem.event;
private _onDidRemoveTraitItem = new Relay<IItemTraitEvent>();
readonly onDidRemoveTraitItem: Event<IItemTraitEvent> = this._onDidRemoveTraitItem.event;
private _onDidRefreshItem = new Relay<Item>();
readonly onDidRefreshItem: Event<Item> = this._onDidRefreshItem.event;
private _onRefreshItemChildren = new Relay<IItemChildrenRefreshEvent>();

Просмотреть файл

@ -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<IThrottledGestureEvent>(this.wrapper, 'MSGestureChange', (e) => this.onThrottledMsGestureChange(e), (lastEvent: IThrottledGestureEvent, event: MSGestureEvent): IThrottledGestureEvent => {
this.viewListeners.push(DOM.addDisposableThrottledListener<IThrottledGestureEvent, MSGestureEvent>(this.wrapper, 'MSGestureChange', e => this.onThrottledMsGestureChange(e), (lastEvent, event) => {
event.stopPropagation();
event.preventDefault();

Просмотреть файл

@ -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' },
'...'
]);
});
});

Просмотреть файл

@ -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');

Просмотреть файл

@ -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<any[]>(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);
});
});

Просмотреть файл

@ -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
});
})();

Просмотреть файл

@ -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<IElectronService>(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);

Просмотреть файл

@ -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<Client<string>>): Promise<IInstantiationService> {
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<boolean> {
// 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;

Просмотреть файл

@ -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));

Просмотреть файл

@ -306,16 +306,6 @@ export async function main(argv: ParsedArgs): Promise<void> {
await Promise.all<void | undefined>([environmentService.appSettingsHome.fsPath, environmentService.extensionsPath]
.map((path): undefined | Promise<void> => 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<void> {
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 => {

Просмотреть файл

@ -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);
}
};

Просмотреть файл

@ -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);
}));

Просмотреть файл

@ -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);

Просмотреть файл

@ -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);

Просмотреть файл

@ -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);
}

Просмотреть файл

@ -615,7 +615,9 @@ export interface CodeActionProvider {
/**
* Optional list of CodeActionKinds that this provider returns.
*/
providedCodeActionKinds?: ReadonlyArray<string>;
readonly providedCodeActionKinds?: ReadonlyArray<string>;
readonly documentation?: ReadonlyArray<{ readonly kind: string, readonly command: Command }>;
/**
* @internal

Просмотреть файл

@ -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;

Просмотреть файл

@ -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<ViewLineData | null>;
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) {

Просмотреть файл

@ -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;

Просмотреть файл

@ -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);
}

Просмотреть файл

@ -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<ReadonlyArray<modes.CodeAction>> {
const { resource, rangeOrSelection, kind } = args;
if (!(resource instanceof URI)) {

Просмотреть файл

@ -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;
}

Просмотреть файл

@ -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<T>(
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;
});
}

Просмотреть файл

@ -181,6 +181,8 @@ export interface SelectionEvent {
readonly element?: Location;
}
class ReferencesTree extends WorkbenchAsyncDataTree<ReferencesModel | FileReferences, TreeElement, FuzzyScore> { }
/**
* ZoneWidget that is shown inside the editor
*/
@ -195,7 +197,7 @@ export class ReferenceWidget extends peekView.PeekViewWidget {
private readonly _onDidSelectReference = new Emitter<SelectionEvent>();
readonly onDidSelectReference = this._onDidSelectReference.event;
private _tree!: WorkbenchAsyncDataTree<ReferencesModel | FileReferences, TreeElement, FuzzyScore>;
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<typeof WorkbenchAsyncDataTree, WorkbenchAsyncDataTree<ReferencesModel | FileReferences, TreeElement, FuzzyScore>>(
WorkbenchAsyncDataTree,
this._tree = this._instantiationService.createInstance(
ReferencesTree,
'ReferencesWidget',
this._treeContainer,
new Delegate(),

Просмотреть файл

@ -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%;
}

Просмотреть файл

@ -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;
}

Просмотреть файл

@ -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;
}

Просмотреть файл

@ -26,6 +26,7 @@ export const Context = {
MakesTextEdit: new RawContextKey('suggestionMakesTextEdit', true),
AcceptSuggestionsOnEnter: new RawContextKey<boolean>('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<void>;
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<void> | 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 => {

Просмотреть файл

@ -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")
}]
}));

Просмотреть файл

@ -244,12 +244,12 @@ class ItemRenderer implements IListRenderer<CompletionItem, ISuggestionTemplateD
data.signatureLabel.textContent = '';
data.qualifierLabel.textContent = '';
data.detailsLabel.textContent = (suggestion.detail || '').replace(/\n.*$/m, '');
removeClass(data.right, 'always-show-details');
addClass(data.root, 'string-label');
} else {
data.signatureLabel.textContent = (suggestion.label.signature || '').replace(/\n.*$/m, '');
data.qualifierLabel.textContent = (suggestion.label.qualifier || '').replace(/\n.*$/m, '');
data.detailsLabel.textContent = (suggestion.label.type || '').replace(/\n.*$/m, '');
addClass(data.right, 'always-show-details');
removeClass(data.root, 'string-label');
}
if (canExpandCompletionItem(element)) {

Просмотреть файл

@ -0,0 +1,118 @@
/*---------------------------------------------------------------------------------------------
* 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 { EditorSimpleWorker } from 'vs/editor/common/services/editorSimpleWorker';
import { mock } from 'vs/editor/contrib/suggest/test/suggestModel.test';
import { EditorWorkerHost, EditorWorkerServiceImpl } from 'vs/editor/common/services/editorWorkerServiceImpl';
import { IModelService } from 'vs/editor/common/services/modelService';
import { TextModel } from 'vs/editor/common/model/textModel';
import { URI } from 'vs/base/common/uri';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfigurationService';
import { NullLogService } from 'vs/platform/log/common/log';
import { WordDistance } from 'vs/editor/contrib/suggest/wordDistance';
import { createTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor';
import { IRange } from 'vs/editor/common/core/range';
import { DEFAULT_WORD_REGEXP } from 'vs/editor/common/model/wordHelper';
import { Event } from 'vs/base/common/event';
import { CompletionItem } from 'vs/editor/contrib/suggest/suggest';
import { IPosition } from 'vs/editor/common/core/position';
import * as modes from 'vs/editor/common/modes';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { MockMode } from 'vs/editor/test/common/mocks/mockMode';
suite('suggest, word distance', function () {
class BracketMode extends MockMode {
private static readonly _id = new modes.LanguageIdentifier('bracketMode', 3);
constructor() {
super(BracketMode._id);
this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), {
brackets: [
['{', '}'],
['[', ']'],
['(', ')'],
]
}));
}
}
let distance: WordDistance;
let disposables = new DisposableStore();
setup(async function () {
disposables.clear();
let mode = new BracketMode();
let model = TextModel.createFromString('function abc(aa, ab){\na\n}', undefined, mode.getLanguageIdentifier(), URI.parse('test:///some.path'));
let editor = createTestCodeEditor({ model: model });
editor.updateOptions({ suggest: { localityBonus: true } });
editor.setPosition({ lineNumber: 2, column: 2 });
let modelService = new class extends mock<IModelService>() {
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<EditorWorkerHost>() { }, null);
constructor() {
super(modelService, new class extends mock<ITextResourceConfigurationService>() { }, 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);
});
});

11
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<any>;
readonly onCancellationRequested: (listener: (e: any) => any, thisArgs?: any, disposables?: IDisposable[]) => IDisposable;
}
/**
* Uniform Resource Identifier (Uri) http://tools.ietf.org/html/rfc3986.

Просмотреть файл

@ -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<void> = this._register(new Emitter<void>());
readonly onDidChange: Event<void> = 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<ConfigurationModel> {
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;

Просмотреть файл

@ -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<ConfigurationModelParser> | undefined;
private userConfiguration: UserSettings;
private readonly reloadConfigurationScheduler: RunOnceScheduler;
private readonly _onDidChangeConfiguration: Emitter<IConfigurationChangeEvent> = this._register(new Emitter<IConfigurationChangeEvent>());
readonly onDidChangeConfiguration: Event<IConfigurationChangeEvent> = 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<IConfigurationRegistry>(Extensions.Configuration).onDidUpdateConfiguration(configurationProperties => this.onDidDefaultConfigurationChange(configurationProperties)));
this._register(this.userConfiguration.onDidChange(() => this.reloadConfigurationScheduler.schedule()));
}
initialize(): Promise<void> {
if (this.userConfigModelWatcher) {
this.userConfigModelWatcher.dispose();
}
if (this.settingsResource.scheme !== Schemas.file) {
return Promise.resolve();
}
return new Promise<void>((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<void> {
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<void> {
if (this.userConfigModelWatcher) {
return new Promise<void>(c => this.userConfigModelWatcher!.reload(userConfigModelParser => {
this.onDidChangeUserConfiguration(userConfigModelParser.configurationModel);
c();
}));
}
return this.initialize();
async reloadConfiguration(): Promise<void> {
const configurationModel = await this.userConfiguration.loadConfiguration();
this.onDidChangeUserConfiguration(configurationModel);
}
private onDidChangeUserConfiguration(userConfigurationModel: ConfigurationModel): void {

Просмотреть файл

@ -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<ITestSetting>();
@ -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<ITestSetting>();
@ -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');

Просмотреть файл

@ -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<ILifecycleMainService>('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<void>;
/**
* 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<void> {
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<void>;
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);
}
}

Просмотреть файл

@ -243,11 +243,13 @@ export class WorkbenchList<T> extends List<T> {
readonly contextKeyService: IContextKeyService;
private readonly configurationService: IConfigurationService;
private readonly themeService: IThemeService;
private listHasSelectionOrFocus: IContextKey<boolean>;
private listDoubleSelection: IContextKey<boolean>;
private listMultiSelection: IContextKey<boolean>;
private _styler: IDisposable | undefined;
private _useAltAsMultipleSelectionModifier: boolean;
constructor(
@ -278,6 +280,7 @@ export class WorkbenchList<T> extends List<T> {
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<T> extends List<T> {
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<T> extends List<T> {
this.registerListeners();
}
updateOptions(options: IWorkbenchListOptions<T>): 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<T extends NonNullable<any>, 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<T, TFilterData, IWorkbenchObjectTreeOptions<T, TFilterData>>(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<T extends NonNullable<any>, 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<T, TFilterData, IWorkbenchCompressibleObjectTreeOptions<T, TFilterData>>(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<T, TFilterData> = {}): void {
super.updateOptions(options);
if (options.overrideStyles) {
this.internals.updateStyleOverrides(options.overrideStyles);
}
}
}
export interface IWorkbenchDataTreeOptions<T, TFilterData> extends IDataTreeOptions<T, TFilterData> {
@ -801,7 +835,7 @@ export class WorkbenchDataTree<TInput, T, TFilterData = void> extends DataTree<T
@IKeybindingService keybindingService: IKeybindingService,
@IAccessibilityService accessibilityService: IAccessibilityService
) {
const { options: treeOptions, getAutomaticKeyboardNavigation, disposable } = workbenchTreeDataPreamble(container, options, contextKeyService, configurationService, keybindingService, accessibilityService);
const { options: treeOptions, getAutomaticKeyboardNavigation, disposable } = workbenchTreeDataPreamble<T, TFilterData, IWorkbenchDataTreeOptions<T, TFilterData>>(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<TInput, T, TFilterData = void> 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<T, TFilterData, IWorkbenchAsyncDataTreeOptions<T, TFilterData>>(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<TInput, T, TFilterData = void> 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<T, TFilterData, IWorkbenchCompressibleAsyncDataTreeOptions<T, TFilterData>>(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);

Просмотреть файл

@ -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 = [];

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше