diff --git a/.travis.yml b/.travis.yml index b552f3ee567..15af121f5c8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,8 +27,8 @@ before_install: - git submodule update --init --recursive - git clone --depth 1 https://github.com/creationix/nvm.git ./.nvm - source ./.nvm/nvm.sh - - nvm install 7.4.0 - - nvm use 7.4.0 + - nvm install 7.9.0 + - nvm use 7.9.0 - npm config set python `which python` - npm install -g gulp - if [ $TRAVIS_OS_NAME == "linux" ]; then diff --git a/appveyor.yml b/appveyor.yml index 49e80fed313..eaf86cb5b20 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -3,7 +3,7 @@ environment: VSCODE_BUILD_VERBOSE: true install: - - ps: Install-Product node 7.4.0 x64 + - ps: Install-Product node 7.9.0 x64 - npm install -g npm@4 --silent - npm install -g gulp mocha --silent diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 3c351d454ad..b4465b4a505 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -45,8 +45,8 @@ const nodeModules = ['electron', 'original-fs'] // Build const builtInExtensions = [ - { name: 'ms-vscode.node-debug', version: '1.17.8' }, - { name: 'ms-vscode.node-debug2', version: '1.17.1' } + { name: 'ms-vscode.node-debug', version: '1.17.12' }, + { name: 'ms-vscode.node-debug2', version: '1.17.4' } ]; const excludedExtensions = [ @@ -479,8 +479,10 @@ gulp.task('generate-vscode-configuration', () => { return reject(new Error('$AGENT_BUILDDIRECTORY not set')); } + const userDataDir = path.join(os.tmpdir(), 'tmpuserdata'); + const extensionsDir = path.join(os.tmpdir(), 'tmpextdir'); const appPath = path.join(buildDir, 'VSCode-darwin/Visual\\ Studio\\ Code\\ -\\ Insiders.app/Contents/Resources/app/bin/code'); - const codeProc = cp.exec(`${appPath} --export-default-configuration='${allConfigDetailsPath}' --wait`); + const codeProc = cp.exec(`${appPath} --export-default-configuration='${allConfigDetailsPath}' --wait --user-data-dir='${userDataDir}' --extensions-dir='${extensionsDir}'`); const timer = setTimeout(() => { codeProc.kill(); diff --git a/build/tfs/common/common.sh b/build/tfs/common/common.sh index 52f53537943..cdd656676a3 100755 --- a/build/tfs/common/common.sh +++ b/build/tfs/common/common.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -e # set agent specific npm cache diff --git a/build/tfs/common/node.sh b/build/tfs/common/node.sh index 87f95a5e1d7..67f3f59552f 100755 --- a/build/tfs/common/node.sh +++ b/build/tfs/common/node.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -e # setup nvm diff --git a/build/tfs/darwin/release.sh b/build/tfs/darwin/release.sh index 24ed4c30a78..9547d62f489 100755 --- a/build/tfs/darwin/release.sh +++ b/build/tfs/darwin/release.sh @@ -19,8 +19,11 @@ rm -rf $UNSIGNEDZIP step "Create unsigned archive" \ zip -r -X -y $UNSIGNEDZIP *) -step "Upload unsigned archive" \ - node build/tfs/common/publish.js --upload-only $VSCODE_QUALITY darwin archive-unsigned VSCode-darwin-$VSCODE_QUALITY-unsigned.zip $VERSION false $UNSIGNEDZIP +# step "Upload unsigned archive" \ +# node build/tfs/common/publish.js --upload-only $VSCODE_QUALITY darwin archive-unsigned VSCode-darwin-$VSCODE_QUALITY-unsigned.zip $VERSION false $UNSIGNEDZIP -step "Sign build" \ - node build/tfs/common/enqueue.js $VSCODE_QUALITY \ No newline at end of file +step "Upload unsigned archive" \ + node build/tfs/common/publish.js $VSCODE_QUALITY darwin archive-unsigned VSCode-darwin-$VSCODE_QUALITY-unsigned.zip $VERSION false $UNSIGNEDZIP + +# step "Sign build" \ +# node build/tfs/common/enqueue.js $VSCODE_QUALITY \ No newline at end of file diff --git a/build/tfs/linux/build-ia32.sh b/build/tfs/linux/build-ia32.sh index 0b0f1c2a458..0d9f98c692d 100755 --- a/build/tfs/linux/build-ia32.sh +++ b/build/tfs/linux/build-ia32.sh @@ -1,3 +1,3 @@ -#!/bin/bash +#!/usr/bin/env bash set -e ./build/tfs/linux/build.sh ia32 "$@" \ No newline at end of file diff --git a/build/tfs/linux/build-x64.sh b/build/tfs/linux/build-x64.sh index fb5b38e02b3..e193a01a5b7 100755 --- a/build/tfs/linux/build-x64.sh +++ b/build/tfs/linux/build-x64.sh @@ -1,3 +1,3 @@ -#!/bin/bash +#!/usr/bin/env bash set -e ./build/tfs/linux/build.sh x64 "$@" \ No newline at end of file diff --git a/build/tfs/linux/build.sh b/build/tfs/linux/build.sh index b3d1825c2d9..4af53947033 100755 --- a/build/tfs/linux/build.sh +++ b/build/tfs/linux/build.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . ./build/tfs/common/node.sh . ./scripts/env.sh diff --git a/build/tfs/linux/ia32/run-agent.sh b/build/tfs/linux/ia32/run-agent.sh index bcf9017f3cf..efcc96632a3 100755 --- a/build/tfs/linux/ia32/run-agent.sh +++ b/build/tfs/linux/ia32/run-agent.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash if [ ! -f pat ]; then echo "Error: file pat not found" diff --git a/build/tfs/linux/ia32/xvfb.init b/build/tfs/linux/ia32/xvfb.init index 4d77d253a26..74f6e3b2522 100644 --- a/build/tfs/linux/ia32/xvfb.init +++ b/build/tfs/linux/ia32/xvfb.init @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # /etc/rc.d/init.d/xvfbd # diff --git a/build/tfs/linux/release.sh b/build/tfs/linux/release.sh index 41f6d35e675..e4d2009a172 100755 --- a/build/tfs/linux/release.sh +++ b/build/tfs/linux/release.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . ./scripts/env.sh . ./build/tfs/common/common.sh diff --git a/build/tfs/linux/repoapi_client.sh b/build/tfs/linux/repoapi_client.sh index b214ef10726..80de4db4a2a 100755 --- a/build/tfs/linux/repoapi_client.sh +++ b/build/tfs/linux/repoapi_client.sh @@ -1,4 +1,4 @@ -#!/bin/bash -e +#!/usr/bin/env bash -e # This is a VERY basic script for Create/Delete operations on repos and packages # cmd=$1 diff --git a/build/tfs/linux/smoketest.sh b/build/tfs/linux/smoketest.sh index 308fb047590..9866874a208 100644 --- a/build/tfs/linux/smoketest.sh +++ b/build/tfs/linux/smoketest.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -e . ./build/tfs/common/node.sh diff --git a/build/tfs/linux/x64/run-agent.sh b/build/tfs/linux/x64/run-agent.sh index 76978ce2b02..1f122aa40dd 100755 --- a/build/tfs/linux/x64/run-agent.sh +++ b/build/tfs/linux/x64/run-agent.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash if [ ! -f pat ]; then echo "Error: file pat not found" diff --git a/build/tfs/linux/x64/xvfb.init b/build/tfs/linux/x64/xvfb.init index 4d77d253a26..74f6e3b2522 100644 --- a/build/tfs/linux/x64/xvfb.init +++ b/build/tfs/linux/x64/xvfb.init @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # /etc/rc.d/init.d/xvfbd # diff --git a/build/tfs/win32/3_upload.ps1 b/build/tfs/win32/3_upload.ps1 index 8d10c908261..c37aba70599 100644 --- a/build/tfs/win32/3_upload.ps1 +++ b/build/tfs/win32/3_upload.ps1 @@ -24,12 +24,20 @@ $env:AZURE_DOCUMENTDB_MASTERKEY = $documentDbKey $assetPlatform = if ($arch -eq "ia32") { "win32" } else { "win32-x64" } -step "Publish archive" { - exec { & node build/tfs/common/publish.js $Quality "$global:assetPlatform-archive" archive "VSCode-win32-$global:arch-$Version.zip" $Version true $Zip } +# step "Publish archive" { +# exec { & node build/tfs/common/publish.js $Quality "$global:assetPlatform-archive" archive "VSCode-win32-$global:arch-$Version.zip" $Version true $Zip } +# } + +# step "Publish setup package" { +# exec { & node build/tfs/common/publish.js $Quality "$global:assetPlatform" setup "VSCodeSetup-$global:arch-$Version.exe" $Version true $Exe } +# } + +step "Publish UNSIGNED archive" { + exec { & node build/tfs/common/publish.js $Quality "$global:assetPlatform-archive" archive-unsigned "VSCode-win32-$global:arch-$Version.zip" $Version false $Zip } } -step "Publish setup package" { - exec { & node build/tfs/common/publish.js $Quality "$global:assetPlatform" setup "VSCodeSetup-$global:arch-$Version.exe" $Version true $Exe } +step "Publish UNSIGNED setup package" { + exec { & node build/tfs/common/publish.js $Quality "$global:assetPlatform" setup-unsigned "VSCodeSetup-$global:arch-$Version.exe" $Version false $Exe } } done \ No newline at end of file diff --git a/extensions/clojure/language-configuration.json b/extensions/clojure/language-configuration.json index 004e1dbd95b..24f52480015 100644 --- a/extensions/clojure/language-configuration.json +++ b/extensions/clojure/language-configuration.json @@ -18,5 +18,8 @@ ["[", "]"], ["(", ")"], ["\"", "\""] - ] + ], + "folding": { + "offSide": true + } } \ No newline at end of file diff --git a/extensions/coffeescript/language-configuration.json b/extensions/coffeescript/language-configuration.json index 745427eacfb..8c7fbd458df 100644 --- a/extensions/coffeescript/language-configuration.json +++ b/extensions/coffeescript/language-configuration.json @@ -21,5 +21,8 @@ ["(", ")"], ["\"", "\""], ["'", "'"] - ] + ], + "folding": { + "offSide": true + } } \ No newline at end of file diff --git a/extensions/configuration-editing/package.json b/extensions/configuration-editing/package.json index 1fd34c23234..2217043af38 100644 --- a/extensions/configuration-editing/package.json +++ b/extensions/configuration-editing/package.json @@ -33,19 +33,11 @@ }, { "fileMatch": "vscode://defaultsettings/settings.json", - "url": "vscode://schemas/settings" - }, - { - "fileMatch": "vscode://defaultsettings/resourceSettings.json", - "url": "vscode://schemas/settings/resource" - }, - { - "fileMatch": "vscode://settings/workspaceSettings.json", - "url": "vscode://schemas/settings" + "url": "vscode://schemas/settings/default" }, { "fileMatch": "%APP_SETTINGS_HOME%/settings.json", - "url": "vscode://schemas/settings" + "url": "vscode://schemas/settings/user" }, { "fileMatch": "%APP_WORKSPACES_HOME%/*/workspace.json", @@ -61,7 +53,7 @@ }, { "fileMatch": "/.vscode/settings.json", - "url": "vscode://schemas/settings" + "url": "vscode://schemas/settings/folder" }, { "fileMatch": "/.vscode/launch.json", diff --git a/extensions/cpp/language-configuration.json b/extensions/cpp/language-configuration.json index a8782a61e3c..2888f3edea9 100644 --- a/extensions/cpp/language-configuration.json +++ b/extensions/cpp/language-configuration.json @@ -25,5 +25,11 @@ "indentationRules": { "increaseIndentPattern": "^.*\\{[^}\"\\']*$|^.*\\([^\\)\"\\']*$|^\\s*(public|private|protected):\\s*$|^\\s*@(public|private|protected)\\s*$|^\\s*\\{\\}$", "decreaseIndentPattern": "^\\s*(\\s*/[*].*[*]/\\s*)*\\}|^\\s*(\\s*/[*].*[*]/\\s*)*\\)|^\\s*(public|private|protected):\\s*$|^\\s*@(public|private|protected)\\s*$" + }, + "folding": { + "markers": { + "start": "^\\s*#pragma\\s+region", + "end": "^\\s*#pragma\\s+endregion" + } } } \ No newline at end of file diff --git a/extensions/csharp/language-configuration.json b/extensions/csharp/language-configuration.json index 5502deac4da..17f3fb0caf6 100644 --- a/extensions/csharp/language-configuration.json +++ b/extensions/csharp/language-configuration.json @@ -23,5 +23,11 @@ ["<", ">"], ["'", "'"], ["\"", "\""] - ] + ], + "folding": { + "markers": { + "start": "^\\s*#region", + "end": "^\\s*#endregion" + } + } } \ No newline at end of file diff --git a/extensions/css/client/src/cssMain.ts b/extensions/css/client/src/cssMain.ts index 5515147dc9f..6b43c68252f 100644 --- a/extensions/css/client/src/cssMain.ts +++ b/extensions/css/client/src/cssMain.ts @@ -6,14 +6,13 @@ import * as path from 'path'; -import { languages, window, commands, ExtensionContext, TextDocument, ColorInformation, ColorPresentation, Color, TextEdit as CodeTextEdit } from 'vscode'; +import { languages, window, commands, ExtensionContext, TextDocument, ColorInformation, ColorPresentation, Color } from 'vscode'; import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind, TextEdit } from 'vscode-languageclient'; -import { ConfigurationFeature } from 'vscode-languageclient/lib/proposed'; -import { DocumentColorRequest } from 'vscode-languageserver-protocol/lib/protocol.colorProvider.proposed'; +import { ConfigurationFeature } from 'vscode-languageclient/lib/configuration.proposed'; +import { DocumentColorRequest, DocumentColorParams, ColorPresentationRequest, ColorPresentationParams } from 'vscode-languageserver-protocol/lib/protocol.colorProvider.proposed'; import * as nls from 'vscode-nls'; -import * as convert from 'color-convert'; let localize = nls.loadMessageBundle(); // this method is called when vs code is activated @@ -52,53 +51,34 @@ export function activate(context: ExtensionContext) { // client can be deactivated on extension deactivation context.subscriptions.push(disposable); - var _toTwoDigitHex = function (n: number): string { - const r = n.toString(16); - return r.length !== 2 ? '0' + r : r; - }; - client.onReady().then(_ => { // register color provider context.subscriptions.push(languages.registerColorProvider(documentSelector, { provideDocumentColors(document: TextDocument): Thenable { - let params = client.code2ProtocolConverter.asDocumentSymbolParams(document); + let params: DocumentColorParams = { + textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document) + }; return client.sendRequest(DocumentColorRequest.type, params).then(symbols => { return symbols.map(symbol => { let range = client.protocol2CodeConverter.asRange(symbol.range); - let color = new Color(symbol.color.red * 255, symbol.color.green * 255, symbol.color.blue * 255, symbol.color.alpha); + let color = new Color(symbol.color.red, symbol.color.green, symbol.color.blue, symbol.color.alpha); return new ColorInformation(range, color); }); }); }, - provideColorPresentations(colorInfo: ColorInformation): ColorPresentation[] | Thenable { - let result: ColorPresentation[] = []; - let color = colorInfo.color; - let label; - if (color.alpha === 1) { - label = `rgb(${Math.round(color.red * 255)}, ${Math.round(color.green * 255)}, ${Math.round(color.blue * 255)})`; - } else { - label = `rgba(${Math.round(color.red * 255)}, ${Math.round(color.green * 255)}, ${Math.round(color.blue * 255)}, ${color.alpha})`; - } - - result.push({ label: label, textEdit: new CodeTextEdit(colorInfo.range, label) }); - - if (color.alpha === 1) { - label = `#${_toTwoDigitHex(Math.round(color.red * 255))}${_toTwoDigitHex(Math.round(color.green * 255))}${_toTwoDigitHex(Math.round(color.blue * 255))}`; - } else { - label = `#${_toTwoDigitHex(Math.round(color.red * 255))}${_toTwoDigitHex(Math.round(color.green * 255))}${_toTwoDigitHex(Math.round(color.blue * 255))}${_toTwoDigitHex(Math.round(color.alpha * 255))}`; - } - - result.push({ label: label, textEdit: new CodeTextEdit(colorInfo.range, label) }); - - const hsl = convert.rgb.hsl(Math.round(color.red * 255), Math.round(color.green * 255), Math.round(color.blue * 255)); - if (color.alpha === 1) { - label = `hsl(${hsl[0]}, ${hsl[1]}%, ${hsl[2]}%)`; - } else { - label = `hsla(${hsl[0]}, ${hsl[1]}%, ${hsl[2]}%, ${color.alpha})`; - } - - result.push({ label: label, textEdit: new CodeTextEdit(colorInfo.range, label) }); - return result; + provideColorPresentations(document: TextDocument, colorInfo: ColorInformation): ColorPresentation[] | Thenable { + let params: ColorPresentationParams = { + textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document), + colorInfo: { range: client.code2ProtocolConverter.asRange(colorInfo.range), color: colorInfo.color } + }; + return client.sendRequest(ColorPresentationRequest.type, params).then(presentations => { + return presentations.map(p => { + let presentation = new ColorPresentation(p.label); + presentation.textEdit = p.textEdit && client.protocol2CodeConverter.asTextEdit(p.textEdit); + presentation.additionalTextEdits = p.additionalTextEdits && client.protocol2CodeConverter.asTextEdits(p.additionalTextEdits); + return presentation; + }); + }); } })); }); diff --git a/extensions/css/npm-shrinkwrap.json b/extensions/css/npm-shrinkwrap.json index 6ae15a5c670..0cb7377ce6d 100644 --- a/extensions/css/npm-shrinkwrap.json +++ b/extensions/css/npm-shrinkwrap.json @@ -2,30 +2,26 @@ "name": "css", "version": "0.1.0", "dependencies": { - "color-convert": { - "version": "0.5.3", - "from": "color-convert@>=0.5.0 <0.6.0", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-0.5.3.tgz" - }, + "vscode-jsonrpc": { - "version": "3.3.1", - "from": "vscode-jsonrpc@>=3.3.0 <4.0.0", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-3.3.1.tgz" + "version": "3.4.0", + "from": "vscode-jsonrpc@>=3.4.0 <4.0.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-3.4.0.tgz" }, "vscode-languageclient": { - "version": "3.4.0-next.17", + "version": "3.4.2", "from": "vscode-languageclient@next", - "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-3.4.0-next.17.tgz" + "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-3.4.2.tgz" }, "vscode-languageserver-protocol": { - "version": "3.1.1", - "from": "vscode-languageserver-protocol@>=3.1.1 <4.0.0", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.1.1.tgz" + "version": "3.4.2", + "from": "vscode-languageserver-protocol@next", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.4.2.tgz" }, "vscode-languageserver-types": { - "version": "3.3.0", - "from": "vscode-languageserver-types@>=3.3.0 <4.0.0", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.3.0.tgz" + "version": "3.4.0", + "from": "vscode-languageserver-types@>=3.4.0 <4.0.0", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.4.0.tgz" }, "vscode-nls": { "version": "2.0.2", diff --git a/extensions/css/package.json b/extensions/css/package.json index 1f7e0681b1e..fa48af3a4fc 100644 --- a/extensions/css/package.json +++ b/extensions/css/package.json @@ -720,12 +720,11 @@ ] }, "dependencies": { - "color-convert": "^0.5.3", - "vscode-languageclient": "3.4.0-next.17", - "vscode-languageserver-protocol": "^3.1.1", + "vscode-languageclient": "^3.4.2", + "vscode-languageserver-protocol": "^3.4.2", "vscode-nls": "^2.0.2" }, "devDependencies": { "@types/node": "^6.0.51" } -} \ No newline at end of file +} diff --git a/extensions/css/server/npm-shrinkwrap.json b/extensions/css/server/npm-shrinkwrap.json index 2c2d280b1a2..348abc705e6 100644 --- a/extensions/css/server/npm-shrinkwrap.json +++ b/extensions/css/server/npm-shrinkwrap.json @@ -3,29 +3,29 @@ "version": "1.0.0", "dependencies": { "vscode-css-languageservice": { - "version": "2.1.6", + "version": "2.1.7", "from": "vscode-css-languageservice@next", - "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-2.1.6.tgz" + "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-2.1.7.tgz" }, "vscode-jsonrpc": { - "version": "3.3.1", - "from": "vscode-jsonrpc@>=3.3.0 <4.0.0", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-3.3.1.tgz" + "version": "3.4.0", + "from": "vscode-jsonrpc@>=3.4.0 <4.0.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-3.4.0.tgz" }, "vscode-languageserver": { - "version": "3.4.0-next.6", + "version": "3.4.2", "from": "vscode-languageserver@next", - "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-3.4.0-next.6.tgz" + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-3.4.2.tgz" }, "vscode-languageserver-protocol": { - "version": "3.1.1", - "from": "vscode-languageserver-protocol@>=3.1.1 <4.0.0", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.1.1.tgz" + "version": "3.4.2", + "from": "vscode-languageserver-protocol@next", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.4.2.tgz" }, "vscode-languageserver-types": { - "version": "3.3.0", + "version": "3.4.0", "from": "vscode-languageserver-types@>=3.3.0 <4.0.0", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.3.0.tgz" + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.4.0.tgz" }, "vscode-nls": { "version": "2.0.2", diff --git a/extensions/css/server/package.json b/extensions/css/server/package.json index b62b50928bf..76d9afbecf9 100644 --- a/extensions/css/server/package.json +++ b/extensions/css/server/package.json @@ -8,9 +8,9 @@ "node": "*" }, "dependencies": { - "vscode-css-languageservice": "^2.1.6", - "vscode-languageserver": "3.4.0-next.6", - "vscode-languageserver-protocol": "^3.1.1" + "vscode-css-languageservice": "^2.1.7", + "vscode-languageserver": "^3.4.2", + "vscode-languageserver-protocol": "^3.4.2" }, "devDependencies": { "@types/node": "^6.0.51" diff --git a/extensions/css/server/src/cssServerMain.ts b/extensions/css/server/src/cssServerMain.ts index 0f4e8873e98..34f558429f3 100644 --- a/extensions/css/server/src/cssServerMain.ts +++ b/extensions/css/server/src/cssServerMain.ts @@ -5,11 +5,13 @@ 'use strict'; import { - createConnection, IConnection, TextDocuments, TextDocument, InitializeParams, InitializeResult, ServerCapabilities + createConnection, IConnection, TextDocuments, InitializeParams, InitializeResult, ServerCapabilities } from 'vscode-languageserver'; -import { GetConfigurationRequest } from 'vscode-languageserver-protocol/lib/protocol.configuration.proposed'; -import { DocumentColorRequest, ServerCapabilities as CPServerCapabilities } from 'vscode-languageserver-protocol/lib/protocol.colorProvider.proposed'; +import { TextDocument } from 'vscode-languageserver-types'; + +import { ConfigurationRequest } from 'vscode-languageserver-protocol/lib/protocol.configuration.proposed'; +import { DocumentColorRequest, ServerCapabilities as CPServerCapabilities, ColorPresentationRequest } from 'vscode-languageserver-protocol/lib/protocol.colorProvider.proposed'; import { getCSSLanguageService, getSCSSLanguageService, getLESSLanguageService, LanguageSettings, LanguageService, Stylesheet } from 'vscode-css-languageservice'; import { getLanguageModelCache } from './languageModelCache'; @@ -96,7 +98,7 @@ function getDocumentSettings(textDocument: TextDocument): Thenable s[0]); + promise = connection.sendRequest(ConfigurationRequest.type, configRequestParam).then(s => s[0]); documentSettings[textDocument.uri] = promise; } return promise; @@ -211,6 +213,15 @@ connection.onRequest(DocumentColorRequest.type, params => { return []; }); +connection.onRequest(ColorPresentationRequest.type, params => { + let document = documents.get(params.textDocument.uri); + if (document) { + let stylesheet = stylesheets.get(document); + return getLanguageService(document).getColorPresentations(document, stylesheet, params.colorInfo); + } + return []; +}); + connection.onRenameRequest(renameParameters => { let document = documents.get(renameParameters.textDocument.uri); let stylesheet = stylesheets.get(document); diff --git a/extensions/emmet/npm-shrinkwrap.json b/extensions/emmet/npm-shrinkwrap.json index e91e4e9c33b..85111c5d6c4 100644 --- a/extensions/emmet/npm-shrinkwrap.json +++ b/extensions/emmet/npm-shrinkwrap.json @@ -38,9 +38,9 @@ "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz" }, "vscode-emmet-helper": { - "version": "1.1.8", - "from": "vscode-emmet-helper@>=1.0.8 <2.0.0", - "resolved": "https://registry.npmjs.org/vscode-emmet-helper/-/vscode-emmet-helper-1.1.8.tgz" + "version": "1.1.9", + "from": "vscode-emmet-helper@>=1.0.9 <2.0.0", + "resolved": "https://registry.npmjs.org/vscode-emmet-helper/-/vscode-emmet-helper-1.1.9.tgz" }, "vscode-languageserver-types": { "version": "3.3.0", diff --git a/extensions/emmet/package.json b/extensions/emmet/package.json index 7e6d7155d31..8b67b7f8e89 100644 --- a/extensions/emmet/package.json +++ b/extensions/emmet/package.json @@ -265,7 +265,7 @@ "@emmetio/html-matcher": "^0.3.1", "@emmetio/css-parser": "ramya-rao-a/css-parser#vscode", "@emmetio/math-expression": "^0.1.1", - "vscode-emmet-helper": "^1.1.8", + "vscode-emmet-helper": "^1.1.9", "vscode-languageserver-types": "^3.0.3", "image-size": "^0.5.2", "vscode-nls": "2.0.2" diff --git a/extensions/emmet/src/abbreviationActions.ts b/extensions/emmet/src/abbreviationActions.ts index eaec9939c2e..c0d3758abdb 100644 --- a/extensions/emmet/src/abbreviationActions.ts +++ b/extensions/emmet/src/abbreviationActions.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { Node, HtmlNode, Rule, Property } from 'EmmetNode'; +import { Node, HtmlNode, Rule } from 'EmmetNode'; import { getNode, getInnerRange, getMappingForIncludedLanguages, parseDocument, validate, getEmmetConfiguration } from './util'; import { getExpandOptions, extractAbbreviation, extractAbbreviationFromText, isStyleSheet, isAbbreviationValid, getEmmetMode, expandAbbreviation } from 'vscode-emmet-helper'; @@ -150,7 +150,7 @@ export function expandEmmetAbbreviation(args): Thenable { } let currentNode = getNode(rootNode, position, true); - if (!isValidLocationForEmmetAbbreviation(currentNode, syntax, position, abbreviation)) { + if (!isValidLocationForEmmetAbbreviation(currentNode, syntax, position)) { return; } @@ -182,22 +182,13 @@ function fallbackTab(): Thenable { * @param syntax syntax of the abbreviation * @param position position to validate */ -export function isValidLocationForEmmetAbbreviation(currentNode: Node, syntax: string, position: vscode.Position, abbreviation: string): boolean { +export function isValidLocationForEmmetAbbreviation(currentNode: Node, syntax: string, position: vscode.Position): boolean { // Continue validation only if the file was parse-able and the currentNode has been found if (!currentNode) { return true; } if (isStyleSheet(syntax)) { - - // CSS Emmet snippets are for property-value or in case of colors, just value - if (currentNode.type === 'property' && (currentNode).value) { - if (position.isBefore((currentNode).valueToken.start)) { - return false; - } - return /^#\d+$/.test(abbreviation); - } - // If current node is a rule or at-rule, then perform additional checks to ensure // emmet suggestions are not provided in the rule selector if (currentNode.type !== 'rule' && currentNode.type !== 'at-rule') { diff --git a/extensions/emmet/src/defaultCompletionProvider.ts b/extensions/emmet/src/defaultCompletionProvider.ts index 36b07ea06aa..45daedcb868 100644 --- a/extensions/emmet/src/defaultCompletionProvider.ts +++ b/extensions/emmet/src/defaultCompletionProvider.ts @@ -111,7 +111,7 @@ export class DefaultCompletionItemProvider implements vscode.CompletionItemProvi } } - if (!isValidLocationForEmmetAbbreviation(currentNode, syntax, position, document.getText(document.getWordRangeAtPosition(position)))) { + if (!isValidLocationForEmmetAbbreviation(currentNode, syntax, position)) { return; } return syntax; diff --git a/extensions/emmet/src/test/abbreviationAction.test.ts b/extensions/emmet/src/test/abbreviationAction.test.ts index d35fc4a394b..0a43576e2bb 100644 --- a/extensions/emmet/src/test/abbreviationAction.test.ts +++ b/extensions/emmet/src/test/abbreviationAction.test.ts @@ -11,6 +11,7 @@ import { expandEmmetAbbreviation, wrapWithAbbreviation, wrapIndividualLinesWithA const cssContents = ` .boo { margin: 20px 10px; + m10 background-image: url('tryme.png'); m10 } @@ -189,9 +190,9 @@ suite('Tests for Expand Abbreviations (CSS)', () => { test('Expand abbreviation (CSS)', () => { return withRandomFileEditor(cssContents, 'css', (editor, doc) => { - editor.selection = new Selection(4, 1, 4, 4); + editor.selections = [new Selection(3, 1, 3, 4), new Selection(5, 1, 5, 4)]; return expandEmmetAbbreviation(null).then(() => { - assert.equal(editor.document.getText(), cssContents.replace('m10', 'margin: 10px;')); + assert.equal(editor.document.getText(), cssContents.replace(/m10/g, 'margin: 10px;')); return Promise.resolve(); }); }); diff --git a/extensions/emmet/src/util.ts b/extensions/emmet/src/util.ts index 66f8ba03fc8..3bb9dad8639 100644 --- a/extensions/emmet/src/util.ts +++ b/extensions/emmet/src/util.ts @@ -17,11 +17,11 @@ export const LANGUAGE_MODES: Object = { 'haml': ['!', '.', '}', ':', '*', '$', ']', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'], 'xml': ['.', '}', '*', '$', ']', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'], 'xsl': ['!', '.', '}', '*', '$', ']', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'], - 'css': [':', ';'], - 'scss': [':', ';'], - 'sass': [':'], - 'less': [':', ';'], - 'stylus': [':'], + 'css': [':', ';', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'], + 'scss': [':', ';', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'], + 'sass': [':', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'], + 'less': [':', ';', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'], + 'stylus': [':', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'], 'javascriptreact': ['.', '}', '*', '$', ']', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'], 'typescriptreact': ['.', '}', '*', '$', ']', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'] }; diff --git a/extensions/fsharp/language-configuration.json b/extensions/fsharp/language-configuration.json index 89dc2adb5a8..effac7e3835 100644 --- a/extensions/fsharp/language-configuration.json +++ b/extensions/fsharp/language-configuration.json @@ -20,5 +20,8 @@ ["(", ")"], ["\"", "\""], ["'", "'"] - ] + ], + "folding": { + "offSide": true + } } diff --git a/extensions/git/package.json b/extensions/git/package.json index 995ac785f74..a207c29d6a5 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -693,7 +693,7 @@ { "command": "git.openChange", "group": "navigation", - "when": "config.git.enabled && gitOpenRepositoryCount != 0 && !isInDiffEditor && resourceScheme != extension" + "when": "config.git.enabled && gitOpenRepositoryCount != 0 && !isInDiffEditor && resourceScheme == file" }, { "command": "git.stageSelectedRanges", diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 80fad68410e..7b4c807989d 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -169,12 +169,12 @@ export class CommandCenter { const opts: TextDocumentShowOptions = { preserveFocus, preview, - viewColumn: window.activeTextEditor && window.activeTextEditor.viewColumn || ViewColumn.One + viewColumn: ViewColumn.Active }; const activeTextEditor = window.activeTextEditor; - if (preserveSelection && activeTextEditor && activeTextEditor.document.uri.fsPath === right.fsPath) { + if (preserveSelection && activeTextEditor && activeTextEditor.document.uri.toString() === right.toString()) { opts.selection = activeTextEditor.selection; } @@ -365,10 +365,10 @@ export class CommandCenter { const opts: TextDocumentShowOptions = { preserveFocus, preview: preview, - viewColumn: activeTextEditor && activeTextEditor.viewColumn || ViewColumn.One + viewColumn: ViewColumn.Active }; - if (activeTextEditor && activeTextEditor.document.uri.fsPath === uri.fsPath) { + if (activeTextEditor && activeTextEditor.document.uri.toString() === uri.toString()) { opts.selection = activeTextEditor.selection; } diff --git a/extensions/git/src/contentProvider.ts b/extensions/git/src/contentProvider.ts index f47765c484e..ff8529d1612 100644 --- a/extensions/git/src/contentProvider.ts +++ b/extensions/git/src/contentProvider.ts @@ -101,7 +101,7 @@ export class GitContentProvider { Object.keys(this.cache).forEach(key => { const row = this.cache[key]; - const isOpen = window.visibleTextEditors.some(e => e.document.uri.fsPath === row.uri.fsPath); + const isOpen = window.visibleTextEditors.some(e => e.document.toString() === row.uri.toString()); if (isOpen || now - row.timestamp < THREE_MINUTES) { cache[row.uri.toString()] = row; diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index 3cd0dadfdac..6600d727131 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -66,7 +66,7 @@ function findSpecificGit(path: string): Promise { const buffers: Buffer[] = []; const child = cp.spawn(path, ['--version']); child.stdout.on('data', (b: Buffer) => buffers.push(b)); - child.on('error', e); + child.on('error', cpErrorHandler(e)); child.on('exit', code => code ? e(new Error('Not found')) : c({ path, version: parseVersion(Buffer.concat(buffers).toString('utf8').trim()) })); }); } @@ -159,6 +159,20 @@ export interface IExecutionResult { stderr: string; } +function cpErrorHandler(cb: (reason?: any) => void): (reason?: any) => void { + return err => { + if (/ENOENT/.test(err.message)) { + err = new GitError({ + error: err, + message: 'Failed to execute git (ENOENT)', + gitErrorCode: GitErrorCodes.NotAGitRepository + }); + } + + cb(err); + }; +} + async function exec(child: cp.ChildProcess, options: any = {}): Promise { if (!child.stdout || !child.stderr) { throw new GitError({ @@ -183,7 +197,7 @@ async function exec(child: cp.ChildProcess, options: any = {}): Promise([ new Promise((c, e) => { - once(child, 'error', e); + once(child, 'error', cpErrorHandler(e)); once(child, 'exit', c); }), new Promise(c => { @@ -919,7 +933,7 @@ export class Repository { child.stderr.setEncoding('utf8'); child.stderr.on('data', raw => stderrData.push(raw as string)); - child.on('error', e); + child.on('error', cpErrorHandler(e)); child.on('exit', onExit); }); } diff --git a/extensions/html/client/src/htmlMain.ts b/extensions/html/client/src/htmlMain.ts index 943d064b9ba..2dc9899c125 100644 --- a/extensions/html/client/src/htmlMain.ts +++ b/extensions/html/client/src/htmlMain.ts @@ -6,15 +6,14 @@ import * as path from 'path'; -import { languages, ExtensionContext, IndentAction, Position, TextDocument, Color, ColorInformation, ColorPresentation, TextEdit } from 'vscode'; +import { languages, ExtensionContext, IndentAction, Position, TextDocument, Color, ColorInformation, ColorPresentation } from 'vscode'; import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind, RequestType, TextDocumentPositionParams } from 'vscode-languageclient'; import { EMPTY_ELEMENTS } from './htmlEmptyTagsShared'; import { activateTagClosing } from './tagClosing'; import TelemetryReporter from 'vscode-extension-telemetry'; -import * as convert from 'color-convert'; -import { ConfigurationFeature } from 'vscode-languageclient/lib/proposed'; -import { DocumentColorRequest } from 'vscode-languageserver-protocol/lib/protocol.colorProvider.proposed'; +import { ConfigurationFeature } from 'vscode-languageclient/lib/configuration.proposed'; +import { DocumentColorRequest, DocumentColorParams, ColorPresentationRequest, ColorPresentationParams } from 'vscode-languageserver-protocol/lib/protocol.colorProvider.proposed'; import * as nls from 'vscode-nls'; let localize = nls.loadMessageBundle(); @@ -68,54 +67,35 @@ export function activate(context: ExtensionContext) { let client = new LanguageClient('html', localize('htmlserver.name', 'HTML Language Server'), serverOptions, clientOptions); client.registerFeature(new ConfigurationFeature(client)); - var _toTwoDigitHex = function (n: number): string { - const r = n.toString(16); - return r.length !== 2 ? '0' + r : r; - }; - let disposable = client.start(); toDispose.push(disposable); client.onReady().then(() => { disposable = languages.registerColorProvider(documentSelector, { provideDocumentColors(document: TextDocument): Thenable { - let params = client.code2ProtocolConverter.asDocumentSymbolParams(document); + let params: DocumentColorParams = { + textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document) + }; return client.sendRequest(DocumentColorRequest.type, params).then(symbols => { return symbols.map(symbol => { let range = client.protocol2CodeConverter.asRange(symbol.range); - let color = new Color(symbol.color.red * 255, symbol.color.green * 255, symbol.color.blue * 255, symbol.color.alpha); + let color = new Color(symbol.color.red, symbol.color.green, symbol.color.blue, symbol.color.alpha); return new ColorInformation(range, color); }); }); }, - provideColorPresentations(colorInfo: ColorInformation): ColorPresentation[] | Thenable { - let result: ColorPresentation[] = []; - let color = colorInfo.color; - let label; - if (color.alpha === 1) { - label = `rgb(${Math.round(color.red * 255)}, ${Math.round(color.green * 255)}, ${Math.round(color.blue * 255)})`; - } else { - label = `rgba(${Math.round(color.red * 255)}, ${Math.round(color.green * 255)}, ${Math.round(color.blue * 255)}, ${color.alpha})`; - } - - result.push({ label: label, textEdit: new TextEdit(colorInfo.range, label) }); - - if (color.alpha === 1) { - label = `#${_toTwoDigitHex(Math.round(color.red * 255))}${_toTwoDigitHex(Math.round(color.green * 255))}${_toTwoDigitHex(Math.round(color.blue * 255))}`; - } else { - label = `#${_toTwoDigitHex(Math.round(color.red * 255))}${_toTwoDigitHex(Math.round(color.green * 255))}${_toTwoDigitHex(Math.round(color.blue * 255))}${_toTwoDigitHex(Math.round(color.alpha * 255))}`; - } - - result.push({ label: label, textEdit: new TextEdit(colorInfo.range, label) }); - - const hsl = convert.rgb.hsl(Math.round(color.red * 255), Math.round(color.green * 255), Math.round(color.blue * 255)); - if (color.alpha === 1) { - label = `hsl(${hsl[0]}, ${hsl[1]}%, ${hsl[2]}%)`; - } else { - label = `hsla(${hsl[0]}, ${hsl[1]}%, ${hsl[2]}%, ${color.alpha})`; - } - - result.push({ label: label, textEdit: new TextEdit(colorInfo.range, label) }); - return result; + provideColorPresentations(document: TextDocument, colorInfo: ColorInformation): Thenable { + let params: ColorPresentationParams = { + textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document), + colorInfo: { range: client.code2ProtocolConverter.asRange(colorInfo.range), color: colorInfo.color } + }; + return client.sendRequest(ColorPresentationRequest.type, params).then(presentations => { + return presentations.map(p => { + let presentation = new ColorPresentation(p.label); + presentation.textEdit = p.textEdit && client.protocol2CodeConverter.asTextEdit(p.textEdit); + presentation.additionalTextEdits = p.additionalTextEdits && client.protocol2CodeConverter.asTextEdits(p.additionalTextEdits); + return presentation; + }); + }); } }); toDispose.push(disposable); diff --git a/extensions/html/npm-shrinkwrap.json b/extensions/html/npm-shrinkwrap.json index 4f5dd2f3686..97c20854f46 100644 --- a/extensions/html/npm-shrinkwrap.json +++ b/extensions/html/npm-shrinkwrap.json @@ -2,40 +2,40 @@ "name": "html", "version": "0.1.0", "dependencies": { - "color-convert": { - "version": "0.5.3", - "from": "color-convert@>=0.5.0 <0.6.0", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-0.5.3.tgz" - }, "applicationinsights": { "version": "0.18.0", "from": "applicationinsights@0.18.0", "resolved": "https://registry.npmjs.org/applicationinsights/-/applicationinsights-0.18.0.tgz" }, + "color-convert": { + "version": "0.5.3", + "from": "color-convert@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-0.5.3.tgz" + }, "vscode-extension-telemetry": { "version": "0.0.8", "from": "vscode-extension-telemetry@>=0.0.8 <0.0.9", "resolved": "https://registry.npmjs.org/vscode-extension-telemetry/-/vscode-extension-telemetry-0.0.8.tgz" }, "vscode-jsonrpc": { - "version": "3.3.1", - "from": "vscode-jsonrpc@>=3.3.0 <4.0.0", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-3.3.1.tgz" + "version": "3.4.0", + "from": "vscode-jsonrpc@>=3.4.0 <4.0.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-3.4.0.tgz" }, "vscode-languageclient": { - "version": "3.4.0-next.17", + "version": "3.4.2", "from": "vscode-languageclient@next", - "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-3.4.0-next.17.tgz" + "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-3.4.2.tgz" }, "vscode-languageserver-protocol": { - "version": "3.1.1", - "from": "vscode-languageserver-protocol@>=3.1.1 <4.0.0", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.1.1.tgz" + "version": "3.4.2", + "from": "vscode-languageserver-protocol@3.4.2", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.4.2.tgz" }, "vscode-languageserver-types": { - "version": "3.3.0", - "from": "vscode-languageserver-types@>=3.3.0 <4.0.0", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.3.0.tgz" + "version": "3.4.0", + "from": "vscode-languageserver-types@3.4.0", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.4.0.tgz" }, "vscode-nls": { "version": "2.0.2", diff --git a/extensions/html/package.json b/extensions/html/package.json index 240cb7d8d9f..6926ac3f85a 100644 --- a/extensions/html/package.json +++ b/extensions/html/package.json @@ -215,11 +215,10 @@ } }, "dependencies": { - "color-convert": "^0.5.3", "vscode-extension-telemetry": "0.0.8", - "vscode-languageclient": "3.4.0-next.17", - "vscode-languageserver-protocol": "^3.1.1", - "vscode-languageserver-types": "^3.3.0", + "vscode-languageclient": "^3.4.2", + "vscode-languageserver-protocol": "^3.4.2", + "vscode-languageserver-types": "^3.4.0", "vscode-nls": "2.0.2" }, "devDependencies": { diff --git a/extensions/html/server/npm-shrinkwrap.json b/extensions/html/server/npm-shrinkwrap.json index 52f9b38450f..6f187b944b3 100644 --- a/extensions/html/server/npm-shrinkwrap.json +++ b/extensions/html/server/npm-shrinkwrap.json @@ -3,9 +3,9 @@ "version": "1.0.0", "dependencies": { "vscode-css-languageservice": { - "version": "2.1.6", + "version": "2.1.7", "from": "vscode-css-languageservice@next", - "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-2.1.6.tgz" + "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-2.1.7.tgz" }, "vscode-html-languageservice": { "version": "2.0.8", @@ -13,24 +13,24 @@ "resolved": "https://registry.npmjs.org/vscode-html-languageservice/-/vscode-html-languageservice-2.0.8.tgz" }, "vscode-jsonrpc": { - "version": "3.3.1", - "from": "vscode-jsonrpc@>=3.3.0 <4.0.0", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-3.3.1.tgz" + "version": "3.4.0", + "from": "vscode-jsonrpc@>=3.4.0 <4.0.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-3.4.0.tgz" }, "vscode-languageserver": { - "version": "3.4.0-next.6", + "version": "3.4.2", "from": "vscode-languageserver@next", - "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-3.4.0-next.6.tgz" + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-3.4.2.tgz" }, "vscode-languageserver-protocol": { - "version": "3.1.1", - "from": "vscode-languageserver-protocol@>=3.1.1 <4.0.0", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.1.1.tgz" + "version": "3.4.2", + "from": "vscode-languageserver-protocol@3.4.2", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.4.2.tgz" }, "vscode-languageserver-types": { - "version": "3.3.0", - "from": "vscode-languageserver-types@>=3.3.0 <4.0.0", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.3.0.tgz" + "version": "3.4.0", + "from": "vscode-languageserver-types@3.4.0", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.4.0.tgz" }, "vscode-nls": { "version": "2.0.2", diff --git a/extensions/html/server/package.json b/extensions/html/server/package.json index 8bcae7d7d90..56c6df7218e 100644 --- a/extensions/html/server/package.json +++ b/extensions/html/server/package.json @@ -8,11 +8,11 @@ "node": "*" }, "dependencies": { - "vscode-css-languageservice": "^2.1.6", + "vscode-css-languageservice": "^2.1.7", "vscode-html-languageservice": "^2.0.8", - "vscode-languageserver": "3.4.0-next.6", - "vscode-languageserver-protocol": "^3.1.1", - "vscode-languageserver-types": "^3.3.0", + "vscode-languageserver": "^3.4.2", + "vscode-languageserver-protocol": "^3.4.2", + "vscode-languageserver-types": "^3.4.0", "vscode-nls": "^2.0.2", "vscode-uri": "^1.0.1" }, diff --git a/extensions/html/server/src/htmlServerMain.ts b/extensions/html/server/src/htmlServerMain.ts index 14ea13ba25c..3f7258197a8 100644 --- a/extensions/html/server/src/htmlServerMain.ts +++ b/extensions/html/server/src/htmlServerMain.ts @@ -4,13 +4,13 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { createConnection, IConnection, TextDocuments, InitializeParams, InitializeResult, RequestType, DocumentRangeFormattingRequest, Disposable, DocumentSelector, GetConfigurationParams, TextDocumentPositionParams, ServerCapabilities, Position } from 'vscode-languageserver'; +import { createConnection, IConnection, TextDocuments, InitializeParams, InitializeResult, RequestType, DocumentRangeFormattingRequest, Disposable, DocumentSelector, TextDocumentPositionParams, ServerCapabilities, Position } from 'vscode-languageserver'; import { DocumentContext } from 'vscode-html-languageservice'; import { TextDocument, Diagnostic, DocumentLink, SymbolInformation } from 'vscode-languageserver-types'; import { getLanguageModes, LanguageModes, Settings } from './modes/languageModes'; -import { GetConfigurationRequest } from 'vscode-languageserver-protocol/lib/protocol.configuration.proposed'; -import { DocumentColorRequest, ServerCapabilities as CPServerCapabilities, ColorInformation } from 'vscode-languageserver-protocol/lib/protocol.colorProvider.proposed'; +import { ConfigurationRequest, ConfigurationParams } from 'vscode-languageserver-protocol/lib/protocol.configuration.proposed'; +import { DocumentColorRequest, ServerCapabilities as CPServerCapabilities, ColorInformation, ColorPresentationRequest } from 'vscode-languageserver-protocol/lib/protocol.colorProvider.proposed'; import { format } from './modes/formatting'; import { pushAll } from './utils/arrays'; @@ -58,8 +58,8 @@ function getDocumentSettings(textDocument: TextDocument, needsDocumentSettings: let promise = documentSettings[textDocument.uri]; if (!promise) { let scopeUri = textDocument.uri; - let configRequestParam: GetConfigurationParams = { items: [{ scopeUri, section: 'css' }, { scopeUri, section: 'html' }, { scopeUri, section: 'javascript' }] }; - promise = connection.sendRequest(GetConfigurationRequest.type, configRequestParam).then(s => ({ css: s[0], html: s[1], javascript: s[2] })); + let configRequestParam: ConfigurationParams = { items: [{ scopeUri, section: 'css' }, { scopeUri, section: 'html' }, { scopeUri, section: 'javascript' }] }; + promise = connection.sendRequest(ConfigurationRequest.type, configRequestParam).then(s => ({ css: s[0], html: s[1], javascript: s[2] })); documentSettings[textDocument.uri] = promise; } return promise; @@ -323,6 +323,17 @@ connection.onRequest(DocumentColorRequest.type, params => { return infos; }); +connection.onRequest(ColorPresentationRequest.type, params => { + let document = documents.get(params.textDocument.uri); + if (document) { + let mode = languageModes.getModeAtPosition(document, params.colorInfo.range.start); + if (mode && mode.getColorPresentations) { + return mode.getColorPresentations(document, params.colorInfo); + } + } + return []; +}); + connection.onRequest(TagCloseRequest.type, params => { let document = documents.get(params.textDocument.uri); if (document) { diff --git a/extensions/html/server/src/modes/cssMode.ts b/extensions/html/server/src/modes/cssMode.ts index dcb221e83d5..0dc06cc10ba 100644 --- a/extensions/html/server/src/modes/cssMode.ts +++ b/extensions/html/server/src/modes/cssMode.ts @@ -7,7 +7,7 @@ import { LanguageModelCache, getLanguageModelCache } from '../languageModelCache'; import { TextDocument, Position } from 'vscode-languageserver-types'; import { getCSSLanguageService, Stylesheet } from 'vscode-css-languageservice'; -import { LanguageMode, Settings } from './languageModes'; +import { LanguageMode, Settings, ColorInformation } from './languageModes'; import { HTMLDocumentRegions, CSS_STYLE_RULE } from './embeddedSupport'; export function getCSSMode(documentRegions: LanguageModelCache): LanguageMode { @@ -54,6 +54,10 @@ export function getCSSMode(documentRegions: LanguageModelCache Location[]; format?: (document: TextDocument, range: Range, options: FormattingOptions, settings: Settings) => TextEdit[]; findDocumentColors?: (document: TextDocument) => ColorInformation[]; + getColorPresentations?: (document: TextDocument, colorInfo: ColorInformation) => ColorPresentation[]; doAutoClose?: (document: TextDocument, position: Position) => string; onDocumentRemoved(document: TextDocument): void; dispose(): void; diff --git a/extensions/javascript/javascript-language-configuration.json b/extensions/javascript/javascript-language-configuration.json index f25940db455..4080becc987 100644 --- a/extensions/javascript/javascript-language-configuration.json +++ b/extensions/javascript/javascript-language-configuration.json @@ -24,5 +24,11 @@ ["'", "'"], ["\"", "\""], ["`", "`"] - ] + ], + "folding": { + "markers": { + "start": "^\\s*//\\s*#region", + "end": "^\\s*//\\s*#endregion" + } + } } \ No newline at end of file diff --git a/extensions/javascript/snippets/javascript.json b/extensions/javascript/snippets/javascript.json index bdae6179418..ec6c69161a3 100644 --- a/extensions/javascript/snippets/javascript.json +++ b/extensions/javascript/snippets/javascript.json @@ -137,5 +137,12 @@ "/// $0" ], "description": "Relative Reference to another File" + }, + "Import external module.": { + "prefix": "import statement", + "body": [ + "import { $0 } from \"${1:module}\";" + ], + "description": "Import external module." } } diff --git a/extensions/javascript/snippets/javascriptreact.json b/extensions/javascript/snippets/javascriptreact.json index 0566af20e7b..49d8cff0d09 100644 --- a/extensions/javascript/snippets/javascriptreact.json +++ b/extensions/javascript/snippets/javascriptreact.json @@ -137,5 +137,12 @@ "/// $0" ], "description": "Relative Reference to another File" + }, + "Import external module.": { + "prefix": "import statement", + "body": [ + "import { $0 } from \"${1:module}\";" + ], + "description": "Import external module." } } diff --git a/extensions/json/client/src/jsonMain.ts b/extensions/json/client/src/jsonMain.ts index a650ed5aa23..e1e94e1de80 100644 --- a/extensions/json/client/src/jsonMain.ts +++ b/extensions/json/client/src/jsonMain.ts @@ -6,11 +6,11 @@ import * as path from 'path'; -import { workspace, languages, ExtensionContext, extensions, Uri, TextDocument, ColorInformation, Color, ColorPresentation, TextEdit } from 'vscode'; +import { workspace, languages, ExtensionContext, extensions, Uri, TextDocument, ColorInformation, Color, ColorPresentation } from 'vscode'; import { LanguageClient, LanguageClientOptions, RequestType, ServerOptions, TransportKind, NotificationType, DidChangeConfigurationNotification } from 'vscode-languageclient'; import TelemetryReporter from 'vscode-extension-telemetry'; -import { ConfigurationFeature } from 'vscode-languageclient/lib/proposed'; -import { DocumentColorRequest } from 'vscode-languageserver-protocol/lib/protocol.colorProvider.proposed'; +import { ConfigurationFeature } from 'vscode-languageclient/lib/configuration.proposed'; +import { DocumentColorRequest, DocumentColorParams, ColorPresentationParams, ColorPresentationRequest } from 'vscode-languageserver-protocol/lib/protocol.colorProvider.proposed'; import * as nls from 'vscode-nls'; @@ -116,35 +116,33 @@ export function activate(context: ExtensionContext) { client.sendNotification(SchemaAssociationNotification.type, getSchemaAssociation(context)); - var _toTwoDigitHex = function (n: number): string { - const r = n.toString(16); - return r.length !== 2 ? '0' + r : r; - }; // register color provider context.subscriptions.push(languages.registerColorProvider(documentSelector, { provideDocumentColors(document: TextDocument): Thenable { - let params = client.code2ProtocolConverter.asDocumentSymbolParams(document); + let params: DocumentColorParams = { + textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document) + }; return client.sendRequest(DocumentColorRequest.type, params).then(symbols => { return symbols.map(symbol => { let range = client.protocol2CodeConverter.asRange(symbol.range); - let color = new Color(symbol.color.red * 255, symbol.color.green * 255, symbol.color.blue * 255, symbol.color.alpha); + let color = new Color(symbol.color.red, symbol.color.green, symbol.color.blue, symbol.color.alpha); return new ColorInformation(range, color); }); }); }, - provideColorPresentations(colorInfo: ColorInformation): ColorPresentation[] | Thenable { - let result: ColorPresentation[] = []; - let color = colorInfo.color; - let label; - - if (color.alpha === 1) { - label = `#${_toTwoDigitHex(Math.round(color.red * 255))}${_toTwoDigitHex(Math.round(color.green * 255))}${_toTwoDigitHex(Math.round(color.blue * 255))}`; - } else { - label = `#${_toTwoDigitHex(Math.round(color.red * 255))}${_toTwoDigitHex(Math.round(color.green * 255))}${_toTwoDigitHex(Math.round(color.blue * 255))}${_toTwoDigitHex(Math.round(color.alpha * 255))}`; - } - - result.push({ label: label, textEdit: new TextEdit(colorInfo.range, label) }); - return result; + provideColorPresentations(document: TextDocument, colorInfo: ColorInformation): Thenable { + let params: ColorPresentationParams = { + textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document), + colorInfo: { range: client.code2ProtocolConverter.asRange(colorInfo.range), color: colorInfo.color } + }; + return client.sendRequest(ColorPresentationRequest.type, params).then(presentations => { + return presentations.map(p => { + let presentation = new ColorPresentation(p.label); + presentation.textEdit = p.textEdit && client.protocol2CodeConverter.asTextEdit(p.textEdit); + presentation.additionalTextEdits = p.additionalTextEdits && client.protocol2CodeConverter.asTextEdits(p.additionalTextEdits); + return presentation; + }); + }); } })); }); diff --git a/extensions/json/npm-shrinkwrap.json b/extensions/json/npm-shrinkwrap.json index 4caf64d28b7..2b10b555249 100644 --- a/extensions/json/npm-shrinkwrap.json +++ b/extensions/json/npm-shrinkwrap.json @@ -13,24 +13,24 @@ "resolved": "https://registry.npmjs.org/vscode-extension-telemetry/-/vscode-extension-telemetry-0.0.8.tgz" }, "vscode-jsonrpc": { - "version": "3.3.1", - "from": "vscode-jsonrpc@>=3.3.0 <4.0.0", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-3.3.1.tgz" + "version": "3.4.0", + "from": "vscode-jsonrpc@>=3.4.0 <4.0.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-3.4.0.tgz" }, "vscode-languageclient": { - "version": "3.4.0-next.17", + "version": "3.4.2", "from": "vscode-languageclient@next", - "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-3.4.0-next.17.tgz" + "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-3.4.2.tgz" }, "vscode-languageserver-protocol": { - "version": "3.1.1", - "from": "vscode-languageserver-protocol@>=3.1.1 <4.0.0", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.1.1.tgz" + "version": "3.4.2", + "from": "vscode-languageserver-protocol@3.4.2", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.4.2.tgz" }, "vscode-languageserver-types": { - "version": "3.3.0", - "from": "vscode-languageserver-types@>=3.3.0 <4.0.0", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.3.0.tgz" + "version": "3.4.0", + "from": "vscode-languageserver-types@>=3.4.0 <4.0.0", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.4.0.tgz" }, "vscode-nls": { "version": "2.0.2", @@ -43,4 +43,4 @@ "resolved": "https://registry.npmjs.org/winreg/-/winreg-1.2.3.tgz" } } -} +} \ No newline at end of file diff --git a/extensions/json/package.json b/extensions/json/package.json index ba30bb1045d..7d49798f446 100644 --- a/extensions/json/package.json +++ b/extensions/json/package.json @@ -133,8 +133,8 @@ }, "dependencies": { "vscode-extension-telemetry": "0.0.8", - "vscode-languageclient": "3.4.0-next.17", - "vscode-languageserver-protocol": "^3.1.1", + "vscode-languageclient": "^3.4.2", + "vscode-languageserver-protocol": "^3.4.2", "vscode-nls": "2.0.2" }, "devDependencies": { diff --git a/extensions/json/server/npm-shrinkwrap.json b/extensions/json/server/npm-shrinkwrap.json index 3bcdb83008a..31c46482d9a 100644 --- a/extensions/json/server/npm-shrinkwrap.json +++ b/extensions/json/server/npm-shrinkwrap.json @@ -43,29 +43,29 @@ "resolved": "https://registry.npmjs.org/request-light/-/request-light-0.2.1.tgz" }, "vscode-json-languageservice": { - "version": "2.0.18", + "version": "2.0.20", "from": "vscode-json-languageservice@next", - "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-2.0.18.tgz" + "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-2.0.20.tgz" }, "vscode-jsonrpc": { - "version": "3.3.1", - "from": "vscode-jsonrpc@>=3.3.0 <4.0.0", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-3.3.1.tgz" + "version": "3.4.0", + "from": "vscode-jsonrpc@>=3.4.0 <4.0.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-3.4.0.tgz" }, "vscode-languageserver": { - "version": "3.4.0-next.6", + "version": "3.4.2", "from": "vscode-languageserver@next", - "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-3.4.0-next.6.tgz" + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-3.4.2.tgz" }, "vscode-languageserver-protocol": { - "version": "3.1.1", - "from": "vscode-languageserver-protocol@>=3.1.1 <4.0.0", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.1.1.tgz" + "version": "3.4.2", + "from": "vscode-languageserver-protocol@3.4.2", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.4.2.tgz" }, "vscode-languageserver-types": { - "version": "3.3.0", - "from": "vscode-languageserver-types@>=3.0.3 <4.0.0", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.3.0.tgz" + "version": "3.4.0", + "from": "vscode-languageserver-types@3.4.0", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.4.0.tgz" }, "vscode-nls": { "version": "2.0.2", diff --git a/extensions/json/server/package.json b/extensions/json/server/package.json index 684e9c35d28..068d0e7174b 100644 --- a/extensions/json/server/package.json +++ b/extensions/json/server/package.json @@ -10,9 +10,10 @@ "dependencies": { "jsonc-parser": "^1.0.0", "request-light": "^0.2.1", - "vscode-json-languageservice": "^2.0.18", - "vscode-languageserver": "3.4.0-next.6", - "vscode-languageserver-protocol": "^3.1.1", + "vscode-json-languageservice": "^2.0.20", + "vscode-languageserver": "^3.4.2", + "vscode-languageserver-protocol": "^3.4.2", + "vscode-languageserver-types": "^3.4.0", "vscode-nls": "^2.0.2", "vscode-uri": "^1.0.1" }, diff --git a/extensions/json/server/src/jsonServerMain.ts b/extensions/json/server/src/jsonServerMain.ts index 4d1c416c41c..64b604093d9 100644 --- a/extensions/json/server/src/jsonServerMain.ts +++ b/extensions/json/server/src/jsonServerMain.ts @@ -10,7 +10,7 @@ import { DocumentRangeFormattingRequest, Disposable, ServerCapabilities } from 'vscode-languageserver'; -import { DocumentColorRequest, ServerCapabilities as CPServerCapabilities } from 'vscode-languageserver-protocol/lib/protocol.colorProvider.proposed'; +import { DocumentColorRequest, ServerCapabilities as CPServerCapabilities, ColorPresentationRequest } from 'vscode-languageserver-protocol/lib/protocol.colorProvider.proposed'; import { xhr, XHRResponse, configure as configureHttpRequests, getErrorStatusDescription } from 'request-light'; import fs = require('fs'); @@ -311,5 +311,14 @@ connection.onRequest(DocumentColorRequest.type, params => { return []; }); +connection.onRequest(ColorPresentationRequest.type, params => { + let document = documents.get(params.textDocument.uri); + if (document) { + let jsonDocument = getJSONDocument(document); + return languageService.getColorPresentations(document, jsonDocument, params.colorInfo); + } + return []; +}); + // Listen on the connection connection.listen(); \ No newline at end of file diff --git a/extensions/markdown/language-configuration.json b/extensions/markdown/language-configuration.json index 6c811c66aa6..6fa9139b76f 100644 --- a/extensions/markdown/language-configuration.json +++ b/extensions/markdown/language-configuration.json @@ -37,5 +37,8 @@ ["(", ")"], ["[", "]"], ["`", "`"] - ] + ], + "folding": { + "offSide": true + } } \ No newline at end of file diff --git a/extensions/markdown/src/extension.ts b/extensions/markdown/src/extension.ts index 2dd53b296d7..b684f8ad743 100644 --- a/extensions/markdown/src/extension.ts +++ b/extensions/markdown/src/extension.ts @@ -117,7 +117,7 @@ export function activate(context: vscode.ExtensionContext) { logger.log('revealLine', { uri, sourceUri: sourceUri.toString(), line }); vscode.window.visibleTextEditors - .filter(editor => isMarkdownFile(editor.document) && editor.document.uri.fsPath === sourceUri.fsPath) + .filter(editor => isMarkdownFile(editor.document) && editor.document.uri.toString() === sourceUri.toString()) .forEach(editor => { const sourceLine = Math.floor(line); const fraction = line - sourceLine; @@ -253,7 +253,7 @@ function showPreview(cspArbiter: ExtensionContentSecurityPolicyArbiter, uri?: vs const thenable = vscode.commands.executeCommand('vscode.previewHtml', getMarkdownUri(resource), getViewColumn(sideBySide), - `Preview '${path.basename(resource.fsPath)}'`, + localize('previewTitle', 'Preview {0}', path.basename(resource.fsPath)), { allowScripts: true, allowSvgs: cspArbiter.shouldAllowSvgsForResource(resource) @@ -296,7 +296,7 @@ function showSource(mdUri: vscode.Uri) { const docUri = vscode.Uri.parse(mdUri.query); for (const editor of vscode.window.visibleTextEditors) { - if (editor.document.uri.scheme === docUri.scheme && editor.document.uri.fsPath === docUri.fsPath) { + if (editor.document.uri.scheme === docUri.scheme && editor.document.uri.toString() === docUri.toString()) { return vscode.window.showTextDocument(editor.document, editor.viewColumn); } } diff --git a/extensions/markdown/src/previewContentProvider.ts b/extensions/markdown/src/previewContentProvider.ts index a7eef2330a3..870816c1eeb 100644 --- a/extensions/markdown/src/previewContentProvider.ts +++ b/extensions/markdown/src/previewContentProvider.ts @@ -205,7 +205,7 @@ export class MDDocumentContentProvider implements vscode.TextDocumentContentProv let initialLine: number | undefined = undefined; const editor = vscode.window.activeTextEditor; - if (editor && editor.document.uri.fsPath === sourceUri.fsPath) { + if (editor && editor.document.uri.toString() === sourceUri.toString()) { initialLine = editor.selection.active.line; } diff --git a/extensions/markdown/src/tableOfContentsProvider.ts b/extensions/markdown/src/tableOfContentsProvider.ts index 26e64e4d837..c93dd25f6e7 100644 --- a/extensions/markdown/src/tableOfContentsProvider.ts +++ b/extensions/markdown/src/tableOfContentsProvider.ts @@ -79,7 +79,7 @@ export class TableOfContentsProvider { } private static getHeaderText(header: string): string { - return header.replace(/^\s*#+\s*(.*?)\s*\1*$/, (_, word) => `${word.trim()}`); + return header.replace(/^\s*#+\s*(.*?)\s*#*$/, (_, word) => word.trim()); } public static slugify(header: string): string { diff --git a/extensions/npm-shrinkwrap.json b/extensions/npm-shrinkwrap.json index 2cdcc763dd5..884010a7c42 100644 --- a/extensions/npm-shrinkwrap.json +++ b/extensions/npm-shrinkwrap.json @@ -3,9 +3,9 @@ "version": "0.0.1", "dependencies": { "typescript": { - "version": "2.5.3-insiders.20170909", - "from": "typescript@2.5.3-insiders.20170909", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.5.3-insiders.20170909.tgz" + "version": "2.5.3-insiders.20170922", + "from": "typescript@2.5.3-insiders.20170922", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.5.3-insiders.20170922.tgz" } } } diff --git a/extensions/npm/package.json b/extensions/npm/package.json index 07387994960..183680250e8 100644 --- a/extensions/npm/package.json +++ b/extensions/npm/package.json @@ -37,11 +37,13 @@ "on" ], "default": "on", + "scope": "resource", "description": "%config.npm.autoDetect%" }, "npm.runSilent": { "type": "boolean", - "default": false, + "default": false, + "scope": "resource", "description": "%config.npm.runSilent%" } } diff --git a/extensions/npm/src/main.ts b/extensions/npm/src/main.ts index 6c22119b342..925622f2010 100644 --- a/extensions/npm/src/main.ts +++ b/extensions/npm/src/main.ts @@ -17,24 +17,14 @@ export function activate(_context: vscode.ExtensionContext): void { return; } - function onConfigurationChanged() { - let autoDetect = vscode.workspace.getConfiguration('npm').get('autoDetect'); - if (taskProvider && autoDetect === 'off') { - taskProvider.dispose(); - taskProvider = undefined; - } else if (!taskProvider && autoDetect === 'on') { - taskProvider = vscode.workspace.registerTaskProvider('npm', { - provideTasks: () => { - return provideNpmScripts(); - }, - resolveTask(_task: vscode.Task): vscode.Task | undefined { - return undefined; - } - }); + taskProvider = vscode.workspace.registerTaskProvider('npm', { + provideTasks: () => { + return provideNpmScripts(); + }, + resolveTask(_task: vscode.Task): vscode.Task | undefined { + return undefined; } - } - vscode.workspace.onDidChangeConfiguration(onConfigurationChanged); - onConfigurationChanged(); + }); } export function deactivate(): void { @@ -100,19 +90,27 @@ async function provideNpmScripts(): Promise { return emptyTasks; } - const isSingleRoot = folders.length === 1; - for (let i = 0; i < folders.length; i++) { - let tasks = await provideNpmScriptsForFolder(folders[i], isSingleRoot); - allTasks.push(...tasks); + if (isEnabled(folders[i])) { + let tasks = await provideNpmScriptsForFolder(folders[i]); + allTasks.push(...tasks); + } } return allTasks; } -async function provideNpmScriptsForFolder(folder: vscode.WorkspaceFolder, singleRoot: boolean): Promise { - let rootPath = folder.uri.fsPath; +function isEnabled(folder: vscode.WorkspaceFolder): boolean { + return vscode.workspace.getConfiguration('npm', folder.uri).get('autoDetect') === 'on'; +} + +async function provideNpmScriptsForFolder(folder: vscode.WorkspaceFolder): Promise { let emptyTasks: vscode.Task[] = []; + if (folder.uri.scheme !== 'file') { + return emptyTasks; + } + let rootPath = folder.uri.fsPath; + let packageJson = path.join(rootPath, 'package.json'); if (!await exists(packageJson)) { return emptyTasks; @@ -127,7 +125,7 @@ async function provideNpmScriptsForFolder(folder: vscode.WorkspaceFolder, single const result: vscode.Task[] = []; Object.keys(json.scripts).filter(isNotPreOrPostScript).forEach(each => { - const task = createTask(each, `run ${each}`, rootPath, folder.name, singleRoot); + const task = createTask(each, `run ${each}`, rootPath, folder); const lowerCaseTaskName = each.toLowerCase(); if (isBuildTask(lowerCaseTaskName)) { task.group = vscode.TaskGroup.Build; @@ -137,24 +135,21 @@ async function provideNpmScriptsForFolder(folder: vscode.WorkspaceFolder, single result.push(task); }); // always add npm install (without a problem matcher) - result.push(createTask('install', 'install', rootPath, folder.name, singleRoot, [])); + result.push(createTask('install', 'install', rootPath, folder, [])); return result; } catch (e) { return emptyTasks; } } -function createTask(script: string, cmd: string, rootPath: string, shortPath: string, singleRoot: boolean, matcher?: any): vscode.Task { +function createTask(script: string, cmd: string, rootPath: string, folder: vscode.WorkspaceFolder, matcher?: any): vscode.Task { - function getTaskName(script: string, shortPath: string, singleRoot: boolean) { - if (singleRoot) { - return script; - } - return `${script} - ${shortPath}`; + function getTaskName(script: string) { + return script; } - function getNpmCommandLine(cmd: string): string { - if (vscode.workspace.getConfiguration('npm').get('runSilent')) { + function getNpmCommandLine(folder: vscode.WorkspaceFolder, cmd: string): string { + if (vscode.workspace.getConfiguration('npm', folder.uri).get('runSilent')) { return `npm --silent ${cmd}`; } return `npm ${cmd}`; @@ -164,7 +159,6 @@ function createTask(script: string, cmd: string, rootPath: string, shortPath: st type: 'npm', script: script }; - let taskName = getTaskName(script, shortPath, singleRoot); - - return new vscode.Task(kind, taskName, 'npm', new vscode.ShellExecution(getNpmCommandLine(cmd), { cwd: rootPath }), matcher); + let taskName = getTaskName(script); + return new vscode.Task(kind, folder, taskName, 'npm', new vscode.ShellExecution(getNpmCommandLine(folder, cmd), { cwd: rootPath }), matcher); } \ No newline at end of file diff --git a/extensions/package.json b/extensions/package.json index 3ad8d484a3d..30339830b0a 100644 --- a/extensions/package.json +++ b/extensions/package.json @@ -3,7 +3,7 @@ "version": "0.0.1", "description": "Dependencies shared by all extensions", "dependencies": { - "typescript": "2.5.3-insiders.20170909" + "typescript": "2.5.3-insiders.20170922" }, "scripts": { "postinstall": "node ./postinstall" diff --git a/extensions/powershell/language-configuration.json b/extensions/powershell/language-configuration.json index b409b3ce4c0..1227fef697d 100644 --- a/extensions/powershell/language-configuration.json +++ b/extensions/powershell/language-configuration.json @@ -22,5 +22,11 @@ ["(", ")"], ["\"", "\""], ["'", "'"] - ] + ], + "folding": { + "markers": { + "start": "^\\s*#region", + "end": "^\\s*#endregion" + } + } } \ No newline at end of file diff --git a/extensions/powershell/syntaxes/PowershellSyntax.tmLanguage b/extensions/powershell/syntaxes/PowershellSyntax.tmLanguage index f54cf896b52..5a8c8f24ce3 100644 --- a/extensions/powershell/syntaxes/PowershellSyntax.tmLanguage +++ b/extensions/powershell/syntaxes/PowershellSyntax.tmLanguage @@ -246,7 +246,7 @@ match - (?<!\w)((?i:begin|break|catch|continue|data|define|do|dynamicparam|else|elseif|end|exit|finally|for|foreach(?!-object)|from|if|in|inlinescript|parallel|param|process|return|switch|throw|trap|try|until|using|var|where(?!=-object)|while)|%|\?)(?!\w) + (?<!\w)((?i:begin|break|catch|continue|data|define|do|dynamicparam|else|elseif|end|exit|finally|for|foreach(?!-object)|from|if|in|inlinescript|parallel|param|process|return|switch|throw|trap|try|until|using|var|where(?!-object)|while)|%|\?)(?!\w) name keyword.control.powershell @@ -426,6 +426,14 @@ name support.function.powershell + + comment + Builtin cmdlets with reserved verbs + match + (?<!\w)(?i:where-object)(?!\w) + name + support.function.powershell + commentEmbeddedDocs diff --git a/extensions/pug/language-configuration.json b/extensions/pug/language-configuration.json index e22b6120252..00f4885d46e 100644 --- a/extensions/pug/language-configuration.json +++ b/extensions/pug/language-configuration.json @@ -20,5 +20,8 @@ ["(", ")"], ["'", "'"], ["\"", "\""] - ] + ], + "folding": { + "offSide": true + } } \ No newline at end of file diff --git a/extensions/python/language-configuration.json b/extensions/python/language-configuration.json index c995ea91f3d..97feb4d8aca 100644 --- a/extensions/python/language-configuration.json +++ b/extensions/python/language-configuration.json @@ -21,6 +21,8 @@ ["(", ")"], ["\"", "\""], ["'", "'"] - ] - // enhancedBrackets: [ { open: /.*:\s*$/, closeComplete: 'else:' } ], + ], + "folding": { + "offSide": true + } } \ No newline at end of file diff --git a/extensions/ruby/language-configuration.json b/extensions/ruby/language-configuration.json index fe8a5e9e2ac..fc4125f0691 100644 --- a/extensions/ruby/language-configuration.json +++ b/extensions/ruby/language-configuration.json @@ -23,7 +23,7 @@ ["'", "'"] ], "indentationRules": { - "increaseIndentPattern": "^\\s*((begin|class|def|else|elsif|ensure|for|if|module|rescue|unless|until|when|while)|(.*\\sdo\\b))\\b[^\\{;]*$", - "decreaseIndentPattern": "^\\s*([}\\]]([,)]?\\s*(#|$)|\\.[a-zA-Z_]\\w*\\b)|(end|rescue|ensure|else|elsif|when)\\b)" + "increaseIndentPattern": "^\\s*((begin|class|(private|protected)\\s+def|def|else|elsif|ensure|for|if|module|rescue|unless|until|when|while)|(.*\\sdo\\b))\\b[^\\{;]*$", + "decreaseIndentPattern": "^\\s*([}\\]]([,)]?\\s*(#|$)|\\.[a-zA-Z_]\\w*\\b)|(end|rescue|ensure|else|elsif)\\b)" } -} \ No newline at end of file +} diff --git a/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json b/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json index c7bf6f68cb1..bfd823e9658 100644 --- a/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json +++ b/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json @@ -371,9 +371,9 @@ // "editor.inactiveSelectionBackground": "", // "editor.lineHighlightBorder": "", // "editor.rangeHighlightBackground": "", - // "editor.selectionHighlightBackground": "", - // "editor.wordHighlightBackground": "", - // "editor.wordHighlightStrongBackground": "", + "editor.selectionHighlightBackground": "#005A6FAA", + "editor.wordHighlightBackground": "#004454AA", + "editor.wordHighlightStrongBackground": "#005A6FAA", // Editor: Suggest // "editorSuggestWidget.background": "", @@ -483,4 +483,4 @@ "terminal.ansiBrightCyan": "#93a1a1", "terminal.ansiBrightWhite": "#fdf6e3" } -} \ No newline at end of file +} diff --git a/extensions/typescript/language-configuration.json b/extensions/typescript/language-configuration.json index f25940db455..4080becc987 100644 --- a/extensions/typescript/language-configuration.json +++ b/extensions/typescript/language-configuration.json @@ -24,5 +24,11 @@ ["'", "'"], ["\"", "\""], ["`", "`"] - ] + ], + "folding": { + "markers": { + "start": "^\\s*//\\s*#region", + "end": "^\\s*//\\s*#endregion" + } + } } \ No newline at end of file diff --git a/extensions/typescript/package.json b/extensions/typescript/package.json index 1bab55ee7e3..a5093c1f0c3 100644 --- a/extensions/typescript/package.json +++ b/extensions/typescript/package.json @@ -516,6 +516,9 @@ "tsconfig": { "type": "string", "description": "The tsconfig file that defines the TS build" + }, + "option": { + "type": "string" } } } diff --git a/extensions/typescript/snippets/typescriptreact.json b/extensions/typescript/snippets/typescriptreact.json index c0d38f23c18..894d6177276 100644 --- a/extensions/typescript/snippets/typescriptreact.json +++ b/extensions/typescript/snippets/typescriptreact.json @@ -50,7 +50,7 @@ "Import external module.": { "prefix": "import statement", "body": [ - "import ${1:name} = require('$0');" + "import { $0 } from \"${1:module}\";" ], "description": "Import external module." }, diff --git a/extensions/typescript/src/features/baseCodeLensProvider.ts b/extensions/typescript/src/features/baseCodeLensProvider.ts index 84ba69a0a96..9f5d6012d80 100644 --- a/extensions/typescript/src/features/baseCodeLensProvider.ts +++ b/extensions/typescript/src/features/baseCodeLensProvider.ts @@ -7,6 +7,7 @@ import { CodeLensProvider, CodeLens, CancellationToken, TextDocument, Range, Uri import * as Proto from '../protocol'; import { ITypescriptServiceClient } from '../typescriptService'; +import { tsTextSpanToVsRange } from '../utils/convert'; export class ReferencesCodeLens extends CodeLens { constructor( @@ -99,10 +100,7 @@ export abstract class TypeScriptBaseCodeLensProvider implements CodeLensProvider return null; } - const range = new Range( - span.start.line - 1, span.start.offset - 1, - span.end.line - 1, span.end.offset - 1); - + const range = tsTextSpanToVsRange(span); const text = document.getText(range); const identifierMatch = new RegExp(`^(.*?(\\b|\\W))${(item.text || '').replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&')}(\\b|\\W)`, 'gm'); diff --git a/extensions/typescript/src/features/codeActionProvider.ts b/extensions/typescript/src/features/codeActionProvider.ts index c48caaf79e4..32f50fef59f 100644 --- a/extensions/typescript/src/features/codeActionProvider.ts +++ b/extensions/typescript/src/features/codeActionProvider.ts @@ -3,22 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CodeActionProvider, TextDocument, Range, CancellationToken, CodeActionContext, Command, commands, Uri, workspace, WorkspaceEdit, TextEdit, FormattingOptions, window } from 'vscode'; +import { CodeActionProvider, TextDocument, Range, CancellationToken, CodeActionContext, Command, commands, workspace, WorkspaceEdit } from 'vscode'; import * as Proto from '../protocol'; import { ITypescriptServiceClient } from '../typescriptService'; +import { tsTextSpanToVsRange, vsRangeToTsFileRange } from '../utils/convert'; +import FormattingConfigurationManager from './formattingConfigurationManager'; interface NumberSet { [key: number]: boolean; } -interface Source { - uri: Uri; - version: number; - range: Range; - formattingOptions: FormattingOptions | undefined; -} - export default class TypeScriptCodeActionProvider implements CodeActionProvider { private commandId: string; @@ -26,6 +21,7 @@ export default class TypeScriptCodeActionProvider implements CodeActionProvider constructor( private readonly client: ITypescriptServiceClient, + private readonly formattingConfigurationManager: FormattingConfigurationManager, mode: string ) { this.commandId = `_typescript.applyCodeAction.${mode}`; @@ -52,30 +48,14 @@ export default class TypeScriptCodeActionProvider implements CodeActionProvider return []; } - let formattingOptions: FormattingOptions | undefined = undefined; - for (const editor of window.visibleTextEditors) { - if (editor.document.fileName === document.fileName) { - formattingOptions = { tabSize: editor.options.tabSize, insertSpaces: editor.options.insertSpaces } as FormattingOptions; - break; - } - } + await this.formattingConfigurationManager.ensureFormatOptionsForDocument(document, token); - const source: Source = { - uri: document.uri, - version: document.version, - range: range, - formattingOptions: formattingOptions - }; const args: Proto.CodeFixRequestArgs = { - file: file, - startLine: range.start.line + 1, - endLine: range.end.line + 1, - startOffset: range.start.character + 1, - endOffset: range.end.character + 1, + ...vsRangeToTsFileRange(file, range), errorCodes: Array.from(supportedActions) }; const response = await this.client.execute('getCodeFixes', args, token); - return (response.body || []).map(action => this.getCommandForAction(source, action)); + return (response.body || []).map(action => this.getCommandForAction(action)); } private get supportedCodeActions(): Thenable { @@ -99,55 +79,24 @@ export default class TypeScriptCodeActionProvider implements CodeActionProvider .filter(code => supportedActions[code]))); } - private getCommandForAction(source: Source, action: Proto.CodeAction): Command { + private getCommandForAction(action: Proto.CodeAction): Command { return { title: action.description, command: this.commandId, - arguments: [source, action] + arguments: [action] }; } - private async onCodeAction(source: Source, action: Proto.CodeAction): Promise { + private async onCodeAction(action: Proto.CodeAction): Promise { const workspaceEdit = new WorkspaceEdit(); for (const change of action.changes) { for (const textChange of change.textChanges) { workspaceEdit.replace(this.client.asUrl(change.fileName), - new Range( - textChange.start.line - 1, textChange.start.offset - 1, - textChange.end.line - 1, textChange.end.offset - 1), + tsTextSpanToVsRange(textChange), textChange.newText); } } - const success = workspace.applyEdit(workspaceEdit); - if (!success) { - return false; - } - - let firstEdit: TextEdit | undefined = undefined; - for (const [uri, edits] of workspaceEdit.entries()) { - if (uri.fsPath === source.uri.fsPath) { - firstEdit = edits[0]; - break; - } - } - - if (!firstEdit) { - return true; - } - - const newLines = firstEdit.newText.match(/\n/g); - const editedRange = new Range( - firstEdit.range.start.line, 0, - firstEdit.range.end.line + 1 + (newLines ? newLines.length : 0), 0); - // TODO: Workaround for https://github.com/Microsoft/TypeScript/issues/12249 - // apply formatting to the source range until TS returns formatted results - const edits = (await commands.executeCommand('vscode.executeFormatRangeProvider', source.uri, editedRange, source.formattingOptions || {})) as TextEdit[]; - if (!edits || !edits.length) { - return false; - } - const formattingEdit = new WorkspaceEdit(); - formattingEdit.set(source.uri, edits); - return workspace.applyEdit(formattingEdit); + return workspace.applyEdit(workspaceEdit); } } \ No newline at end of file diff --git a/extensions/typescript/src/features/completionItemProvider.ts b/extensions/typescript/src/features/completionItemProvider.ts index 1fbcb707863..795230ae0b7 100644 --- a/extensions/typescript/src/features/completionItemProvider.ts +++ b/extensions/typescript/src/features/completionItemProvider.ts @@ -3,14 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CompletionItem, TextDocument, Position, CompletionItemKind, CompletionItemProvider, CancellationToken, TextEdit, Range, SnippetString, workspace, ProviderResult } from 'vscode'; +import { CompletionItem, TextDocument, Position, CompletionItemKind, CompletionItemProvider, CancellationToken, TextEdit, Range, SnippetString, workspace, ProviderResult, CompletionContext } from 'vscode'; import { ITypescriptServiceClient } from '../typescriptService'; import TypingsStatus from '../utils/typingsStatus'; import * as PConst from '../protocol.const'; -import { CompletionEntry, CompletionsRequestArgs, CompletionDetailsRequestArgs, CompletionEntryDetails, FileLocationRequestArgs } from '../protocol'; +import { CompletionEntry, CompletionsRequestArgs, CompletionDetailsRequestArgs, CompletionEntryDetails } from '../protocol'; import * as Previewer from './previewer'; +import { tsTextSpanToVsRange, vsPositionToTsFileLocation } from '../utils/convert'; import * as nls from 'vscode-nls'; let localize = nls.loadMessageBundle(); @@ -32,7 +33,7 @@ class MyCompletionItem extends CompletionItem { let span: protocol.TextSpan = entry.replacementSpan; // The indexing for the range returned by the server uses 1-based indexing. // We convert to 0-based indexing. - this.textEdit = TextEdit.replace(new Range(span.start.line - 1, span.start.offset - 1, span.end.line - 1, span.end.offset - 1), entry.name); + this.textEdit = TextEdit.replace(tsTextSpanToVsRange(span), entry.name); } else { // Try getting longer, prefix based range for completions that span words const wordRange = document.getWordRangeAtPosition(position); @@ -158,7 +159,12 @@ export default class TypeScriptCompletionItemProvider implements CompletionItemP this.config.nameSuggestions = jsConfig.get(Configuration.nameSuggestions, true); } - public provideCompletionItems(document: TextDocument, position: Position, token: CancellationToken): Promise { + public provideCompletionItems( + document: TextDocument, + position: Position, + token: CancellationToken, + context: CompletionContext + ): Promise { if (this.typingsStatus.isAcquiringTypings) { return Promise.reject({ label: localize( @@ -174,12 +180,32 @@ export default class TypeScriptCompletionItemProvider implements CompletionItemP if (!file) { return Promise.resolve([]); } - const args: CompletionsRequestArgs = { - file: file, - line: position.line + 1, - offset: position.character + 1 - }; + if (context.triggerCharacter === '"' || context.triggerCharacter === '\'') { + // make sure we are in something that looks like the start of an import + const line = document.lineAt(position.line).text.slice(0, position.character); + if (!line.match(/^import .+? from\s*["']$/) && !line.match(/\b(import|require)\(['"]$/)) { + return Promise.resolve([]); + } + } + + if (context.triggerCharacter === '/') { + // make sure we are in something that looks like an import path + const line = document.lineAt(position.line).text.slice(0, position.character); + if (!line.match(/^import .+? from\s*["'][^'"]*$/) && !line.match(/\b(import|require)\(['"][^'"]*$/)) { + return Promise.resolve([]); + } + } + + if (context.triggerCharacter === '@') { + // make sure we are in something that looks like the start of a jsdoc comment + const line = document.lineAt(position.line).text.slice(0, position.character); + if (!line.match(/^\s*\*[ ]?@/) && !line.match(/\/\*\*+[ ]?@/)) { + return Promise.resolve([]); + } + } + + const args: CompletionsRequestArgs = vsPositionToTsFileLocation(file, position); return this.client.execute('completions', args, token).then((msg) => { // This info has to come from the tsserver. See https://github.com/Microsoft/TypeScript/issues/2831 // let isMemberCompletion = false; @@ -238,9 +264,7 @@ export default class TypeScriptCompletionItemProvider implements CompletionItemP return null; } const args: CompletionDetailsRequestArgs = { - file: filepath, - line: item.position.line + 1, - offset: item.position.character + 1, + ...vsPositionToTsFileLocation(filepath, item.position), entryNames: [item.label] }; return this.client.execute('completionEntryDetails', args, token).then((response) => { @@ -269,11 +293,7 @@ export default class TypeScriptCompletionItemProvider implements CompletionItemP } private isValidFunctionCompletionContext(filepath: string, position: Position): Promise { - const args: FileLocationRequestArgs = { - file: filepath, - line: position.line + 1, - offset: position.character + 1 - }; + const args = vsPositionToTsFileLocation(filepath, position); // Workaround for https://github.com/Microsoft/TypeScript/issues/12677 // Don't complete function calls inside of destructive assigments or imports return this.client.execute('quickinfo', args).then(infoResponse => { diff --git a/extensions/typescript/src/features/definitionProviderBase.ts b/extensions/typescript/src/features/definitionProviderBase.ts index 78768df2fee..bf5ffabd704 100644 --- a/extensions/typescript/src/features/definitionProviderBase.ts +++ b/extensions/typescript/src/features/definitionProviderBase.ts @@ -3,10 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TextDocument, Position, Range, CancellationToken, Location } from 'vscode'; +import { TextDocument, Position, CancellationToken, Location } from 'vscode'; import * as Proto from '../protocol'; import { ITypescriptServiceClient } from '../typescriptService'; +import { tsTextSpanToVsRange, vsPositionToTsFileLocation } from '../utils/convert'; export default class TypeScriptDefinitionProviderBase { constructor( @@ -22,11 +23,7 @@ export default class TypeScriptDefinitionProviderBase { if (!filepath) { return Promise.resolve(null); } - const args: Proto.FileLocationRequestArgs = { - file: filepath, - line: position.line + 1, - offset: position.character + 1 - }; + const args = vsPositionToTsFileLocation(filepath, position); return this.client.execute(definitionType, args, token).then(response => { const locations: Proto.FileSpan[] = (response && response.body) || []; if (!locations || locations.length === 0) { @@ -37,7 +34,7 @@ export default class TypeScriptDefinitionProviderBase { if (resource === null) { return null; } else { - return new Location(resource, new Range(location.start.line - 1, location.start.offset - 1, location.end.line - 1, location.end.offset - 1)); + return new Location(resource, tsTextSpanToVsRange(location)); } }).filter(x => x !== null) as Location[]; }, () => { diff --git a/extensions/typescript/src/features/documentHighlightProvider.ts b/extensions/typescript/src/features/documentHighlightProvider.ts index 15224ca2c2e..d91cb8fd90b 100644 --- a/extensions/typescript/src/features/documentHighlightProvider.ts +++ b/extensions/typescript/src/features/documentHighlightProvider.ts @@ -5,8 +5,8 @@ import { DocumentHighlightProvider, DocumentHighlight, DocumentHighlightKind, TextDocument, Position, Range, CancellationToken } from 'vscode'; -import * as Proto from '../protocol'; import { ITypescriptServiceClient } from '../typescriptService'; +import { tsTextSpanToVsRange, vsPositionToTsFileLocation } from '../utils/convert'; export default class TypeScriptDocumentHighlightProvider implements DocumentHighlightProvider { @@ -18,11 +18,7 @@ export default class TypeScriptDocumentHighlightProvider implements DocumentHigh if (!filepath) { return Promise.resolve([]); } - const args: Proto.FileLocationRequestArgs = { - file: filepath, - line: position.line + 1, - offset: position.character + 1 - }; + const args = vsPositionToTsFileLocation(filepath, position); return this.client.execute('occurrences', args, token).then((response): DocumentHighlight[] => { let data = response.body; if (data && data.length) { @@ -37,10 +33,10 @@ export default class TypeScriptDocumentHighlightProvider implements DocumentHigh return []; } } - return data.map((item) => { - return new DocumentHighlight(new Range(item.start.line - 1, item.start.offset - 1, item.end.line - 1, item.end.offset - 1), - item.isWriteAccess ? DocumentHighlightKind.Write : DocumentHighlightKind.Read); - }); + return data.map(item => + new DocumentHighlight( + tsTextSpanToVsRange(item), + item.isWriteAccess ? DocumentHighlightKind.Write : DocumentHighlightKind.Read)); } return []; }, () => { diff --git a/extensions/typescript/src/features/documentSymbolProvider.ts b/extensions/typescript/src/features/documentSymbolProvider.ts index 456776cf3a6..bf289d24ee2 100644 --- a/extensions/typescript/src/features/documentSymbolProvider.ts +++ b/extensions/typescript/src/features/documentSymbolProvider.ts @@ -3,11 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { DocumentSymbolProvider, SymbolInformation, SymbolKind, TextDocument, Range, Location, CancellationToken, Uri } from 'vscode'; +import { DocumentSymbolProvider, SymbolInformation, SymbolKind, TextDocument, Location, CancellationToken, Uri } from 'vscode'; import * as Proto from '../protocol'; import * as PConst from '../protocol.const'; import { ITypescriptServiceClient } from '../typescriptService'; +import { tsTextSpanToVsRange } from '../utils/convert'; const outlineTypeTable: { [kind: string]: SymbolKind } = Object.create(null); outlineTypeTable[PConst.Kind.module] = SymbolKind.Module; @@ -25,9 +26,6 @@ outlineTypeTable[PConst.Kind.variable] = SymbolKind.Variable; outlineTypeTable[PConst.Kind.function] = SymbolKind.Function; outlineTypeTable[PConst.Kind.localFunction] = SymbolKind.Function; -function textSpan2Range(value: Proto.TextSpan): Range { - return new Range(value.start.line - 1, value.start.offset - 1, value.end.line - 1, value.end.offset - 1); -} export default class TypeScriptDocumentSymbolProvider implements DocumentSymbolProvider { public constructor( @@ -73,7 +71,7 @@ export default class TypeScriptDocumentSymbolProvider implements DocumentSymbolP let result = new SymbolInformation(item.text, outlineTypeTable[item.kind as string] || SymbolKind.Variable, containerLabel ? containerLabel : '', - new Location(resource, textSpan2Range(item.spans[0]))); + new Location(resource, tsTextSpanToVsRange(item.spans[0]))); foldingMap[key] = result; bucket.push(result); } @@ -88,7 +86,7 @@ export default class TypeScriptDocumentSymbolProvider implements DocumentSymbolP const result = new SymbolInformation(item.text, outlineTypeTable[item.kind as string] || SymbolKind.Variable, containerLabel ? containerLabel : '', - new Location(resource, textSpan2Range(item.spans[0])) + new Location(resource, tsTextSpanToVsRange(item.spans[0])) ); if (item.childItems && item.childItems.length > 0) { for (const child of item.childItems) { diff --git a/extensions/typescript/src/features/formattingConfigurationManager.ts b/extensions/typescript/src/features/formattingConfigurationManager.ts new file mode 100644 index 00000000000..923c3d43a65 --- /dev/null +++ b/extensions/typescript/src/features/formattingConfigurationManager.ts @@ -0,0 +1,146 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { workspace as Workspace, FormattingOptions, TextDocument, CancellationToken, WorkspaceConfiguration, window } from 'vscode'; + +import * as Proto from '../protocol'; +import { ITypescriptServiceClient } from '../typescriptService'; + +interface FormattingConfiguration { + insertSpaceAfterCommaDelimiter: boolean; + insertSpaceAfterConstructor: boolean; + insertSpaceAfterSemicolonInForStatements: boolean; + insertSpaceBeforeAndAfterBinaryOperators: boolean; + insertSpaceAfterKeywordsInControlFlowStatements: boolean; + insertSpaceAfterFunctionKeywordForAnonymousFunctions: boolean; + insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: boolean; + insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: boolean; + insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: boolean; + insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: boolean; + insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces: boolean; + insertSpaceAfterTypeAssertion: boolean; + insertSpaceBeforeFunctionParenthesis: boolean; + placeOpenBraceOnNewLineForFunctions: boolean; + placeOpenBraceOnNewLineForControlBlocks: boolean; +} + +namespace FormattingConfiguration { + export function equals(a: FormattingConfiguration, b: FormattingConfiguration): boolean { + let keys = Object.keys(a); + for (let i = 0; i < keys.length; i++) { + let key = keys[i]; + if ((a as any)[key] !== (b as any)[key]) { + return false; + } + } + return true; + } + + export const def: FormattingConfiguration = { + insertSpaceAfterCommaDelimiter: true, + insertSpaceAfterConstructor: false, + insertSpaceAfterSemicolonInForStatements: true, + insertSpaceBeforeAndAfterBinaryOperators: true, + insertSpaceAfterKeywordsInControlFlowStatements: true, + insertSpaceAfterFunctionKeywordForAnonymousFunctions: false, + insertSpaceBeforeFunctionParenthesis: false, + insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false, + insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false, + insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: true, + insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: false, + insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces: false, + insertSpaceAfterTypeAssertion: false, + placeOpenBraceOnNewLineForFunctions: false, + placeOpenBraceOnNewLineForControlBlocks: false + }; +} + +export default class FormattingConfigurationManager { + private config: FormattingConfiguration = FormattingConfiguration.def; + + private formatOptions: { [key: string]: Proto.FormatCodeSettings | undefined; } = Object.create(null); + + public constructor( + private client: ITypescriptServiceClient + ) { + Workspace.onDidCloseTextDocument((textDocument) => { + let key = textDocument.uri.toString(); + // When a document gets closed delete the cached formatting options. + // This is necessary sine the tsserver now closed a project when its + // last file in it closes which drops the stored formatting options + // as well. + delete this.formatOptions[key]; + }); + } + + public async ensureFormatOptionsForDocument( + document: TextDocument, + token: CancellationToken | undefined + ): Promise { + for (const editor of window.visibleTextEditors) { + if (editor.document.fileName === document.fileName) { + const formattingOptions = { tabSize: editor.options.tabSize, insertSpaces: editor.options.insertSpaces } as FormattingOptions; + return this.ensureFormatOptions(document, formattingOptions, token); + } + } + } + + public async ensureFormatOptions( + document: TextDocument, + options: FormattingOptions, + token: CancellationToken | undefined + ): Promise { + const key = document.uri.toString(); + const currentOptions = this.formatOptions[key]; + if (currentOptions && currentOptions.tabSize === options.tabSize && currentOptions.indentSize === options.tabSize && currentOptions.convertTabsToSpaces === options.insertSpaces) { + return; + } + const absPath = this.client.normalizePath(document.uri); + if (!absPath) { + return Object.create(null); + } + const formatOptions = this.getFormatOptions(options); + const args: Proto.ConfigureRequestArguments = { + file: absPath, + formatOptions: formatOptions + }; + await this.client.execute('configure', args, token); + this.formatOptions[key] = formatOptions; + } + + public updateConfiguration(config: WorkspaceConfiguration): void { + const newConfig = config.get('format', FormattingConfiguration.def); + + if (!FormattingConfiguration.equals(this.config, newConfig)) { + this.formatOptions = Object.create(null); + } + this.config = newConfig; + } + + private getFormatOptions(options: FormattingOptions): Proto.FormatCodeSettings { + return { + tabSize: options.tabSize, + indentSize: options.tabSize, + convertTabsToSpaces: options.insertSpaces, + // We can use \n here since the editor normalizes later on to its line endings. + newLineCharacter: '\n', + insertSpaceAfterCommaDelimiter: this.config.insertSpaceAfterCommaDelimiter, + insertSpaceAfterConstructor: this.config.insertSpaceAfterConstructor, + insertSpaceAfterSemicolonInForStatements: this.config.insertSpaceAfterSemicolonInForStatements, + insertSpaceBeforeAndAfterBinaryOperators: this.config.insertSpaceBeforeAndAfterBinaryOperators, + insertSpaceAfterKeywordsInControlFlowStatements: this.config.insertSpaceAfterKeywordsInControlFlowStatements, + insertSpaceAfterFunctionKeywordForAnonymousFunctions: this.config.insertSpaceAfterFunctionKeywordForAnonymousFunctions, + insertSpaceBeforeFunctionParenthesis: this.config.insertSpaceBeforeFunctionParenthesis, + insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: this.config.insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis, + insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: this.config.insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets, + insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: this.config.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces, + insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: this.config.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces, + insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces: this.config.insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces, + insertSpaceAfterTypeAssertion: this.config.insertSpaceAfterTypeAssertion, + placeOpenBraceOnNewLineForFunctions: this.config.placeOpenBraceOnNewLineForFunctions, + placeOpenBraceOnNewLineForControlBlocks: this.config.placeOpenBraceOnNewLineForControlBlocks, + }; + } +} diff --git a/extensions/typescript/src/features/formattingProvider.ts b/extensions/typescript/src/features/formattingProvider.ts index 1c5e7866b8f..04fa0ff4675 100644 --- a/extensions/typescript/src/features/formattingProvider.ts +++ b/extensions/typescript/src/features/formattingProvider.ts @@ -3,153 +3,56 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { workspace as Workspace, DocumentRangeFormattingEditProvider, OnTypeFormattingEditProvider, FormattingOptions, TextDocument, Position, Range, CancellationToken, TextEdit, WorkspaceConfiguration, Disposable, languages, workspace } from 'vscode'; +import { DocumentRangeFormattingEditProvider, OnTypeFormattingEditProvider, FormattingOptions, TextDocument, Position, Range, CancellationToken, TextEdit, WorkspaceConfiguration, Disposable, languages, workspace } from 'vscode'; import * as Proto from '../protocol'; import { ITypescriptServiceClient } from '../typescriptService'; - -interface Configuration { - enable: boolean; - insertSpaceAfterCommaDelimiter: boolean; - insertSpaceAfterConstructor: boolean; - insertSpaceAfterSemicolonInForStatements: boolean; - insertSpaceBeforeAndAfterBinaryOperators: boolean; - insertSpaceAfterKeywordsInControlFlowStatements: boolean; - insertSpaceAfterFunctionKeywordForAnonymousFunctions: boolean; - insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: boolean; - insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: boolean; - insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: boolean; - insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: boolean; - insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces: boolean; - insertSpaceAfterTypeAssertion: boolean; - insertSpaceBeforeFunctionParenthesis: boolean; - placeOpenBraceOnNewLineForFunctions: boolean; - placeOpenBraceOnNewLineForControlBlocks: boolean; -} - -namespace Configuration { - export const insertSpaceAfterCommaDelimiter = 'insertSpaceAfterCommaDelimiter'; - export const insertSpaceAfterConstructor = 'insertSpaceAfterConstructor'; - export const insertSpaceAfterSemicolonInForStatements = 'insertSpaceAfterSemicolonInForStatements'; - export const insertSpaceBeforeAndAfterBinaryOperators = 'insertSpaceBeforeAndAfterBinaryOperators'; - export const insertSpaceAfterKeywordsInControlFlowStatements = 'insertSpaceAfterKeywordsInControlFlowStatements'; - export const insertSpaceAfterFunctionKeywordForAnonymousFunctions = 'insertSpaceAfterFunctionKeywordForAnonymousFunctions'; - export const insertSpaceBeforeFunctionParenthesis = 'insertSpaceBeforeFunctionParenthesis'; - export const insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis = 'insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis'; - export const insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets = 'insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets'; - export const insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces = 'insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces'; - export const insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces = 'insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces'; - export const insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces = 'insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces'; - export const insertSpaceAfterTypeAssertion = 'insertSpaceAfterTypeAssertion'; - export const placeOpenBraceOnNewLineForFunctions = 'placeOpenBraceOnNewLineForFunctions'; - export const placeOpenBraceOnNewLineForControlBlocks = 'placeOpenBraceOnNewLineForControlBlocks'; - - export function equals(a: Configuration, b: Configuration): boolean { - let keys = Object.keys(a); - for (let i = 0; i < keys.length; i++) { - let key = keys[i]; - if ((a as any)[key] !== (b as any)[key]) { - return false; - } - } - return true; - } - - export function def(): Configuration { - let result: Configuration = Object.create(null); - result.enable = true; - result.insertSpaceAfterCommaDelimiter = true; - result.insertSpaceAfterConstructor = false; - result.insertSpaceAfterSemicolonInForStatements = true; - result.insertSpaceBeforeAndAfterBinaryOperators = true; - result.insertSpaceAfterKeywordsInControlFlowStatements = true; - result.insertSpaceAfterFunctionKeywordForAnonymousFunctions = false; - result.insertSpaceBeforeFunctionParenthesis = false; - result.insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis = false; - result.insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets = false; - result.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces = true; - result.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces = false; - result.insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces = false; - result.insertSpaceAfterTypeAssertion = false; - result.placeOpenBraceOnNewLineForFunctions = false; - result.placeOpenBraceOnNewLineForControlBlocks = false; - return result; - } -} +import { tsTextSpanToVsRange } from '../utils/convert'; +import FormattingConfigurationManager from './formattingConfigurationManager'; export class TypeScriptFormattingProvider implements DocumentRangeFormattingEditProvider, OnTypeFormattingEditProvider { - private config: Configuration; - private formatOptions: { [key: string]: Proto.FormatCodeSettings | undefined; }; + private enabled: boolean = true; public constructor( - private client: ITypescriptServiceClient - ) { - this.config = Configuration.def(); - this.formatOptions = Object.create(null); - Workspace.onDidCloseTextDocument((textDocument) => { - let key = textDocument.uri.toString(); - // When a document gets closed delete the cached formatting options. - // This is necessary sine the tsserver now closed a project when its - // last file in it closes which drops the stored formatting options - // as well. - delete this.formatOptions[key]; - }); - } + private readonly client: ITypescriptServiceClient, + private readonly formattingOptionsManager: FormattingConfigurationManager + ) { } public updateConfiguration(config: WorkspaceConfiguration): void { - let newConfig = config.get('format', Configuration.def()); - - if (!Configuration.equals(this.config, newConfig)) { - this.config = newConfig; - this.formatOptions = Object.create(null); - } + this.enabled = config.get('format.enable', true); } public isEnabled(): boolean { - return this.config.enable; + return this.enabled; } - private ensureFormatOptions(document: TextDocument, options: FormattingOptions, token: CancellationToken): Promise { - const key = document.uri.toString(); - const currentOptions = this.formatOptions[key]; - if (currentOptions && currentOptions.tabSize === options.tabSize && currentOptions.indentSize === options.tabSize && currentOptions.convertTabsToSpaces === options.insertSpaces) { - return Promise.resolve(currentOptions); - } else { - const absPath = this.client.normalizePath(document.uri); - if (!absPath) { - return Promise.resolve(Object.create(null)); + private async doFormat( + document: TextDocument, + options: FormattingOptions, + args: Proto.FormatRequestArgs, + token: CancellationToken + ): Promise { + await this.formattingOptionsManager.ensureFormatOptions(document, options, token); + try { + const response = await this.client.execute('format', args, token); + if (response.body) { + return response.body.map(this.codeEdit2SingleEditOperation); } - - const formatOptions = this.getFormatOptions(options); - const args: Proto.ConfigureRequestArguments = { - file: absPath, - formatOptions: formatOptions - }; - return this.client.execute('configure', args, token).then(_ => { - this.formatOptions[key] = formatOptions; - return formatOptions; - }); + } catch { + // noop } + return []; } - private doFormat(document: TextDocument, options: FormattingOptions, args: Proto.FormatRequestArgs, token: CancellationToken): Promise { - return this.ensureFormatOptions(document, options, token).then(() => { - return this.client.execute('format', args, token).then((response): TextEdit[] => { - if (response.body) { - return response.body.map(this.codeEdit2SingleEditOperation); - } else { - return []; - } - }, () => { - return []; - }); - }); - } - - public provideDocumentRangeFormattingEdits(document: TextDocument, range: Range, options: FormattingOptions, token: CancellationToken): Promise { + public async provideDocumentRangeFormattingEdits( + document: TextDocument, + range: Range, + options: FormattingOptions, + token: CancellationToken + ): Promise { const absPath = this.client.normalizePath(document.uri); if (!absPath) { - return Promise.resolve([]); + return []; } const args: Proto.FormatRequestArgs = { file: absPath, @@ -161,10 +64,16 @@ export class TypeScriptFormattingProvider implements DocumentRangeFormattingEdit return this.doFormat(document, options, args, token); } - public provideOnTypeFormattingEdits(document: TextDocument, position: Position, ch: string, options: FormattingOptions, token: CancellationToken): Promise { + public async provideOnTypeFormattingEdits( + document: TextDocument, + position: Position, + ch: string, + options: FormattingOptions, + token: CancellationToken + ): Promise { const filepath = this.client.normalizePath(document.uri); if (!filepath) { - return Promise.resolve([]); + return []; } let args: Proto.FormatOnKeyRequestArgs = { file: filepath, @@ -173,66 +82,38 @@ export class TypeScriptFormattingProvider implements DocumentRangeFormattingEdit key: ch }; - return this.ensureFormatOptions(document, options, token).then(() => { - return this.client.execute('formatonkey', args, token).then((response): TextEdit[] => { - let edits = response.body; - let result: TextEdit[] = []; - if (!edits) { - return result; - } - for (let edit of edits) { - let textEdit = this.codeEdit2SingleEditOperation(edit); - let range = textEdit.range; - // Work around for https://github.com/Microsoft/TypeScript/issues/6700. - // Check if we have an edit at the beginning of the line which only removes white spaces and leaves - // an empty line. Drop those edits - if (range.start.character === 0 && range.start.line === range.end.line && textEdit.newText === '') { - let lText = document.lineAt(range.start.line).text; - // If the edit leaves something on the line keep the edit (note that the end character is exclusive). - // Keep it also if it removes something else than whitespace - if (lText.trim().length > 0 || lText.length > range.end.character) { - result.push(textEdit); - } - } else { + await this.formattingOptionsManager.ensureFormatOptions(document, options, token); + return this.client.execute('formatonkey', args, token).then((response): TextEdit[] => { + let edits = response.body; + let result: TextEdit[] = []; + if (!edits) { + return result; + } + for (let edit of edits) { + let textEdit = this.codeEdit2SingleEditOperation(edit); + let range = textEdit.range; + // Work around for https://github.com/Microsoft/TypeScript/issues/6700. + // Check if we have an edit at the beginning of the line which only removes white spaces and leaves + // an empty line. Drop those edits + if (range.start.character === 0 && range.start.line === range.end.line && textEdit.newText === '') { + let lText = document.lineAt(range.start.line).text; + // If the edit leaves something on the line keep the edit (note that the end character is exclusive). + // Keep it also if it removes something else than whitespace + if (lText.trim().length > 0 || lText.length > range.end.character) { result.push(textEdit); } + } else { + result.push(textEdit); } - return result; - }, () => { - return []; - }); + } + return result; + }, () => { + return []; }); } private codeEdit2SingleEditOperation(edit: Proto.CodeEdit): TextEdit { - return new TextEdit(new Range(edit.start.line - 1, edit.start.offset - 1, edit.end.line - 1, edit.end.offset - 1), - edit.newText); - } - - private getFormatOptions(options: FormattingOptions): Proto.FormatCodeSettings { - return { - tabSize: options.tabSize, - indentSize: options.tabSize, - convertTabsToSpaces: options.insertSpaces, - // We can use \n here since the editor normalizes later on to its line endings. - newLineCharacter: '\n', - insertSpaceAfterCommaDelimiter: this.config.insertSpaceAfterCommaDelimiter, - insertSpaceAfterConstructor: this.config.insertSpaceAfterConstructor, - insertSpaceAfterSemicolonInForStatements: this.config.insertSpaceAfterSemicolonInForStatements, - insertSpaceBeforeAndAfterBinaryOperators: this.config.insertSpaceBeforeAndAfterBinaryOperators, - insertSpaceAfterKeywordsInControlFlowStatements: this.config.insertSpaceAfterKeywordsInControlFlowStatements, - insertSpaceAfterFunctionKeywordForAnonymousFunctions: this.config.insertSpaceAfterFunctionKeywordForAnonymousFunctions, - insertSpaceBeforeFunctionParenthesis: this.config.insertSpaceBeforeFunctionParenthesis, - insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: this.config.insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis, - insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: this.config.insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets, - insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: this.config.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces, - insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: this.config.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces, - insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces: this.config.insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces, - insertSpaceAfterTypeAssertion: this.config.insertSpaceAfterTypeAssertion, - placeOpenBraceOnNewLineForFunctions: this.config.placeOpenBraceOnNewLineForFunctions, - placeOpenBraceOnNewLineForControlBlocks: this.config.placeOpenBraceOnNewLineForControlBlocks, - - }; + return new TextEdit(tsTextSpanToVsRange(edit), edit.newText); } } diff --git a/extensions/typescript/src/features/hoverProvider.ts b/extensions/typescript/src/features/hoverProvider.ts index 9fd1483b625..8b27c535292 100644 --- a/extensions/typescript/src/features/hoverProvider.ts +++ b/extensions/typescript/src/features/hoverProvider.ts @@ -3,11 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { HoverProvider, Hover, TextDocument, Position, Range, CancellationToken } from 'vscode'; +import { HoverProvider, Hover, TextDocument, Position, CancellationToken } from 'vscode'; import * as Proto from '../protocol'; import { ITypescriptServiceClient } from '../typescriptService'; import { tagsMarkdownPreview } from './previewer'; +import { tsTextSpanToVsRange, vsPositionToTsFileLocation } from '../utils/convert'; export default class TypeScriptHoverProvider implements HoverProvider { @@ -19,19 +20,14 @@ export default class TypeScriptHoverProvider implements HoverProvider { if (!filepath) { return undefined; } - const args: Proto.FileLocationRequestArgs = { - file: filepath, - line: position.line + 1, - offset: position.character + 1 - }; - + const args = vsPositionToTsFileLocation(filepath, position); try { const response = await this.client.execute('quickinfo', args, token); if (response && response.body) { const data = response.body; return new Hover( TypeScriptHoverProvider.getContents(data), - new Range(data.start.line - 1, data.start.offset - 1, data.end.line - 1, data.end.offset - 1)); + tsTextSpanToVsRange(data)); } } catch (e) { // noop @@ -40,10 +36,14 @@ export default class TypeScriptHoverProvider implements HoverProvider { } private static getContents(data: Proto.QuickInfoResponseBody) { + const parts = []; + + if (data.displayString) { + parts.push({ language: 'typescript', value: data.displayString }); + } + const tags = tagsMarkdownPreview(data.tags); - return [ - { language: 'typescript', value: data.displayString }, - data.documentation + (tags ? '\n\n' + tags : '') - ]; + parts.push(data.documentation + (tags ? '\n\n' + tags : '')); + return parts; } } \ No newline at end of file diff --git a/extensions/typescript/src/features/implementationsCodeLensProvider.ts b/extensions/typescript/src/features/implementationsCodeLensProvider.ts index 30daa644a4e..b51da2970a2 100644 --- a/extensions/typescript/src/features/implementationsCodeLensProvider.ts +++ b/extensions/typescript/src/features/implementationsCodeLensProvider.ts @@ -9,6 +9,7 @@ import * as PConst from '../protocol.const'; import { TypeScriptBaseCodeLensProvider, ReferencesCodeLens } from './baseCodeLensProvider'; import { ITypescriptServiceClient } from '../typescriptService'; +import { tsTextSpanToVsRange, vsPositionToTsFileLocation } from '../utils/convert'; import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); @@ -35,11 +36,7 @@ export default class TypeScriptImplementationsCodeLensProvider extends TypeScrip resolveCodeLens(inputCodeLens: CodeLens, token: CancellationToken): Promise { const codeLens = inputCodeLens as ReferencesCodeLens; - const args: Proto.FileLocationRequestArgs = { - file: codeLens.file, - line: codeLens.range.start.line + 1, - offset: codeLens.range.start.character + 1 - }; + const args = vsPositionToTsFileLocation(codeLens.file, codeLens.range.start); return this.client.execute('implementation', args, token).then(response => { if (!response || !response.body) { throw codeLens; @@ -50,15 +47,13 @@ export default class TypeScriptImplementationsCodeLensProvider extends TypeScrip // Only take first line on implementation: https://github.com/Microsoft/vscode/issues/23924 new Location(this.client.asUrl(reference.file), reference.start.line === reference.end.line - ? new Range( - reference.start.line - 1, reference.start.offset - 1, - reference.end.line - 1, reference.end.offset - 1) + ? tsTextSpanToVsRange(reference) : new Range( reference.start.line - 1, reference.start.offset - 1, reference.start.line, 0))) // Exclude original from implementations .filter(location => - !(location.uri.fsPath === codeLens.document.fsPath && + !(location.uri.toString() === codeLens.document.toString() && location.range.start.line === codeLens.range.start.line && location.range.start.character === codeLens.range.start.character)); diff --git a/extensions/typescript/src/features/jsDocCompletionProvider.ts b/extensions/typescript/src/features/jsDocCompletionProvider.ts index 50f0147cbc0..d9c87da1cc0 100644 --- a/extensions/typescript/src/features/jsDocCompletionProvider.ts +++ b/extensions/typescript/src/features/jsDocCompletionProvider.ts @@ -6,9 +6,10 @@ import { Position, Range, CompletionItemProvider, CompletionItemKind, TextDocument, CancellationToken, CompletionItem, window, Uri, ProviderResult, TextEditor, SnippetString, workspace } from 'vscode'; import { ITypescriptServiceClient } from '../typescriptService'; -import { FileLocationRequestArgs, DocCommandTemplateResponse } from '../protocol'; +import { DocCommandTemplateResponse } from '../protocol'; import * as nls from 'vscode-nls'; +import { vsPositionToTsFileLocation } from '../utils/convert'; const localize = nls.loadMessageBundle(); const configurationNamespace = 'jsDocCompletion'; @@ -118,11 +119,7 @@ export class TryCompleteJsDocCommand { } private tryInsertJsDocFromTemplate(editor: TextEditor, file: string, position: Position): Promise { - const args: FileLocationRequestArgs = { - file: file, - line: position.line + 1, - offset: position.character + 1 - }; + const args = vsPositionToTsFileLocation(file, position); return Promise.race([ this.lazyClient().execute('docCommentTemplate', args), new Promise((_, reject) => setTimeout(reject, 250)) diff --git a/extensions/typescript/src/features/refactorProvider.ts b/extensions/typescript/src/features/refactorProvider.ts index 1b9062998b5..a17533f4b4b 100644 --- a/extensions/typescript/src/features/refactorProvider.ts +++ b/extensions/typescript/src/features/refactorProvider.ts @@ -5,11 +5,12 @@ 'use strict'; -import { CodeActionProvider, TextDocument, Range, CancellationToken, CodeActionContext, Command, commands, workspace, WorkspaceEdit, window, QuickPickItem, Selection, Position } from 'vscode'; +import { CodeActionProvider, TextDocument, Range, CancellationToken, CodeActionContext, Command, commands, workspace, WorkspaceEdit, window, QuickPickItem, Selection } from 'vscode'; import * as Proto from '../protocol'; import { ITypescriptServiceClient } from '../typescriptService'; - +import { tsTextSpanToVsRange, vsRangeToTsFileRange, tsLocationToVsPosition } from '../utils/convert'; +import FormattingOptionsManager from './formattingConfigurationManager'; export default class TypeScriptRefactorProvider implements CodeActionProvider { private doRefactorCommandId: string; @@ -17,6 +18,7 @@ export default class TypeScriptRefactorProvider implements CodeActionProvider { constructor( private readonly client: ITypescriptServiceClient, + private formattingOptionsManager: FormattingOptionsManager, mode: string ) { this.doRefactorCommandId = `_typescript.applyRefactoring.${mode}`; @@ -24,7 +26,6 @@ export default class TypeScriptRefactorProvider implements CodeActionProvider { commands.registerCommand(this.doRefactorCommandId, this.doRefactoring, this); commands.registerCommand(this.selectRefactorCommandId, this.selectRefactoring, this); - } public async provideCodeActions( @@ -42,14 +43,7 @@ export default class TypeScriptRefactorProvider implements CodeActionProvider { return []; } - const args: Proto.GetApplicableRefactorsRequestArgs = { - file: file, - startLine: range.start.line + 1, - startOffset: range.start.character + 1, - endLine: range.end.line + 1, - endOffset: range.end.character + 1 - }; - + const args: Proto.GetApplicableRefactorsRequestArgs = vsRangeToTsFileRange(file, range); try { const response = await this.client.execute('getApplicableRefactors', args, token); if (!response || !response.body) { @@ -62,14 +56,14 @@ export default class TypeScriptRefactorProvider implements CodeActionProvider { actions.push({ title: info.description, command: this.selectRefactorCommandId, - arguments: [file, info, range] + arguments: [document, file, info, range] }); } else { for (const action of info.actions) { actions.push({ title: action.description, command: this.doRefactorCommandId, - arguments: [file, info.name, action.name, range] + arguments: [document, file, info.name, action.name, range] }); } } @@ -85,36 +79,31 @@ export default class TypeScriptRefactorProvider implements CodeActionProvider { for (const edit of edits) { for (const textChange of edit.textChanges) { workspaceEdit.replace(this.client.asUrl(edit.fileName), - new Range( - textChange.start.line - 1, textChange.start.offset - 1, - textChange.end.line - 1, textChange.end.offset - 1), + tsTextSpanToVsRange(textChange), textChange.newText); } } return workspaceEdit; } - private async selectRefactoring(file: string, info: Proto.ApplicableRefactorInfo, range: Range): Promise { - return window.showQuickPick(info.actions.map((action): QuickPickItem => ({ + private async selectRefactoring(document: TextDocument, file: string, info: Proto.ApplicableRefactorInfo, range: Range): Promise { + const selected = await window.showQuickPick(info.actions.map((action): QuickPickItem => ({ label: action.name, description: action.description - }))).then(selected => { - if (!selected) { - return false; - } - return this.doRefactoring(file, info.name, selected.label, range); - }); + }))); + if (!selected) { + return false; + } + return this.doRefactoring(document, file, info.name, selected.label, range); } - private async doRefactoring(file: string, refactor: string, action: string, range: Range): Promise { + private async doRefactoring(document: TextDocument, file: string, refactor: string, action: string, range: Range): Promise { + await this.formattingOptionsManager.ensureFormatOptionsForDocument(document, undefined); + const args: Proto.GetEditsForRefactorRequestArgs = { - file, + ...vsRangeToTsFileRange(file, range), refactor, - action, - startLine: range.start.line + 1, - startOffset: range.start.character + 1, - endLine: range.end.line + 1, - endOffset: range.end.character + 1 + action }; const response = await this.client.execute('getEditsForRefactor', args); @@ -130,7 +119,7 @@ export default class TypeScriptRefactorProvider implements CodeActionProvider { const renameLocation = response.body.renameLocation; if (renameLocation) { if (window.activeTextEditor && window.activeTextEditor.document.uri.fsPath === file) { - const pos = new Position(renameLocation.line - 1, renameLocation.offset - 1); + const pos = tsLocationToVsPosition(renameLocation); window.activeTextEditor.selection = new Selection(pos, pos); await commands.executeCommand('editor.action.rename'); } diff --git a/extensions/typescript/src/features/referenceProvider.ts b/extensions/typescript/src/features/referenceProvider.ts index 9f7ccf9f49a..1bce17a5b65 100644 --- a/extensions/typescript/src/features/referenceProvider.ts +++ b/extensions/typescript/src/features/referenceProvider.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ReferenceProvider, Location, TextDocument, Position, Range, CancellationToken } from 'vscode'; +import { ReferenceProvider, Location, TextDocument, Position, CancellationToken } from 'vscode'; -import * as Proto from '../protocol'; import { ITypescriptServiceClient } from '../typescriptService'; +import { tsTextSpanToVsRange, vsPositionToTsFileLocation } from '../utils/convert'; export default class TypeScriptReferenceSupport implements ReferenceProvider { public constructor( @@ -17,11 +17,7 @@ export default class TypeScriptReferenceSupport implements ReferenceProvider { if (!filepath) { return Promise.resolve([]); } - const args: Proto.FileLocationRequestArgs = { - file: filepath, - line: position.line + 1, - offset: position.character + 1 - }; + const args = vsPositionToTsFileLocation(filepath, position); const apiVersion = this.client.apiVersion; return this.client.execute('references', args, token).then((msg) => { const result: Location[] = []; @@ -35,9 +31,7 @@ export default class TypeScriptReferenceSupport implements ReferenceProvider { continue; } const url = this.client.asUrl(ref.file); - const location = new Location( - url, - new Range(ref.start.line - 1, ref.start.offset - 1, ref.end.line - 1, ref.end.offset - 1)); + const location = new Location(url, tsTextSpanToVsRange(ref)); result.push(location); } return result; diff --git a/extensions/typescript/src/features/referencesCodeLensProvider.ts b/extensions/typescript/src/features/referencesCodeLensProvider.ts index 5576bfc4825..641dc28cf8a 100644 --- a/extensions/typescript/src/features/referencesCodeLensProvider.ts +++ b/extensions/typescript/src/features/referencesCodeLensProvider.ts @@ -9,6 +9,7 @@ import * as PConst from '../protocol.const'; import { TypeScriptBaseCodeLensProvider, ReferencesCodeLens } from './baseCodeLensProvider'; import { ITypescriptServiceClient } from '../typescriptService'; +import { tsTextSpanToVsRange, vsPositionToTsFileLocation } from '../utils/convert'; import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); @@ -35,11 +36,7 @@ export default class TypeScriptReferencesCodeLensProvider extends TypeScriptBase resolveCodeLens(inputCodeLens: CodeLens, token: CancellationToken): Promise { const codeLens = inputCodeLens as ReferencesCodeLens; - const args: Proto.FileLocationRequestArgs = { - file: codeLens.file, - line: codeLens.range.start.line + 1, - offset: codeLens.range.start.character + 1 - }; + const args = vsPositionToTsFileLocation(codeLens.file, codeLens.range.start); return this.client.execute('references', args, token).then(response => { if (!response || !response.body) { throw codeLens; @@ -47,13 +44,10 @@ export default class TypeScriptReferencesCodeLensProvider extends TypeScriptBase const locations = response.body.refs .map(reference => - new Location(this.client.asUrl(reference.file), - new Range( - reference.start.line - 1, reference.start.offset - 1, - reference.end.line - 1, reference.end.offset - 1))) + new Location(this.client.asUrl(reference.file), tsTextSpanToVsRange(reference))) .filter(location => // Exclude original definition from references - !(location.uri.fsPath === codeLens.document.fsPath && + !(location.uri.toString() === codeLens.document.toString() && location.range.start.isEqual(codeLens.range.start))); codeLens.command = { diff --git a/extensions/typescript/src/features/renameProvider.ts b/extensions/typescript/src/features/renameProvider.ts index 291be640ca7..26bbb6a29ea 100644 --- a/extensions/typescript/src/features/renameProvider.ts +++ b/extensions/typescript/src/features/renameProvider.ts @@ -3,10 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { RenameProvider, WorkspaceEdit, TextDocument, Position, Range, CancellationToken } from 'vscode'; +import { RenameProvider, WorkspaceEdit, TextDocument, Position, CancellationToken } from 'vscode'; import * as Proto from '../protocol'; import { ITypescriptServiceClient } from '../typescriptService'; +import { tsTextSpanToVsRange, vsPositionToTsFileLocation } from '../utils/convert'; export default class TypeScriptRenameProvider implements RenameProvider { public constructor( @@ -24,9 +25,7 @@ export default class TypeScriptRenameProvider implements RenameProvider { } const args: Proto.RenameRequestArgs = { - file: filepath, - line: position.line + 1, - offset: position.character + 1, + ...vsPositionToTsFileLocation(filepath, position), findInStrings: false, findInComments: false }; @@ -49,9 +48,7 @@ export default class TypeScriptRenameProvider implements RenameProvider { continue; } for (const textSpan of spanGroup.locs) { - result.replace(resource, - new Range(textSpan.start.line - 1, textSpan.start.offset - 1, textSpan.end.line - 1, textSpan.end.offset - 1), - newName); + result.replace(resource, tsTextSpanToVsRange(textSpan), newName); } } return result; diff --git a/extensions/typescript/src/features/signatureHelpProvider.ts b/extensions/typescript/src/features/signatureHelpProvider.ts index d4fb33e2bfb..ea0de1f04d5 100644 --- a/extensions/typescript/src/features/signatureHelpProvider.ts +++ b/extensions/typescript/src/features/signatureHelpProvider.ts @@ -8,6 +8,7 @@ import { SignatureHelpProvider, SignatureHelp, SignatureInformation, ParameterIn import * as Previewer from './previewer'; import * as Proto from '../protocol'; import { ITypescriptServiceClient } from '../typescriptService'; +import { vsPositionToTsFileLocation } from '../utils/convert'; export default class TypeScriptSignatureHelpProvider implements SignatureHelpProvider { @@ -19,11 +20,7 @@ export default class TypeScriptSignatureHelpProvider implements SignatureHelpPro if (!filepath) { return Promise.resolve(null); } - const args: Proto.SignatureHelpRequestArgs = { - file: filepath, - line: position.line + 1, - offset: position.character + 1 - }; + const args: Proto.SignatureHelpRequestArgs = vsPositionToTsFileLocation(filepath, position); return this.client.execute('signatureHelp', args, token).then((response) => { const info = response.body; if (!info) { diff --git a/extensions/typescript/src/features/taskProvider.ts b/extensions/typescript/src/features/taskProvider.ts index 7b88988257e..46dce9fa311 100644 --- a/extensions/typescript/src/features/taskProvider.ts +++ b/extensions/typescript/src/features/taskProvider.ts @@ -28,6 +28,7 @@ const exists = (file: string): Promise => interface TypeScriptTaskDefinition extends vscode.TaskDefinition { tsconfig: string; + option?: string; } /** @@ -53,7 +54,7 @@ class TscTaskProvider implements vscode.TaskProvider { for (const project of await this.getAllTsConfigs(token)) { if (!configPaths.has(project.path)) { configPaths.add(project.path); - tasks.push(await this.getBuildTaskForProject(project)); + tasks.push(...(await this.getTasksForProject(project))); } } return tasks; @@ -135,18 +136,6 @@ class TscTaskProvider implements vscode.TaskProvider { return 'tsc'; } - private shouldUseWatchForBuild(configFile: TSConfig): boolean { - try { - const config = JSON.parse(fs.readFileSync(configFile.path, 'utf-8')); - if (config) { - return !!config.compileOnSave; - } - } catch (e) { - // noop - } - return false; - } - private getActiveTypeScriptFile(): string | null { const editor = vscode.window.activeTextEditor; if (editor) { @@ -158,7 +147,7 @@ class TscTaskProvider implements vscode.TaskProvider { return null; } - private async getBuildTaskForProject(project: TSConfig): Promise { + private async getTasksForProject(project: TSConfig): Promise { const command = await this.getCommand(project); let label: string = project.path; @@ -178,22 +167,27 @@ class TscTaskProvider implements vscode.TaskProvider { } } - const watch = false && this.shouldUseWatchForBuild(project); - const identifier: TypeScriptTaskDefinition = { type: 'typescript', tsconfig: label }; + const buildTaskidentifier: TypeScriptTaskDefinition = { type: 'typescript', tsconfig: label }; const buildTask = new vscode.Task( - identifier, - watch - ? localize('buildAndWatchTscLabel', 'watch - {0}', label) - : localize('buildTscLabel', 'build - {0}', label), + buildTaskidentifier, + localize('buildTscLabel', 'build - {0}', label), 'tsc', - new vscode.ShellExecution(`${command} ${watch ? '--watch' : ''} -p "${project.path}"`), - watch - ? '$tsc-watch' - : '$tsc' - ); + new vscode.ShellExecution(`${command} -p "${project.path}"`), + '$tsc'); buildTask.group = vscode.TaskGroup.Build; - buildTask.isBackground = watch; - return buildTask; + buildTask.isBackground = false; + + const watchTaskidentifier: TypeScriptTaskDefinition = { type: 'typescript', tsconfig: label, option: 'watch' }; + const watchTask = new vscode.Task( + watchTaskidentifier, + localize('buildAndWatchTscLabel', 'watch - {0}', label), + 'tsc', + new vscode.ShellExecution(`${command} --watch -p "${project.path}"`), + '$tsc-watch'); + watchTask.group = vscode.TaskGroup.Build; + watchTask.isBackground = true; + + return [buildTask, watchTask]; } } diff --git a/extensions/typescript/src/features/workspaceSymbolProvider.ts b/extensions/typescript/src/features/workspaceSymbolProvider.ts index ab2329501a3..8885eb877ac 100644 --- a/extensions/typescript/src/features/workspaceSymbolProvider.ts +++ b/extensions/typescript/src/features/workspaceSymbolProvider.ts @@ -3,10 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { workspace, window, Uri, WorkspaceSymbolProvider, SymbolInformation, SymbolKind, Range, Location, CancellationToken } from 'vscode'; +import { workspace, window, Uri, WorkspaceSymbolProvider, SymbolInformation, SymbolKind, Location, CancellationToken } from 'vscode'; import * as Proto from '../protocol'; import { ITypescriptServiceClient } from '../typescriptService'; +import { tsTextSpanToVsRange } from '../utils/convert'; function getSymbolKind(item: Proto.NavtoItem): SymbolKind { switch (item.kind) { @@ -67,7 +68,7 @@ export default class TypeScriptWorkspaceSymbolProvider implements WorkspaceSymbo if (!item.containerName && item.kind === 'alias') { continue; } - const range = new Range(item.start.line - 1, item.start.offset - 1, item.end.line - 1, item.end.offset - 1); + const range = tsTextSpanToVsRange(item); let label = item.name; if (item.kind === 'method' || item.kind === 'function') { label += '()'; diff --git a/extensions/typescript/src/typescriptMain.ts b/extensions/typescript/src/typescriptMain.ts index 9c472f187f0..b4bd55a6357 100644 --- a/extensions/typescript/src/typescriptMain.ts +++ b/extensions/typescript/src/typescriptMain.ts @@ -33,6 +33,8 @@ import TypingsStatus, { AtaProgressReporter } from './utils/typingsStatus'; import VersionStatus from './utils/versionStatus'; import { getContributedTypeScriptServerPlugins, TypeScriptServerPlugin } from './utils/plugins'; import { openOrCreateConfigFile, isImplicitProjectConfigFile } from './utils/tsconfig'; +import { tsLocationToVsPosition } from './utils/convert'; +import FormattingConfigurationManager from './features/formattingConfigurationManager'; interface LanguageDescription { id: string; @@ -173,6 +175,7 @@ class LanguageProvider { private syntaxDiagnostics: ObjectMap; private readonly currentDiagnostics: DiagnosticCollection; private readonly bufferSyncSupport: BufferSyncSupport; + private readonly formattingOptionsManager: FormattingConfigurationManager; private readonly typingsStatus: TypingsStatus; private readonly ataProgressReporter: AtaProgressReporter; @@ -188,6 +191,7 @@ class LanguageProvider { private readonly client: TypeScriptServiceClient, private readonly description: LanguageDescription ) { + this.formattingOptionsManager = new FormattingConfigurationManager(client); this.bufferSyncSupport = new BufferSyncSupport(client, description.modeIds, { delete: (file: string) => { this.currentDiagnostics.delete(client.asUrl(file)); @@ -238,12 +242,12 @@ class LanguageProvider { const completionItemProvider = new (await import('./features/completionItemProvider')).default(client, this.typingsStatus); completionItemProvider.updateConfiguration(); this.toUpdateOnConfigurationChanged.push(completionItemProvider); - this.disposables.push(languages.registerCompletionItemProvider(selector, completionItemProvider, '.')); + this.disposables.push(languages.registerCompletionItemProvider(selector, completionItemProvider, '.', '"', '\'', '/', '@')); this.disposables.push(languages.registerCompletionItemProvider(selector, new (await import('./features/directiveCommentCompletionProvider')).default(client), '@')); const { TypeScriptFormattingProvider, FormattingProviderManager } = await import('./features/formattingProvider'); - const formattingProvider = new TypeScriptFormattingProvider(client); + const formattingProvider = new TypeScriptFormattingProvider(client, this.formattingOptionsManager); formattingProvider.updateConfiguration(config); this.disposables.push(languages.registerOnTypeFormattingEditProvider(selector, formattingProvider, ';', '}', '\n')); @@ -263,8 +267,8 @@ class LanguageProvider { this.disposables.push(languages.registerDocumentSymbolProvider(selector, new (await import('./features/documentSymbolProvider')).default(client))); this.disposables.push(languages.registerSignatureHelpProvider(selector, new (await import('./features/signatureHelpProvider')).default(client), '(', ',')); this.disposables.push(languages.registerRenameProvider(selector, new (await import('./features/renameProvider')).default(client))); - this.disposables.push(languages.registerCodeActionsProvider(selector, new (await import('./features/codeActionProvider')).default(client, this.description.id))); - this.disposables.push(languages.registerCodeActionsProvider(selector, new (await import('./features/refactorProvider')).default(client, this.description.id))); + this.disposables.push(languages.registerCodeActionsProvider(selector, new (await import('./features/codeActionProvider')).default(client, this.formattingOptionsManager, this.description.id))); + this.disposables.push(languages.registerCodeActionsProvider(selector, new (await import('./features/refactorProvider')).default(client, this.formattingOptionsManager, this.description.id))); this.registerVersionDependentProviders(); for (const modeId of this.description.modeIds) { @@ -322,6 +326,7 @@ class LanguageProvider { private configurationChanged(): void { const config = workspace.getConfiguration(this.id); this.updateValidate(config.get(validateSetting, true)); + this.formattingOptionsManager.updateConfiguration(config); for (const toUpdate of this.toUpdateOnConfigurationChanged) { toUpdate.updateConfiguration(); @@ -681,7 +686,7 @@ class TypeScriptServiceClientHost implements ITypescriptServiceClientHost { const result: Diagnostic[] = []; for (let diagnostic of diagnostics) { const { start, end, text } = diagnostic; - const range = new Range(start.line - 1, start.offset - 1, end.line - 1, end.offset - 1); + const range = new Range(tsLocationToVsPosition(start), tsLocationToVsPosition(end)); const converted = new Diagnostic(range, text); converted.severity = this.getDiagnosticSeverity(diagnostic); converted.source = diagnostic.source || source; diff --git a/extensions/typescript/src/utils/convert.ts b/extensions/typescript/src/utils/convert.ts new file mode 100644 index 00000000000..299597f34d5 --- /dev/null +++ b/extensions/typescript/src/utils/convert.ts @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import * as Proto from '../protocol'; + + +export const tsTextSpanToVsRange = (span: Proto.TextSpan) => + new vscode.Range( + span.start.line - 1, span.start.offset - 1, + span.end.line - 1, span.end.offset - 1); + +export const tsLocationToVsPosition = (tslocation: Proto.Location) => + new vscode.Position(tslocation.line - 1, tslocation.offset - 1); + +export const vsPositionToTsFileLocation = (file: string, position: vscode.Position): Proto.FileLocationRequestArgs => ({ + file, + line: position.line + 1, + offset: position.character + 1 +}); + +export const vsRangeToTsFileRange = (file: string, range: vscode.Range): Proto.FileRangeRequestArgs => ({ + file, + startLine: range.start.line + 1, + startOffset: range.start.character + 1, + endLine: range.end.line + 1, + endOffset: range.end.character + 1 +}); \ No newline at end of file diff --git a/extensions/vb/language-configuration.json b/extensions/vb/language-configuration.json index f1fabbd4f22..6ef9c3c4d48 100644 --- a/extensions/vb/language-configuration.json +++ b/extensions/vb/language-configuration.json @@ -20,5 +20,11 @@ ["(", ")"], ["\"", "\""], ["<", ">"] - ] + ], + "folding": { + "markers": { + "start": "^\\s*#Region", + "end": "^\\s*#End Region" + } + } } \ No newline at end of file diff --git a/extensions/vscode-api-tests/package.json b/extensions/vscode-api-tests/package.json index 93ac38acad8..1b3e93e88ec 100644 --- a/extensions/vscode-api-tests/package.json +++ b/extensions/vscode-api-tests/package.json @@ -3,6 +3,7 @@ "description": "API tests for VS Code", "version": "0.0.1", "publisher": "vscode", + "enableProposedApi": true, "private": true, "engines": { "vscode": "*" diff --git a/extensions/vscode-api-tests/src/commands.test.ts b/extensions/vscode-api-tests/src/commands.test.ts index 47b4d85f347..0d92795d241 100644 --- a/extensions/vscode-api-tests/src/commands.test.ts +++ b/extensions/vscode-api-tests/src/commands.test.ts @@ -134,4 +134,4 @@ suite('commands namespace tests', () => { return Promise.all([a, b, c, d]); }); -}); +}); \ No newline at end of file diff --git a/extensions/vscode-api-tests/src/window.test.ts b/extensions/vscode-api-tests/src/window.test.ts index a9d78e8cad3..df08d550a58 100644 --- a/extensions/vscode-api-tests/src/window.test.ts +++ b/extensions/vscode-api-tests/src/window.test.ts @@ -138,6 +138,26 @@ suite('window namespace tests', () => { assert.equal(window.activeTextEditor!.viewColumn, ViewColumn.One); }); + test('issue #27408 - showTextDocument & vscode.diff always default to ViewColumn.One', async () => { + const [docA, docB, docC] = await Promise.all([ + workspace.openTextDocument(await createRandomFile()), + workspace.openTextDocument(await createRandomFile()), + workspace.openTextDocument(await createRandomFile()) + ]); + + await window.showTextDocument(docA, ViewColumn.One); + await window.showTextDocument(docB, ViewColumn.Two); + + assert.ok(window.activeTextEditor); + assert.ok(window.activeTextEditor!.document === docB); + assert.equal(window.activeTextEditor!.viewColumn, ViewColumn.Two); + + await window.showTextDocument(docC, ViewColumn.Active); + + assert.ok(window.activeTextEditor!.document === docC); + assert.equal(window.activeTextEditor!.viewColumn, ViewColumn.Two); + }); + test('issue #5362 - Incorrect TextEditor passed by onDidChangeTextEditorSelection', (done) => { const file10Path = join(workspace.rootPath || '', './10linefile.ts'); const file30Path = join(workspace.rootPath || '', './30linefile.ts'); @@ -329,6 +349,18 @@ suite('window namespace tests', () => { return Promise.all([a, b]); }); + // test('showWorkspaceFolderPick', function () { + // const p = (window).showWorkspaceFolderPick(undefined); + + // return commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem').then(() => { + // return p.then(workspace => { + // assert.ok(true); + // }, error => { + // assert.ok(false); + // }); + // }); + // }); + test('Default value for showInput Box accepted even if fails validateInput, #33691', function () { const result = window.showInputBox({ validateInput: (value: string) => { diff --git a/extensions/yaml/language-configuration.json b/extensions/yaml/language-configuration.json index cab4f6602ff..cc1aa26cde6 100644 --- a/extensions/yaml/language-configuration.json +++ b/extensions/yaml/language-configuration.json @@ -20,5 +20,8 @@ ["(", ")"], ["\"", "\""], ["'", "'"] - ] + ], + "folding": { + "offSide": true + } } \ No newline at end of file diff --git a/i18n/chs/extensions/css/package.i18n.json b/i18n/chs/extensions/css/package.i18n.json index 8f8d582630b..6b34f395c3d 100644 --- a/i18n/chs/extensions/css/package.i18n.json +++ b/i18n/chs/extensions/css/package.i18n.json @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { + "css.title": "CSS", "css.lint.argumentsInColorFunction.desc": "参数数量无效", "css.lint.boxModel.desc": "使用边距或边框时,不要使用宽度或高度", "css.lint.compatibleVendorPrefixes.desc": "使用供应商特定前缀时,确保同时包括所有其他供应商特定属性", @@ -25,6 +26,7 @@ "css.trace.server.desc": "跟踪 VS Code 与 CSS 语言服务器之间的通信。", "css.validate.title": "控制 CSS 验证和问题严重性。", "css.validate.desc": "启用或禁用所有验证", + "less.title": "LESS", "less.lint.argumentsInColorFunction.desc": "参数数量无效", "less.lint.boxModel.desc": "使用边距或边框时,不要使用宽度或高度", "less.lint.compatibleVendorPrefixes.desc": "使用供应商特定前缀时,确保同时包括所有其他供应商特定属性", @@ -45,6 +47,7 @@ "less.lint.zeroUnits.desc": "零不需要单位", "less.validate.title": "控制 LESS 验证和问题严重性。", "less.validate.desc": "启用或禁用所有验证", + "scss.title": "SCSS (Sass)", "scss.lint.argumentsInColorFunction.desc": "参数数量无效", "scss.lint.boxModel.desc": "使用边距或边框时,不要使用宽度或高度", "scss.lint.compatibleVendorPrefixes.desc": "使用供应商特定前缀时,确保同时包括所有其他供应商特定属性", diff --git a/i18n/chs/src/vs/code/electron-main/menus.i18n.json b/i18n/chs/src/vs/code/electron-main/menus.i18n.json index 40b03cafea8..34e3c359aa2 100644 --- a/i18n/chs/src/vs/code/electron-main/menus.i18n.json +++ b/i18n/chs/src/vs/code/electron-main/menus.i18n.json @@ -171,8 +171,6 @@ "miRunningTask": "显示正在运行的任务(&&G)...", "miRestartTask": "重启正在运行的任务(&&E)...", "miTerminateTask": "终止任务(&&T)...", - "miConfigureTask": "配置任务(&&C)", - "miConfigureBuildTask": "配置默认生成任务(&&F)", "accessibilityOptionsWindowTitle": "辅助功能选项", "miRestartToUpdate": "重启以更新...", "miCheckingForUpdates": "正在检查更新...", diff --git a/i18n/chs/src/vs/editor/common/config/commonEditorConfig.i18n.json b/i18n/chs/src/vs/editor/common/config/commonEditorConfig.i18n.json index f7681a600e9..3abd825501a 100644 --- a/i18n/chs/src/vs/editor/common/config/commonEditorConfig.i18n.json +++ b/i18n/chs/src/vs/editor/common/config/commonEditorConfig.i18n.json @@ -88,6 +88,7 @@ "accessibilitySupport": "控制编辑器是否应运行在对屏幕阅读器进行优化的模式。", "links": "控制编辑器是否应检测链接并使它们可被点击", "colorDecorators": "控制编辑器是否显示内联颜色修饰器和颜色选取器。", + "codeActions": "启用代码操作小灯泡提示", "sideBySide": "控制 Diff 编辑器以并排或内联形式显示差异", "ignoreTrimWhitespace": "控制差异编辑器是否将对前导空格或尾随空格的更改显示为差异", "renderIndicators": "控制差异编辑器是否为已添加/删除的更改显示 +/- 指示符号", diff --git a/i18n/chs/src/vs/platform/environment/node/argv.i18n.json b/i18n/chs/src/vs/platform/environment/node/argv.i18n.json index 37a32ad9d69..9fc0d95c7bd 100644 --- a/i18n/chs/src/vs/platform/environment/node/argv.i18n.json +++ b/i18n/chs/src/vs/platform/environment/node/argv.i18n.json @@ -15,6 +15,7 @@ "reuseWindow": "在上一活动窗口中强制打开文件或文件夹。", "userDataDir": "指定存放用户数据的目录,此目录在作为根运行时十分有用。", "verbose": "打印详细输出(表示 - 等待)。", + "wait": "等文件关闭后再返回。", "extensionHomePath": "设置扩展的根路径。", "listExtensions": "列出已安装的扩展。", "showVersions": "使用 --list-extension 时,显示已安装扩展的版本。", diff --git a/i18n/chs/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json b/i18n/chs/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json index c550c5e4e59..9a0ff2ab7db 100644 --- a/i18n/chs/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json +++ b/i18n/chs/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json @@ -5,10 +5,7 @@ // Do not edit this file. It is machine generated. { "invalidManifest": "扩展无效: package.json 不是 JSON 文件。", - "restartCode": "请先重启 Code 再重新安装 {0}。", - "installDependeciesConfirmation": "安装”{0}“还会安装其依赖项。是否要继续?", - "install": "是", - "doNotInstall": "否", + "restartCodeLocal": "请先重启 Code 再重新安装 {0}。", "uninstallDependeciesConfirmation": "要仅卸载“{0}”或者其依赖项也一起卸载?", "uninstallOnly": "仅", "uninstallAll": "全部", diff --git a/i18n/chs/src/vs/platform/extensions/common/extensionsRegistry.i18n.json b/i18n/chs/src/vs/platform/extensions/common/extensionsRegistry.i18n.json index 177ad607767..4ae3c02e3c1 100644 --- a/i18n/chs/src/vs/platform/extensions/common/extensionsRegistry.i18n.json +++ b/i18n/chs/src/vs/platform/extensions/common/extensionsRegistry.i18n.json @@ -16,6 +16,7 @@ "vscode.extension.activationEvents": "VS Code 扩展的激活事件。", "vscode.extension.activationEvents.onLanguage": "在打开被解析为指定语言的文件时发出的激活事件。", "vscode.extension.activationEvents.onCommand": "在调用指定命令时发出的激活事件。", + "vscode.extension.activationEvents.onDebug": "在用户准备调试或准备设置调试配置时发出的激活事件。", "vscode.extension.activationEvents.workspaceContains": "在打开至少包含一个匹配指定 glob 模式的文件的文件夹时发出的激活事件。", "vscode.extension.activationEvents.onView": "在指定视图被展开时发出的激活事件。", "vscode.extension.activationEvents.star": "在 VS Code 启动时发出的激活事件。为确保良好的最终用户体验,请仅在其他激活事件组合不适用于你的情况时,才在扩展中使用此事件。", diff --git a/i18n/chs/src/vs/platform/theme/common/colorExtensionPoint.i18n.json b/i18n/chs/src/vs/platform/theme/common/colorExtensionPoint.i18n.json index 8cd1865980d..72e224481c5 100644 --- a/i18n/chs/src/vs/platform/theme/common/colorExtensionPoint.i18n.json +++ b/i18n/chs/src/vs/platform/theme/common/colorExtensionPoint.i18n.json @@ -13,6 +13,8 @@ "contributes.defaults.highContrast": "高对比度主题的默认颜色。应为十六进制颜色值 (#RRGGBB[AA]) 或是主题颜色标识符,其提供默认值。", "invalid.colorConfiguration": "\"configuration.colors\" 必须是数组", "invalid.default.colorType": "{0} 必须为十六进制颜色值 (#RRGGBB[AA] 或 #RGB[A]) 或是主题颜色标识符,其提供默认值。", + "invalid.id": "必须定义 \"configuration.colors.id\",且不能为空", "invalid.id.format": "\"configuration.colors.id\" 必须满足 word[.word]*", + "invalid.description": "必须定义 \"configuration.colors.description\",且不能为空", "invalid.defaults": "必须定义 “configuration.colors.defaults”,且须包含 \"light\"(浅色)、\"dark\"(深色) 和 \"highContrast\"(高对比度)" } \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/browser/actions/workspaceActions.i18n.json b/i18n/chs/src/vs/workbench/browser/actions/workspaceActions.i18n.json index 8a712411c27..7b7cfbc9ea7 100644 --- a/i18n/chs/src/vs/workbench/browser/actions/workspaceActions.i18n.json +++ b/i18n/chs/src/vs/workbench/browser/actions/workspaceActions.i18n.json @@ -14,7 +14,6 @@ "selectWorkspace": "选择文件夹作为工作区", "removeFolderFromWorkspace": "将文件夹从工作区删除", "saveWorkspaceAsAction": "将工作区另存为...", - "saveEmptyWorkspaceNotSupported": "请先打开一个工作区再保存。", "save": "保存(&&S)", "saveWorkspace": "保存工作区", "openWorkspaceAction": "打开工作区...", diff --git a/i18n/chs/src/vs/workbench/browser/parts/views/panelViewlet.i18n.json b/i18n/chs/src/vs/workbench/browser/parts/views/panelViewlet.i18n.json new file mode 100644 index 00000000000..08ebcb29e96 --- /dev/null +++ b/i18n/chs/src/vs/workbench/browser/parts/views/panelViewlet.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "viewToolbarAriaLabel": "{0} 操作" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json b/i18n/chs/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json index 15235fff2c5..f0b871bf751 100644 --- a/i18n/chs/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json +++ b/i18n/chs/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json @@ -12,6 +12,7 @@ "breakpointRemoved": "已删除断点,行 {0},文件 {1}", "compoundMustHaveConfigurations": "复合项必须拥有 \"configurations\" 属性集,才能启动多个配置。", "configMissing": "\"launch.json\" 中缺少配置“{0}”。", + "debugRequesMissing": "所选的启动配置缺少调试请求", "debugTypeNotSupported": "配置的类型“{0}”不受支持。", "debugTypeMissing": "所选的启动配置缺少属性 \"type\"。", "preLaunchTaskErrors": "preLaunchTask“{0}”期间检测到多个生成错误。", diff --git a/i18n/chs/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json b/i18n/chs/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json index 4b54f4bccd1..0ccd1274af5 100644 --- a/i18n/chs/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json +++ b/i18n/chs/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json @@ -51,9 +51,8 @@ "showLanguageExtensionsShort": "语言扩展", "showAzureExtensions": "显示 Azure 扩展", "showAzureExtensionsShort": "Azure 扩展", - "configureWorkspaceRecommendedExtensions": "配置建议的扩展(工作区)", - "ConfigureWorkspaceRecommendations.noWorkspace": "建议仅在工作区文件夹上可用。", "OpenExtensionsFile.failed": "无法在 \".vscode\" 文件夹({0})内创建 \"extensions.json\" 文件。", + "configureWorkspaceRecommendedExtensions": "配置建议的扩展(工作区)", "builtin": "内置", "disableAll": "禁用所有已安装的扩展", "disableAllWorkspace": "禁用此工作区的所有已安装的扩展", diff --git a/i18n/chs/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json b/i18n/chs/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json index fefc047fe76..a6386fe575c 100644 --- a/i18n/chs/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json +++ b/i18n/chs/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json @@ -9,7 +9,6 @@ "openGlobalKeybindingsFile": "打开键盘快捷方式文件", "openWorkspaceSettings": "打开工作区设置", "openFolderSettings": "打开文件夹设置", - "pickFolder": "选择文件夹", "configureLanguageBasedSettings": "配置语言特定的设置...", "languageDescriptionConfigured": "({0})", "pickLanguage": "选择语言" diff --git a/i18n/chs/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json b/i18n/chs/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json index d54e22f7680..432af5ba012 100644 --- a/i18n/chs/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json +++ b/i18n/chs/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json @@ -6,7 +6,5 @@ { "relaunchSettingMessage": "设置已更改,需要重启才能生效。", "relaunchSettingDetail": "按下“重启”按钮以重新启动 {0} 并启用该设置。", - "restart": "重启", - "relaunchWorkspaceMessage": "此工作区更改需要重载扩展系统。", - "reload": "重新加载" + "restart": "重启" } \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json b/i18n/chs/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json index 234516b6743..df96046e5e4 100644 --- a/i18n/chs/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json +++ b/i18n/chs/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json @@ -5,13 +5,7 @@ // Do not edit this file. It is machine generated. { "tasksCategory": "任务", - "ConfigureTaskRunnerAction.noWorkspace": "任务仅在工作区文件夹上可用。", - "ConfigureTaskRunnerAction.quickPick.template": "选择任务运行程序", - "ConfigureTaskRunnerAction.autoDetecting": "适用于 {0} 的自动检测任务", - "ConfigureTaskRunnerAction.autoDetect": "自动检测系统任务失败。请使用默认模板。有关详细信息,请参阅任务输出。", - "ConfigureTaskRunnerAction.autoDetectError": "自动检测任务系统时出现错误。有关详细信息,请参阅任务输出。", - "ConfigureTaskRunnerAction.failed": "无法在 \".vscode\" 文件夹中创建 \"tasks.json\" 文件。查看任务输出了解详细信息。", - "ConfigureTaskRunnerAction.label": "配置任务运行程序", + "ConfigureTaskRunnerAction.label": "配置任务", "ConfigureBuildTaskAction.label": "配置生成任务", "CloseMessageAction.label": "关闭", "ShowTerminalAction.label": "查看终端", @@ -47,22 +41,16 @@ "configured": "已配置的任务", "detected": "检测到的任务", "TaskService.fetchingBuildTasks": "正在获取生成任务...", - "TaskService.noBuildTaskTerminal": "未能找到生成任务。按“配置生成任务”来定义一个。", "TaskService.pickBuildTask": "选择要运行的生成任务", "TaskService.fetchingTestTasks": "正在获取测试任务...", - "TaskService.noTestTaskTerminal": "未能找到测试任务。按“配置任务运行程序”来定义一个。", "TaskService.pickTestTask": "选择要运行的测试任务", - "TaskService.noTaskRunning": "当前没有任务在运行。", "TaskService.tastToTerminate": "选择要终止的任务", "TerminateAction.noProcess": "启动的进程不再存在。如果任务衍生的后台任务退出 VS Code,则可能会导致出现孤立的进程。", "TerminateAction.failed": "未能终止运行中的任务", - "TaskService.noTaskToRestart": "没有要重启的任务。", "TaskService.tastToRestart": "选择要重启的任务", - "TaskService.defaultBuildTaskExists": "{0} 已被标记为默认生成任务。", "TaskService.pickDefaultBuildTask": "选择要用作默认生成任务的任务", "TaskService.defaultTestTaskExists": "{0} 已被标记为默认测试任务。", "TaskService.pickDefaultTestTask": "选择要用作默认测试任务的任务", - "TaskService.noTaskIsRunning": "没有任务在运行。", "TaskService.pickShowTask": "选择要显示输出的任务", "ShowLogAction.label": "显示任务日志", "RunTaskAction.label": "运行任务", diff --git a/i18n/chs/src/vs/workbench/services/configuration/node/configuration.i18n.json b/i18n/chs/src/vs/workbench/services/configuration/node/configuration.i18n.json index 2f7035049db..6a7da8e17c6 100644 --- a/i18n/chs/src/vs/workbench/services/configuration/node/configuration.i18n.json +++ b/i18n/chs/src/vs/workbench/services/configuration/node/configuration.i18n.json @@ -13,7 +13,6 @@ "invalid.title": "configuration.title 必须是字符串", "vscode.extension.contributes.defaultConfiguration": "按语言提供默认编辑器配置设置。", "invalid.properties": "configuration.properties 必须是对象", - "workspaceConfig.folders.description": "要在工作区中加载的文件夹列表。必须为文件夹完整路径。例如 \"/root/folderA\" 或 \"./folderA\"。后者表示根据工作区文件位置进行解析的相对路径。", - "workspaceConfig.folder.description": "文件路径。例如 \"/root/folderA\" 或 \"./folderA\"。后者表示根据工作区文件位置进行解析的相对路径。", + "workspaceConfig.path.description": "文件路径。例如 \"/root/folderA\" 或 \"./folderA\"。后者表示根据工作区文件位置进行解析的相对路径。", "workspaceConfig.settings.description": "工作区设置" } \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/services/files/electron-browser/remoteFileService.i18n.json b/i18n/chs/src/vs/workbench/services/files/electron-browser/remoteFileService.i18n.json new file mode 100644 index 00000000000..80b697be3d3 --- /dev/null +++ b/i18n/chs/src/vs/workbench/services/files/electron-browser/remoteFileService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileBinaryError": "文件似乎是二进制文件,无法作为文档打开" +} \ No newline at end of file diff --git a/i18n/cht/extensions/css/package.i18n.json b/i18n/cht/extensions/css/package.i18n.json index 86489b3522c..b346ac6c6b8 100644 --- a/i18n/cht/extensions/css/package.i18n.json +++ b/i18n/cht/extensions/css/package.i18n.json @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { + "css.title": "CSS", "css.lint.argumentsInColorFunction.desc": "參數數目無效", "css.lint.boxModel.desc": "使用填補或框線時不要使用寬度或高度。", "css.lint.compatibleVendorPrefixes.desc": "在使用廠商專屬的前置詞時,請確定也包括其他所有的廠商特定屬性。", @@ -25,6 +26,7 @@ "css.trace.server.desc": "追蹤 VS Code 與 CSS 語言伺服器之間的通訊。", "css.validate.title": "控制 CSS 驗證與問題嚴重性。", "css.validate.desc": "啟用或停用所有驗證", + "less.title": "LESS", "less.lint.argumentsInColorFunction.desc": "參數數目無效", "less.lint.boxModel.desc": "使用填補或框線時不要使用寬度或高度。", "less.lint.compatibleVendorPrefixes.desc": "在使用廠商專屬的前置詞時,請確定也包括其他所有的廠商特定屬性。", @@ -45,6 +47,7 @@ "less.lint.zeroUnits.desc": "零不需要任何單位", "less.validate.title": "控制 LESS 驗證與問題嚴重性。", "less.validate.desc": "啟用或停用所有驗證", + "scss.title": "SCSS (Sass)", "scss.lint.argumentsInColorFunction.desc": "參數數目無效", "scss.lint.boxModel.desc": "使用填補或框線時不要使用寬度或高度。", "scss.lint.compatibleVendorPrefixes.desc": "在使用廠商專屬的前置詞時,請確定也包括其他所有的廠商特定屬性。", diff --git a/i18n/cht/src/vs/code/electron-main/menus.i18n.json b/i18n/cht/src/vs/code/electron-main/menus.i18n.json index fe521f0b990..c6347c60ba9 100644 --- a/i18n/cht/src/vs/code/electron-main/menus.i18n.json +++ b/i18n/cht/src/vs/code/electron-main/menus.i18n.json @@ -171,8 +171,6 @@ "miRunningTask": "顯示執行中的工作(&&G)...", "miRestartTask": "重新開始執行工作(&&E)...", "miTerminateTask": "終止工作(&&T)...", - "miConfigureTask": "設定工作(&&C)", - "miConfigureBuildTask": "設定預設建置工作(&&F)", "accessibilityOptionsWindowTitle": "協助工具選項", "miRestartToUpdate": "重新啟動以更新...", "miCheckingForUpdates": "正在查看是否有更新...", diff --git a/i18n/cht/src/vs/editor/common/config/commonEditorConfig.i18n.json b/i18n/cht/src/vs/editor/common/config/commonEditorConfig.i18n.json index b44b05e5981..b2a9d91c6b1 100644 --- a/i18n/cht/src/vs/editor/common/config/commonEditorConfig.i18n.json +++ b/i18n/cht/src/vs/editor/common/config/commonEditorConfig.i18n.json @@ -88,6 +88,7 @@ "accessibilitySupport": "控制編輯器是否應於已為螢幕助讀程式最佳化的模式中執行。", "links": "控制編輯器是否應偵測連結且讓它可點擊", "colorDecorators": "控制編輯器是否應轉譯內嵌色彩裝飾項目與色彩選擇器。", + "codeActions": "啟用程式動作燈泡提示", "sideBySide": "控制 Diff 編輯器要並排或內嵌顯示差異", "ignoreTrimWhitespace": "控制 Diff 編輯器是否將開頭或尾端空白字元的變更顯示為差異", "renderIndicators": "控制 Diff 編輯器是否要為新增的/移除的變更顯示 +/- 標記", diff --git a/i18n/cht/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json b/i18n/cht/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json index 2a73c1b3b28..91ed221b018 100644 --- a/i18n/cht/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json +++ b/i18n/cht/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json @@ -5,10 +5,8 @@ // Do not edit this file. It is machine generated. { "invalidManifest": "擴充功能無效: package.json 不是 JSON 檔案。", - "restartCode": "請先重新啟動 Code,再重新安裝 {0}。", - "installDependeciesConfirmation": "安裝 '{0}' 也會安裝其相依性。要繼續嗎?", - "install": "是", - "doNotInstall": "否", + "restartCodeLocal": "請先重新啟動 Code,再重新安裝 {0}。", + "restartCodeGallery": "重新安裝之前,請先重新啟動 Code。", "uninstallDependeciesConfirmation": "只要將 '{0}' 解除安裝,或要包含其相依性?", "uninstallOnly": "只有", "uninstallAll": "全部", diff --git a/i18n/cht/src/vs/platform/extensions/common/extensionsRegistry.i18n.json b/i18n/cht/src/vs/platform/extensions/common/extensionsRegistry.i18n.json index 9e42e641acc..50abb35f281 100644 --- a/i18n/cht/src/vs/platform/extensions/common/extensionsRegistry.i18n.json +++ b/i18n/cht/src/vs/platform/extensions/common/extensionsRegistry.i18n.json @@ -16,6 +16,7 @@ "vscode.extension.activationEvents": "VS Code 擴充功能的啟動事件。", "vscode.extension.activationEvents.onLanguage": "當指定語言檔案開啟時激發該事件", "vscode.extension.activationEvents.onCommand": "當指定的命令被調用時激發該事件", + "vscode.extension.activationEvents.onDebug": "當使用者正要開始偵錯或是設定偵錯組態時激發該事件", "vscode.extension.activationEvents.workspaceContains": "當開啟指定的文件夾包含glob模式匹配的文件時激發該事件", "vscode.extension.activationEvents.onView": "當指定的檢視被擴展時激發該事件", "vscode.extension.activationEvents.star": "當VS Code啟動時激發該事件,為了確保最好的使用者體驗,當您的擴充功能沒有其他組合作業時,請激活此事件.", diff --git a/i18n/cht/src/vs/platform/theme/common/colorExtensionPoint.i18n.json b/i18n/cht/src/vs/platform/theme/common/colorExtensionPoint.i18n.json index 75d3b7558fd..4c27d1b8aa4 100644 --- a/i18n/cht/src/vs/platform/theme/common/colorExtensionPoint.i18n.json +++ b/i18n/cht/src/vs/platform/theme/common/colorExtensionPoint.i18n.json @@ -13,6 +13,8 @@ "contributes.defaults.highContrast": "高對比佈景主題的預設色彩。應為十六進位 (#RRGGBB[AA]) 的色彩值,或提供預設的可設定佈景主題色彩。", "invalid.colorConfiguration": "'configuration.colors' 必須是陣列", "invalid.default.colorType": "{0} 必須是十六進位 (#RRGGBB[AA] or #RGB[A]) 的色彩值,或是提供預設的可設定佈景主題色彩之識別碼。", + "invalid.id": "'configuration.colors.id' 必須定義且不得為空白", "invalid.id.format": "'configuration.colors.id' 必須依照 word[.word]*", + "invalid.description": "'configuration.colors.description' 必須定義且不得為空白", "invalid.defaults": "'configuration.colors.defaults' 必須定義,且必須包含 'light'、'dark' 及 'highContrast'" } \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/browser/actions/workspaceActions.i18n.json b/i18n/cht/src/vs/workbench/browser/actions/workspaceActions.i18n.json index c90e6e584f7..de3c6d6122f 100644 --- a/i18n/cht/src/vs/workbench/browser/actions/workspaceActions.i18n.json +++ b/i18n/cht/src/vs/workbench/browser/actions/workspaceActions.i18n.json @@ -14,7 +14,6 @@ "selectWorkspace": "為工作區選取資料夾", "removeFolderFromWorkspace": "將資料夾從工作區移除", "saveWorkspaceAsAction": "另存工作區為...", - "saveEmptyWorkspaceNotSupported": "請先開啟工作區以進行儲存。", "save": "儲存(&&S)", "saveWorkspace": "儲存工作區", "openWorkspaceAction": "開啟工作區...", diff --git a/i18n/cht/src/vs/workbench/browser/parts/views/panelViewlet.i18n.json b/i18n/cht/src/vs/workbench/browser/parts/views/panelViewlet.i18n.json new file mode 100644 index 00000000000..6894078d8f2 --- /dev/null +++ b/i18n/cht/src/vs/workbench/browser/parts/views/panelViewlet.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "viewToolbarAriaLabel": "{0} 個動作" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json b/i18n/cht/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json index 072260a2e3b..4a4fd03e15a 100644 --- a/i18n/cht/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json +++ b/i18n/cht/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json @@ -51,9 +51,8 @@ "showLanguageExtensionsShort": "語言擴充功能", "showAzureExtensions": "顯示 Azure 延伸模組", "showAzureExtensionsShort": "Azure 延伸模組", - "configureWorkspaceRecommendedExtensions": "設定建議的延伸模組 (工作區)", - "ConfigureWorkspaceRecommendations.noWorkspace": "只有在工作區資料夾中才能使用建議。", "OpenExtensionsFile.failed": "無法在 '.vscode' 資料夾 ({0}) 中建立 'extensions.json' 檔案。", + "configureWorkspaceRecommendedExtensions": "設定建議的延伸模組 (工作區)", "builtin": "內建", "disableAll": "停用所有已安裝的延伸模組", "disableAllWorkspace": "停用此工作區的所有已安裝延伸模組", diff --git a/i18n/cht/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json b/i18n/cht/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json index f0f4cd2a6a4..21867210659 100644 --- a/i18n/cht/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json +++ b/i18n/cht/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json @@ -9,7 +9,6 @@ "openGlobalKeybindingsFile": "開啟鍵盤快速鍵檔案", "openWorkspaceSettings": "開啟工作區設定", "openFolderSettings": "開啟資料夾設定", - "pickFolder": "選取資料夾", "configureLanguageBasedSettings": "設定語言專屬設定...", "languageDescriptionConfigured": "({0})", "pickLanguage": "選取語言" diff --git a/i18n/cht/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json b/i18n/cht/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json index 2fbf166712a..6b3119f6f49 100644 --- a/i18n/cht/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json +++ b/i18n/cht/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json @@ -6,7 +6,5 @@ { "relaunchSettingMessage": "設定已經變更,必須重新啟動才會生效。", "relaunchSettingDetail": "請按 [重新啟動] 按鈕以重新啟動 {0} 並啟用設定。", - "restart": "重新啟動", - "relaunchWorkspaceMessage": "必須重新載入延伸模組系統才可變更此工作區。", - "reload": "重新載入" + "restart": "重新啟動" } \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json b/i18n/cht/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json index e43759a7db8..a279a7e25ba 100644 --- a/i18n/cht/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json +++ b/i18n/cht/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json @@ -5,13 +5,7 @@ // Do not edit this file. It is machine generated. { "tasksCategory": "工作", - "ConfigureTaskRunnerAction.noWorkspace": "這些工作只會出現在工作區資料夾中。", - "ConfigureTaskRunnerAction.quickPick.template": "選取工作執行器", - "ConfigureTaskRunnerAction.autoDetecting": "自動偵測 {0} 的工作", - "ConfigureTaskRunnerAction.autoDetect": "自動偵測工作系統失敗。正在使用預設範本。如需詳細資料,請參閱工作輸出。", - "ConfigureTaskRunnerAction.autoDetectError": "自動偵測工作系統產生的錯誤。如需詳細資料,請查看工作輸出。", - "ConfigureTaskRunnerAction.failed": "無法在 '.vscode' 資料夾中建立 'tasks.json' 檔案。如需詳細資訊,請參閱工作輸出。", - "ConfigureTaskRunnerAction.label": "設定工作執行器", + "ConfigureTaskRunnerAction.label": "設定工作", "ConfigureBuildTaskAction.label": "設定建置工作", "CloseMessageAction.label": "關閉", "ShowTerminalAction.label": "檢視終端機", @@ -47,22 +41,16 @@ "configured": "設定的工作", "detected": "偵測到的工作", "TaskService.fetchingBuildTasks": "正在擷取組建工作...", - "TaskService.noBuildTaskTerminal": "找不到任何組建工作。請按一下 [設定組建工作] 以加以定義。", "TaskService.pickBuildTask": "請選取要執行的組建工作", "TaskService.fetchingTestTasks": "正在擷取測試工作...", - "TaskService.noTestTaskTerminal": "找不到任何測試工作。請按一下 [設定測試執行器] 以加以定義。", "TaskService.pickTestTask": "請選取要執行的測試工作", - "TaskService.noTaskRunning": "目前沒有執行中的工作。", "TaskService.tastToTerminate": "請選取要終止的工作", "TerminateAction.noProcess": "啟動的處理序已不存在。如果工作繁衍的背景工作結束,VS Code 可能會產生孤立的處理序。", "TerminateAction.failed": "無法終止執行中的工作", - "TaskService.noTaskToRestart": "沒有任何要重新啟動的工作。", "TaskService.tastToRestart": "請選取要重新啟動的工作", - "TaskService.defaultBuildTaskExists": "已經將 {0} 標記為預設組建工作。", "TaskService.pickDefaultBuildTask": "請選取要用作預設組建工作的工作", "TaskService.defaultTestTaskExists": "已經將 {0} 標記為預設測試工作。", "TaskService.pickDefaultTestTask": "請選取要用作預設測試工作的工作", - "TaskService.noTaskIsRunning": "沒有執行中的工作。", "TaskService.pickShowTask": "選取要顯示輸出的工作", "ShowLogAction.label": "顯示工作記錄檔", "RunTaskAction.label": "執行工作", diff --git a/i18n/cht/src/vs/workbench/services/configuration/node/configuration.i18n.json b/i18n/cht/src/vs/workbench/services/configuration/node/configuration.i18n.json index 53d51ad9b47..b9826aee605 100644 --- a/i18n/cht/src/vs/workbench/services/configuration/node/configuration.i18n.json +++ b/i18n/cht/src/vs/workbench/services/configuration/node/configuration.i18n.json @@ -13,7 +13,6 @@ "invalid.title": "'configuration.title' 必須是字串", "vscode.extension.contributes.defaultConfiguration": "依語言貢獻預設編輯器組態設定。", "invalid.properties": "'configuration.properties' 必須是物件", - "workspaceConfig.folders.description": "要在工作區中載入之資料夾的清單。必須是檔案路徑,例如 `/root/folderA` 或 `./folderA` 即為會對工作區檔案位置解析的相關路徑。", - "workspaceConfig.folder.description": "檔案路徑,例如 `/root/folderA` 或 `./folderA` 即為會對工作區檔案位置解析的相關路徑。", + "workspaceConfig.path.description": "檔案路徑,例如 `/root/folderA` 或 `./folderA` 即為會對工作區檔案位置解析的相關路徑。", "workspaceConfig.settings.description": "工作區設定" } \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/services/files/electron-browser/remoteFileService.i18n.json b/i18n/cht/src/vs/workbench/services/files/electron-browser/remoteFileService.i18n.json new file mode 100644 index 00000000000..067f2923a48 --- /dev/null +++ b/i18n/cht/src/vs/workbench/services/files/electron-browser/remoteFileService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileBinaryError": "檔案似乎是二進位檔,因此無法當做文字開啟" +} \ No newline at end of file diff --git a/i18n/deu/extensions/azure-account/out/azure-account.i18n.json b/i18n/deu/extensions/azure-account/out/azure-account.i18n.json index 888fa9b955d..e3df1915bad 100644 --- a/i18n/deu/extensions/azure-account/out/azure-account.i18n.json +++ b/i18n/deu/extensions/azure-account/out/azure-account.i18n.json @@ -6,6 +6,7 @@ { "azure-account.copyAndOpen": "Kopieren & Öffnen", "azure-account.close": "Schließen", + "azure-account.login": "Einloggen", "azure-account.loginFirst": "Nicht angemeldet, bitte zuerst anmelden.", "azure-account.userCodeFailed": "Fehler beim Erfassen von Benutzercode.", "azure-account.tokenFailed": "Token wird mit Gerätecode erfasst", diff --git a/i18n/deu/src/vs/code/electron-main/menus.i18n.json b/i18n/deu/src/vs/code/electron-main/menus.i18n.json index 790e6e94cd1..49cfdddcd5d 100644 --- a/i18n/deu/src/vs/code/electron-main/menus.i18n.json +++ b/i18n/deu/src/vs/code/electron-main/menus.i18n.json @@ -175,8 +175,6 @@ "miRunningTask": "Aktive Auf&&gaben anzeigen...", "miRestartTask": "Aktuell&&e Aufgabe neu starten...", "miTerminateTask": "&&Aufgabe beenden...", - "miConfigureTask": "Aufgaben &&konfigurieren", - "miConfigureBuildTask": "Standardbuildaufgabe kon&&figurieren", "accessibilityOptionsWindowTitle": "Optionen für erleichterte Bedienung", "miRestartToUpdate": "Zum Aktualisieren neu starten...", "miCheckingForUpdates": "Überprüfen auf Updates...", diff --git a/i18n/deu/src/vs/editor/common/config/commonEditorConfig.i18n.json b/i18n/deu/src/vs/editor/common/config/commonEditorConfig.i18n.json index 17755d6a385..0afb44a967e 100644 --- a/i18n/deu/src/vs/editor/common/config/commonEditorConfig.i18n.json +++ b/i18n/deu/src/vs/editor/common/config/commonEditorConfig.i18n.json @@ -88,6 +88,7 @@ "accessibilitySupport": "Steuert, ob der Editor in einem Modus ausgeführt werden soll, in dem er für die Sprachausgabe optimiert wird.", "links": "Steuert, ob der Editor Links erkennen und anklickbar machen soll", "colorDecorators": "Steuert, ob der Editor die Inline-Farbdecorators und die Farbauswahl rendern soll.", + "codeActions": "Ermöglicht die Code-Aktion \"lightbulb\"", "sideBySide": "Steuert, ob der Diff-Editor das Diff nebeneinander oder inline anzeigt.", "ignoreTrimWhitespace": "Steuert, ob der Diff-Editor Änderungen in führenden oder nachgestellten Leerzeichen als Diffs anzeigt.", "renderIndicators": "Steuert, ob der Diff-Editor die Indikatoren \"+\" und \"-\" für hinzugefügte/entfernte Änderungen anzeigt.", diff --git a/i18n/deu/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json b/i18n/deu/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json index cd123a52668..c43709d2e6c 100644 --- a/i18n/deu/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json +++ b/i18n/deu/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json @@ -5,10 +5,7 @@ // Do not edit this file. It is machine generated. { "invalidManifest": "Die Erweiterung ist ungültig: \"package.json\" ist keine JSON-Datei.", - "restartCode": "Bitte starten Sie Code vor der Neuinstallation von {0} neu.", - "installDependeciesConfirmation": "Durch das Installieren von \"{0}\" werden auch die abhängigen Komponenten installiert. Möchten Sie den Vorgang fortsetzen?", - "install": "Ja", - "doNotInstall": "Nein", + "restartCodeLocal": "Bitte starten Sie Code vor der Neuinstallation von {0} neu.", "uninstallDependeciesConfirmation": "Möchten Sie nur \"{0}\" oder auch die zugehörigen Abhängigkeiten deinstallieren?", "uninstallOnly": "Nur", "uninstallAll": "Alle", diff --git a/i18n/deu/src/vs/workbench/browser/actions/workspaceActions.i18n.json b/i18n/deu/src/vs/workbench/browser/actions/workspaceActions.i18n.json index 4529b938efc..13a7e15f206 100644 --- a/i18n/deu/src/vs/workbench/browser/actions/workspaceActions.i18n.json +++ b/i18n/deu/src/vs/workbench/browser/actions/workspaceActions.i18n.json @@ -14,7 +14,6 @@ "selectWorkspace": "Ordner für den Arbeitsbereich auswählen", "removeFolderFromWorkspace": "Ordner aus dem Arbeitsbereich entfernen", "saveWorkspaceAsAction": "Arbeitsbereich speichern unter...", - "saveEmptyWorkspaceNotSupported": "Öffnen Sie zum Speichern zunächst einen Arbeitsbereich.", "save": "&&Speichern", "saveWorkspace": "Arbeitsbereich speichern", "openWorkspaceAction": "Arbeitsbereich öffnen...", diff --git a/i18n/deu/src/vs/workbench/browser/parts/views/panelViewlet.i18n.json b/i18n/deu/src/vs/workbench/browser/parts/views/panelViewlet.i18n.json new file mode 100644 index 00000000000..b7eb39e941e --- /dev/null +++ b/i18n/deu/src/vs/workbench/browser/parts/views/panelViewlet.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "viewToolbarAriaLabel": "{0}-Aktionen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json b/i18n/deu/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json index e6677c7d0b8..9285fd4e2a9 100644 --- a/i18n/deu/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json +++ b/i18n/deu/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json @@ -51,9 +51,8 @@ "showLanguageExtensionsShort": "Spracherweiterungen", "showAzureExtensions": "Azure-Erweiterungen anzeigen", "showAzureExtensionsShort": "Azure-Erweiterungen", - "configureWorkspaceRecommendedExtensions": "Empfohlene Erweiterungen konfigurieren (Arbeitsbereich)", - "ConfigureWorkspaceRecommendations.noWorkspace": "Empfehlungen sind nur für einen Arbeitsbereichsordner verfügbar.", "OpenExtensionsFile.failed": "Die Datei \"extensions.json\" kann nicht im Ordner \".vscode\" erstellt werden ({0}).", + "configureWorkspaceRecommendedExtensions": "Empfohlene Erweiterungen konfigurieren (Arbeitsbereich)", "builtin": "Integriert", "disableAll": "Alle installierten Erweiterungen löschen", "disableAllWorkspace": "Alle installierten Erweiterungen für diesen Arbeitsbereich deaktivieren", diff --git a/i18n/deu/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json b/i18n/deu/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json index 73113cf6294..503c8aa631f 100644 --- a/i18n/deu/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json +++ b/i18n/deu/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json @@ -9,7 +9,6 @@ "openGlobalKeybindingsFile": "Datei mit Tastaturkurzbefehlen öffnen", "openWorkspaceSettings": "Arbeitsbereichseinstellungen öffnen", "openFolderSettings": "Ordnereinstellungen öffnen", - "pickFolder": "Ordner auswählen", "configureLanguageBasedSettings": "Sprachspezifische Einstellungen konfigurieren...", "languageDescriptionConfigured": "({0})", "pickLanguage": "Sprache auswählen" diff --git a/i18n/deu/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json b/i18n/deu/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json index 4e537daf3e5..b3f84ac1c6e 100644 --- a/i18n/deu/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json +++ b/i18n/deu/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json @@ -6,7 +6,5 @@ { "relaunchSettingMessage": "Eine Einstellung wurde geändert, welche einen Neustart benötigt.", "relaunchSettingDetail": "Drücke den Neu starten-Button, um {0} neuzustarten und die Einstellung zu aktivieren.", - "restart": "Neu starten", - "relaunchWorkspaceMessage": "Dieser Arbeitsbereich erfordert das erneute Laden des Erweiterungssystems.", - "reload": "Neu starten" + "restart": "Neu starten" } \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json b/i18n/deu/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json index d5bb26dd92c..26d75ccd2bf 100644 --- a/i18n/deu/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json +++ b/i18n/deu/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json @@ -5,13 +5,7 @@ // Do not edit this file. It is machine generated. { "tasksCategory": "Aufgaben", - "ConfigureTaskRunnerAction.noWorkspace": "Aufgaben sind nur für einen Arbeitsbereichsordner verfügbar.", - "ConfigureTaskRunnerAction.quickPick.template": "Taskausführung auswählen", - "ConfigureTaskRunnerAction.autoDetecting": "Tasks für {0} werden automatisch erkannt.", - "ConfigureTaskRunnerAction.autoDetect": "Fehler bei der automatischen Erkennung des Tasksystems. Die Standardvorlage wird verwendet. Einzelheiten finden Sie in der Taskausgabe.", - "ConfigureTaskRunnerAction.autoDetectError": "Bei der automatischen Erkennung des Tasksystems sind Fehler aufgetreten. Einzelheiten finden Sie in der Taskausgabe.", - "ConfigureTaskRunnerAction.failed": "Die Datei \"tasks.json\" kann nicht im Ordner \".vscode\" erstellt werden. Einzelheiten finden Sie in der Taskausgabe.", - "ConfigureTaskRunnerAction.label": "Taskausführung konfigurieren", + "ConfigureTaskRunnerAction.label": "Aufgabe konfigurieren", "ConfigureBuildTaskAction.label": "Buildtask konfigurieren", "CloseMessageAction.label": "Schließen", "ShowTerminalAction.label": "Terminal anzeigen", @@ -47,22 +41,16 @@ "configured": "konfigurierte Aufgaben", "detected": "erkannte Aufgaben", "TaskService.fetchingBuildTasks": "Buildaufgaben werden abgerufen...", - "TaskService.noBuildTaskTerminal": "Es wurde keine Buildaufgabe gefunden. Klicken Sie auf \"Buildtask konfigurieren\", um eine Aufgabe zu definieren.", "TaskService.pickBuildTask": "Auszuführende Buildaufgabe auswählen", "TaskService.fetchingTestTasks": "Testaufgaben werden abgerufen...", - "TaskService.noTestTaskTerminal": "Es wurde keine Testaufgabe gefunden. Klicken Sie auf \"Taskausführung konfigurieren\", um eine Aufgabe zu definieren.", "TaskService.pickTestTask": "Auszuführende Testaufgabe auswählen", - "TaskService.noTaskRunning": "Zurzeit wird keine Aufgabe ausgeführt.", "TaskService.tastToTerminate": "Zu beendende Aufgabe auswählen", "TerminateAction.noProcess": "Der gestartete Prozess ist nicht mehr vorhanden. Wenn der Task Hintergrundtasks erzeugt hat, kann das Beenden von VS Code ggf. zu verwaisten Prozessen führen.", "TerminateAction.failed": "Fehler beim Beenden des ausgeführten Tasks.", - "TaskService.noTaskToRestart": "Es ist keine neu zu startende Aufgabe vorhanden.", "TaskService.tastToRestart": "Neu zu startende Aufgabe auswählen", - "TaskService.defaultBuildTaskExists": "{0} ist bereits als Standardbuildaufgabe markiert.", "TaskService.pickDefaultBuildTask": "Als Standardbuildaufgabe zu verwendende Aufgabe auswählen", "TaskService.defaultTestTaskExists": "{0} ist bereits als Standardtestaufgabe markiert.", "TaskService.pickDefaultTestTask": "Als Standardtestaufgabe zu verwendende Aufgabe auswählen", - "TaskService.noTaskIsRunning": "Es wird keine Aufgabe ausgeführt.", "TaskService.pickShowTask": "Aufgabe zum Anzeigen der Ausgabe auswählen", "ShowLogAction.label": "Taskprotokoll anzeigen", "RunTaskAction.label": "Task ausführen", diff --git a/i18n/deu/src/vs/workbench/services/configuration/node/configuration.i18n.json b/i18n/deu/src/vs/workbench/services/configuration/node/configuration.i18n.json index 66444d36312..d135d2e6051 100644 --- a/i18n/deu/src/vs/workbench/services/configuration/node/configuration.i18n.json +++ b/i18n/deu/src/vs/workbench/services/configuration/node/configuration.i18n.json @@ -13,7 +13,6 @@ "invalid.title": "configuration.title muss eine Zeichenfolge sein.", "vscode.extension.contributes.defaultConfiguration": "Trägt zu Konfigurationeinstellungen des Standard-Editors für die jeweilige Sprache bei.", "invalid.properties": "\"configuration.properties\" muss ein Objekt sein.", - "workspaceConfig.folders.description": "Liste von Ordnern, die in den Arbeitsbereich geladen werden. Hierbei muss es sich um einen Dateipfad handeln, z. B. \" /root/folderA\" oder \"./folderA\" bei einem relativen Pfad, der in Bezug auf den Speicherort der Arbeitsbereichsdatei aufgelöst wird.", - "workspaceConfig.folder.description": "Ein Dateipfad, z. B. \" /root/folderA\" oder \"./folderA\" bei einem relativen Pfad, der in Bezug auf den Speicherort der Arbeitsbereichsdatei aufgelöst wird.", + "workspaceConfig.path.description": "Ein Dateipfad, z. B. \" /root/folderA\" oder \"./folderA\" bei einem relativen Pfad, der in Bezug auf den Speicherort der Arbeitsbereichsdatei aufgelöst wird.", "workspaceConfig.settings.description": "Arbeitsbereichseinstellungen" } \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/services/files/electron-browser/remoteFileService.i18n.json b/i18n/deu/src/vs/workbench/services/files/electron-browser/remoteFileService.i18n.json new file mode 100644 index 00000000000..ed6cda9de2a --- /dev/null +++ b/i18n/deu/src/vs/workbench/services/files/electron-browser/remoteFileService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileBinaryError": "Die Datei scheint eine Binärdatei zu sein und kann nicht als Text geöffnet werden." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/code/electron-main/menus.i18n.json b/i18n/esn/src/vs/code/electron-main/menus.i18n.json index 01a9a2bd0ce..484face7717 100644 --- a/i18n/esn/src/vs/code/electron-main/menus.i18n.json +++ b/i18n/esn/src/vs/code/electron-main/menus.i18n.json @@ -175,8 +175,8 @@ "miRunningTask": "Mostrar las &&tareas en ejecución", "miRestartTask": "R&&einiciar tarea en ejecución...", "miTerminateTask": "&&Finalizar tarea...", - "miConfigureTask": "&&Configurar tarea", - "miConfigureBuildTask": "Configurar tarea de compilación predeterminada", + "miConfigureTask": "&&Configurar Tareas...", + "miConfigureBuildTask": "Configurar Tarea de Compilación &&Predeterminada...", "accessibilityOptionsWindowTitle": "Opciones de accesibilidad", "miRestartToUpdate": "Reiniciar para actualizar...", "miCheckingForUpdates": "Buscando actualizaciones...", diff --git a/i18n/esn/src/vs/code/electron-main/windows.i18n.json b/i18n/esn/src/vs/code/electron-main/windows.i18n.json index 228ae40964f..0cc1e6ac83a 100644 --- a/i18n/esn/src/vs/code/electron-main/windows.i18n.json +++ b/i18n/esn/src/vs/code/electron-main/windows.i18n.json @@ -18,6 +18,7 @@ "openFolder": "Abrir carpeta", "openFile": "Abrir archivo", "workspaceOpenedMessage": "No se puede guardar el espacio de trabajo '{0}'", + "workspaceOpenedDetail": "El espacio de trabajo ya está abierto en otra ventana. Por favor, cierre primero la ventana y vuelta a intentarlo de nuevo.", "openWorkspace": "&&Abrir...", "openWorkspaceTitle": "Abrir área de trabajo", "save": "&&Guardar", diff --git a/i18n/esn/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json b/i18n/esn/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json index 253f50641c3..c9693e97782 100644 --- a/i18n/esn/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json +++ b/i18n/esn/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json @@ -13,6 +13,7 @@ "vscode.extension.contributes.menuItem.group": "Grupo al que pertenece este comando", "vscode.extension.contributes.menus": "Contribuye con elementos de menú al editor", "menus.commandPalette": "La paleta de comandos", + "menus.touchBar": "Barra táctil (sólo macOS)", "menus.editorTitle": "El menú de título del editor", "menus.editorContext": "El menú contextual del editor", "menus.explorerContext": "El menú contextual del explorador de archivos", diff --git a/i18n/esn/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json b/i18n/esn/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json index 89b2efaccfe..d12f2822486 100644 --- a/i18n/esn/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json +++ b/i18n/esn/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json @@ -5,10 +5,8 @@ // Do not edit this file. It is machine generated. { "invalidManifest": "Extensión no válida: package.json no es un archivo JSON.", - "restartCode": "Reinicie Code antes de volver a instalar {0}.", - "installDependeciesConfirmation": "Al instalar '{0}', se instalan también sus dependencias. ¿Quiere continuar?", - "install": "Sí", - "doNotInstall": "No", + "restartCodeLocal": "Reinicie Code antes de volver a instalar {0}.", + "restartCodeGallery": "Por favor reinicie Code antes de reinstalar.", "uninstallDependeciesConfirmation": "¿Quiere desinstalar solo '{0}' o también sus dependencias?", "uninstallOnly": "Solo", "uninstallAll": "Todo", diff --git a/i18n/esn/src/vs/workbench/browser/actions/workspaceActions.i18n.json b/i18n/esn/src/vs/workbench/browser/actions/workspaceActions.i18n.json index 9e8733c7faf..81bfd015f9e 100644 --- a/i18n/esn/src/vs/workbench/browser/actions/workspaceActions.i18n.json +++ b/i18n/esn/src/vs/workbench/browser/actions/workspaceActions.i18n.json @@ -14,9 +14,9 @@ "selectWorkspace": "Seleccionar carpetas para el área de trabajo", "removeFolderFromWorkspace": "Quitar carpeta del área de trabajo", "saveWorkspaceAsAction": "Guardar área de trabajo como...", - "saveEmptyWorkspaceNotSupported": "Abra un área de trabajo antes de guardar.", "save": "&&Guardar", "saveWorkspace": "Guardar área de trabajo", "openWorkspaceAction": "Abrir área de trabajo...", - "openWorkspaceConfigFile": "Abrir archivo de configuración del área de trabajo" + "openWorkspaceConfigFile": "Abrir archivo de configuración del área de trabajo", + "workspaceFolderPickerPlaceholder": "Seleccionar la carpeta del área de trabajo" } \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/browser/parts/editor/editorActions.i18n.json b/i18n/esn/src/vs/workbench/browser/parts/editor/editorActions.i18n.json index e50bfab447c..5f8d21bad14 100644 --- a/i18n/esn/src/vs/workbench/browser/parts/editor/editorActions.i18n.json +++ b/i18n/esn/src/vs/workbench/browser/parts/editor/editorActions.i18n.json @@ -35,6 +35,7 @@ "openPreviousEditorInGroup": "Abrir el editor anterior en el grupo", "navigateNext": "Hacia delante", "navigatePrevious": "Hacia atrás", + "navigateLast": "Vaya al último", "reopenClosedEditor": "Volver a abrir el editor cerrado", "clearRecentFiles": "Borrar abiertos recientemente", "showEditorsInFirstGroup": "Mostrar editores del primer grupo", diff --git a/i18n/esn/src/vs/workbench/browser/parts/views/panelViewlet.i18n.json b/i18n/esn/src/vs/workbench/browser/parts/views/panelViewlet.i18n.json new file mode 100644 index 00000000000..62933f6b509 --- /dev/null +++ b/i18n/esn/src/vs/workbench/browser/parts/views/panelViewlet.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "viewToolbarAriaLabel": "{0} acciones" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/electron-browser/main.contribution.i18n.json b/i18n/esn/src/vs/workbench/electron-browser/main.contribution.i18n.json index a142099af46..bc271ff5ade 100644 --- a/i18n/esn/src/vs/workbench/electron-browser/main.contribution.i18n.json +++ b/i18n/esn/src/vs/workbench/electron-browser/main.contribution.i18n.json @@ -10,8 +10,11 @@ "workspaces": "Áreas de trabajo", "developer": "Desarrollador", "showEditorTabs": "Controla si los editores abiertos se deben mostrar o no en pestañas.", + "workbench.editor.labelFormat.default": "Mostrar el nombre del archivo. Cuando las pestañas están habilitadas y dos archivos tienen el mismo nombre en un grupo, se agregan las secciones distintivas de la ruta de acceso de cada archivo. Cuando las pestañas están deshabilitadas, se muestra la ruta de acceso relativa a la raíz del espacio de trabajo si el editor está activo.", "workbench.editor.labelFormat.short": "Mostrar el nombre del archivo seguido de su nombre de directorio.", "workbench.editor.labelFormat.medium": "Mostrar el nombre del archivo seguido de la ruta de acceso relativo a la raíz del espacio de trabajo.", + "workbench.editor.labelFormat.long": "Mostrar el nombre del archivo seguido de la ruta de acceso absoluta.", + "tabDescription": "Controla el formato de la etiqueta para un editor. Modificar este ajuste puede hacer, por ejemplo, que sea más fácil entender la ubicación de un archivo: - corta: 'parent' - media: 'workspace/src/parent' - larga: '/home/user/workspace/src/parect' - por defecto: '.../parent', cuando otra pestaña comparte el mismo título, o la ruta de acceso relativa del espacio de trabajo si las pestañas están deshabilitadas", "editorTabCloseButton": "Controla la posición de los botones de cierre de pestañas del editor o los deshabilita si se establece en \"off\".", "showIcons": "Controla si los editores abiertos deben mostrarse o no con un icono. Requiere que también se habilite un tema de icono.", "enablePreview": "Controla si los editores abiertos se muestran en vista previa. Los editores en vista previa se reutilizan hasta que se guardan (por ejemplo, mediante doble clic o editándolos).", diff --git a/i18n/esn/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json b/i18n/esn/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json index 42304193db2..110e697ca94 100644 --- a/i18n/esn/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json +++ b/i18n/esn/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json @@ -36,5 +36,7 @@ "schema.indentationRules.unIndentedLinePattern": "Si una línea coincide con este patrón, su sangría no se debe cambiar y no se debe evaluar utilizando las otras reglas.", "schema.indentationRules.unIndentedLinePattern.pattern": "El patrón de RegExp para unIndentedLinePattern.", "schema.indentationRules.unIndentedLinePattern.flags": "Las marcas de RegExp para unIndentedLinePattern.", - "schema.indentationRules.unIndentedLinePattern.errorMessage": "Debe coincidir con el patrón `/^([gimuy]+)$/`." + "schema.indentationRules.unIndentedLinePattern.errorMessage": "Debe coincidir con el patrón `/^([gimuy]+)$/`.", + "schema.folding": "Configuración del plegamiento de idioma.", + "schema.folding.offSide": "Un idioma se adhiere a la regla del fuera de juego si los bloques en ese idioma se expresan por su sangría. Si se establece, las líneas vacías pertenecen al bloque posterior." } \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json b/i18n/esn/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json index 25236ca4769..f2fa7ffeb2a 100644 --- a/i18n/esn/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json +++ b/i18n/esn/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json @@ -51,9 +51,9 @@ "showLanguageExtensionsShort": "Extensiones del lenguaje", "showAzureExtensions": "Mostrar extensiones de Azure", "showAzureExtensionsShort": "Extensiones de Azure", - "configureWorkspaceRecommendedExtensions": "Configurar extensiones recomendadas (área de trabajo)", - "ConfigureWorkspaceRecommendations.noWorkspace": "Las recomendaciones solo están disponibles en una carpeta de área de trabajo.", "OpenExtensionsFile.failed": "No se puede crear el archivo \"extensions.json\" dentro de la carpeta \".vscode\" ({0}).", + "configureWorkspaceRecommendedExtensions": "Configurar extensiones recomendadas (área de trabajo)", + "configureWorkspaceFolderRecommendedExtensions": "Configurar extensiones recomendadas (Carpeta del área de trabajo)", "builtin": "Integrada", "disableAll": "Deshabilitar todas las extensiones instaladas", "disableAllWorkspace": "Deshabilitar todas las extensiones instaladas para esta área de trabajo", diff --git a/i18n/esn/src/vs/workbench/parts/files/browser/files.contribution.i18n.json b/i18n/esn/src/vs/workbench/parts/files/browser/files.contribution.i18n.json index 06f719f73f0..b07ed05e826 100644 --- a/i18n/esn/src/vs/workbench/parts/files/browser/files.contribution.i18n.json +++ b/i18n/esn/src/vs/workbench/parts/files/browser/files.contribution.i18n.json @@ -14,6 +14,8 @@ "files.exclude.boolean": "El patrón global con el que se harán coincidir las rutas de acceso de los archivos. Establézcalo en true o false para habilitarlo o deshabilitarlo.", "files.exclude.when": "Comprobación adicional de los elementos del mismo nivel de un archivo coincidente. Use $(nombreBase) como variable para el nombre de archivo que coincide.", "associations": "Configure asociaciones de archivo para los lenguajes (por ejemplo, \"*.extension\": \"html\"). Estas asociaciones tienen prioridad sobre las asociaciones predeterminadas de los lenguajes instalados.", + "encoding": "La codificación del juego de caracteres predeterminada que debe utilizarse al leer y escribir archivos. Este ajuste puede configurarse también por idioma.", + "autoGuessEncoding": "Cuando está activada, intentará adivinar la codificación del juego de caracteres al abrir archivos. Este ajuste puede configurarse también por idioma.", "eol": "Carácter predeterminado de final de línea. Utilice \\n para LF y \\r\\n para CRLF.", "trimTrailingWhitespace": "Si se habilita, se recortará el espacio final cuando se guarde un archivo.", "insertFinalNewline": "Si se habilita, inserte una nueva línea final al final del archivo cuando lo guarde.", diff --git a/i18n/esn/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json b/i18n/esn/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json index 2bfe512b1d5..402cc5d581e 100644 --- a/i18n/esn/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json +++ b/i18n/esn/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json @@ -9,7 +9,6 @@ "openGlobalKeybindingsFile": "Abrir el archivo de métodos abreviados de teclado", "openWorkspaceSettings": "Abrir configuración del área de trabajo", "openFolderSettings": "Abrir Configuración de carpeta", - "pickFolder": "Seleccionar carpeta", "configureLanguageBasedSettings": "Configurar opciones específicas del lenguaje...", "languageDescriptionConfigured": "({0})", "pickLanguage": "Seleccionar lenguaje" diff --git a/i18n/esn/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json b/i18n/esn/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json index a447e55f2de..417500fd04a 100644 --- a/i18n/esn/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json +++ b/i18n/esn/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json @@ -6,7 +6,5 @@ { "relaunchSettingMessage": "Ha cambiado un ajuste que requiere un reinicio para ser efectivo.", "relaunchSettingDetail": "Pulse el botón de reinicio para reiniciar {0} y habilitar el ajuste.", - "restart": "Reiniciar", - "relaunchWorkspaceMessage": "Este cambio de área de trabajo requiere recargar nuestro sistema de extensiones.", - "reload": "Recargar" + "restart": "Reiniciar" } \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json b/i18n/esn/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json index 38790781315..5c65b30517e 100644 --- a/i18n/esn/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json +++ b/i18n/esn/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { + "scm providers": "Proveedores de Control de Código fuente", "commitMessage": "Message (press {0} to commit)", "installAdditionalSCMProviders": "Instalar proveedores adicionales de SCM...", "no open repo": "No hay proveedores de control de código fuente activos.", diff --git a/i18n/esn/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json b/i18n/esn/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json index 300ec37121d..6e97b49f6db 100644 --- a/i18n/esn/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json +++ b/i18n/esn/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json @@ -5,13 +5,7 @@ // Do not edit this file. It is machine generated. { "tasksCategory": "Tareas", - "ConfigureTaskRunnerAction.noWorkspace": "Las tareas solo están disponibles en una carpeta del área de trabajo.", - "ConfigureTaskRunnerAction.quickPick.template": "Seleccionar un ejecutador de tareas", - "ConfigureTaskRunnerAction.autoDetecting": "Detectando tareas automáticamente para {0}", - "ConfigureTaskRunnerAction.autoDetect": "Error de detección automática del sistema de tareas. Se usa la plantilla predeterminada. Consulte el resultado de la tarea para obtener más detalles", - "ConfigureTaskRunnerAction.autoDetectError": "Error de detección automática del sistema de tareas. Consulte el resultado de la tarea para obtener más detalles", - "ConfigureTaskRunnerAction.failed": "No se puede crear el archivo \"tasks.json\" dentro de la carpeta \".vscode\". Consulte el resultado de la tarea para obtener más detalles.", - "ConfigureTaskRunnerAction.label": "Configurar ejecutor de tareas", + "ConfigureTaskRunnerAction.label": "Configurar tarea", "ConfigureBuildTaskAction.label": "Configurar tarea de compilación", "CloseMessageAction.label": "Cerrar", "ShowTerminalAction.label": "Ver terminal", @@ -19,11 +13,13 @@ "manyMarkers": "Más de 99", "runningTasks": "Mostrar tareas en ejecución", "tasks": "Tareas", + "TaskSystem.noHotSwap": "Cambiar el motor de ejecución de tareas con una tarea activa ejecutandose, requiere recargar la ventana", "TaskService.noBuildTask1": "No se ha definido ninguna tarea de compilación. Marque una tarea con \"isBuildCommand\" en el archivo tasks.json.", "TaskService.noBuildTask2": "No se ha definido ninguna tarea de compilación. Marque una tarea con un grupo \"build\" en el archivo tasks.json. ", "TaskService.noTestTask1": "No se ha definido ninguna tarea de prueba. Marque una tarea con \"isTestCommand\" en el archivo tasks.json.", "TaskService.noTestTask2": "No se ha definido ninguna tarea de prueba. Marque una tarea con \"test\" en el archivo tasks.json.", "TaskServer.noTask": "No se encuentra la tarea {0} que se ha solicitado para ejecutarla.", + "TaskService.associate": "asociar", "TaskService.attachProblemMatcher.continueWithout": "Continuar sin examinar la salida de la tarea", "TaskService.attachProblemMatcher.never": "No examinar nunca la salida de la tarea", "TaskService.attachProblemMatcher.learnMoreAbout": "Más información acerca del examen de la salida de la tarea", @@ -35,6 +31,7 @@ "TaskSystem.active": "Ya hay una tarea en ejecución. Finalícela antes de ejecutar otra tarea.", "TaskSystem.restartFailed": "No se pudo terminar y reiniciar la tarea {0}", "TaskSystem.configurationErrors": "Error: La configuración de la tarea proporcionada tiene errores de validación y no se puede usar. Corrija los errores primero.", + "taskService.ignoreingFolder": "Ignorando las configuraciones de tareas para la carpeta del área de trabajo {0}. El soporte de carpetas multi-raíz requiere que todas las carpetas usen la versión 2.0.0 de las tareas\n", "TaskSystem.invalidTaskJson": "Error: El contenido del archivo tasks.json tiene errores de sintaxis. Corríjalos antes de ejecutar una tarea.", "TaskSystem.runningTask": "Hay una tarea en ejecución. ¿Quiere finalizarla?", "TaskSystem.terminateTask": "&&Finalizar tarea", @@ -46,24 +43,30 @@ "recentlyUsed": "Tareas usadas recientemente", "configured": "tareas configuradas", "detected": "tareas detectadas", + "TaskService.pickRunTask": "Seleccione la tarea a ejecutar", + "TaslService.noEntryToRun": "No se encontraron tareas para ejecutar. Configurar tareas...", "TaskService.fetchingBuildTasks": "Obteniendo tareas de compilación...", - "TaskService.noBuildTaskTerminal": "No se encontraron Tareas de Compilación. Pulse 'Configurar Tarea de Compilación' para definir una.", "TaskService.pickBuildTask": "Seleccione la tarea de compilación para ejecutar", + "TaskService.noBuildTask": "No se encontraron tareas de compilación para ejecutar. Configurar tareas...", "TaskService.fetchingTestTasks": "Capturando tareas de prueba...", - "TaskService.noTestTaskTerminal": "No se encontraron tareas de prueba. Presione \"Configurar ejecutor de tareas\" para definir una.", "TaskService.pickTestTask": "Seleccione la tarea de prueba para ejecutar", - "TaskService.noTaskRunning": "Ninguna tarea se está ejecutando actualmente.", + "TaskService.noTestTaskTerminal": "No se encontraron tareas de prueba para ejecutar. Configurar tareas...", "TaskService.tastToTerminate": "Seleccione la tarea para finalizar", + "TaskService.noTaskRunning": "Ninguna tarea se está ejecutando actualmente", "TerminateAction.noProcess": "El proceso iniciado ya no existe. Si la tarea generó procesos en segundo plano al salir de VS Code, puede dar lugar a procesos huérfanos.", "TerminateAction.failed": "No se pudo finalizar la tarea en ejecución", - "TaskService.noTaskToRestart": "No hay tareas para reiniciar.", "TaskService.tastToRestart": "Seleccione la tarea para reiniciar", - "TaskService.defaultBuildTaskExists": "{0} ya se ha marcado como la tarea de compilación predeterminada.", + "TaskService.noTaskToRestart": "No hay tareas para reiniciar", + "TaskService.template": "Seleccione una plantilla de tarea", + "TaskService.createJsonFile": "Crear archivo tasks.json desde plantilla", + "TaskService.openJsonFile": "Abrir archivo tasks.json", + "TaskService.pickTask": "Seleccione una tarea para configurar", + "TaskService.defaultBuildTaskExists": "{0} está marcado ya como la tarea de compilación predeterminada", "TaskService.pickDefaultBuildTask": "Seleccione la tarea que se va a utilizar como tarea de compilación predeterminada", "TaskService.defaultTestTaskExists": "{0} ya se ha marcado como la tarea de prueba predeterminada.", "TaskService.pickDefaultTestTask": "Seleccione la tarea que se va a usar como la tarea de prueba predeterminada ", - "TaskService.noTaskIsRunning": "No hay ninguna tarea en ejecución.", "TaskService.pickShowTask": "Seleccione la tarea de la que desea ver la salida", + "TaskService.noTaskIsRunning": "Ninguna tarea se está ejecutando", "ShowLogAction.label": "Mostrar registro de tareas", "RunTaskAction.label": "Ejecutar tarea", "RestartTaskAction.label": "Reiniciar la tarea en ejecución", diff --git a/i18n/esn/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json b/i18n/esn/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json index 99a6eef0566..a3bf48c132e 100644 --- a/i18n/esn/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json +++ b/i18n/esn/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json @@ -5,6 +5,7 @@ // Do not edit this file. It is machine generated. { "TerminalTaskSystem.unknownError": "Error desconocido durante la ejecución de una tarea. Vea el registro de resultados de la tarea para obtener más detalles.", + "dependencyFailed": "No se pudo resolver la tarea dependiente '{0}' en la carpeta del área de trabajo '{1}'", "TerminalTaskSystem.terminalName": "Tarea - {0}", "reuseTerminal": "Las tareas reutilizarán el terminal, presione cualquier tecla para cerrarlo.", "TerminalTaskSystem": "No se puede ejecutar un comando shell en una unidad UNC.", diff --git a/i18n/esn/src/vs/workbench/services/configuration/node/configuration.i18n.json b/i18n/esn/src/vs/workbench/services/configuration/node/configuration.i18n.json index 87e6573dc45..a038f4e9746 100644 --- a/i18n/esn/src/vs/workbench/services/configuration/node/configuration.i18n.json +++ b/i18n/esn/src/vs/workbench/services/configuration/node/configuration.i18n.json @@ -14,7 +14,10 @@ "vscode.extension.contributes.defaultConfiguration": "Contribuye a la configuración de los parámetros del editor predeterminados por lenguaje.", "invalid.properties": "configuration.properties debe ser un objeto", "invalid.allOf": "'configuration.allOf' está en desuso y ya no debe ser utilizado. En cambio, pasar varias secciones de configuración como una matriz al punto de contribución de 'configuración'.", - "workspaceConfig.folders.description": "Lista de carpetas que debe cargarse en el área de trabajo. Debe ser una ruta de acceso de archivo; por ejemplo, \"/raíz/carpetaA\" o \"./carpetaA\" para una ruta de acceso relativa que se resolverá respecto a la ubicación del archivo del área de trabajo.", - "workspaceConfig.folder.description": "Ruta de acceso de archivo; por ejemplo, \"/raíz/carpetaA\" o \"./carpetaA\" para una ruta de acceso de archivo que se resolverá respecto a la ubicación del archivo del área de trabajo.", - "workspaceConfig.settings.description": "Configuración de área de trabajo" + "workspaceConfig.folders.description": "Lista de carpetas para cargar en el área de trabajo. ", + "workspaceConfig.path.description": "Ruta de acceso de archivo; por ejemplo, \"/raíz/carpetaA\" o \"./carpetaA\" para una ruta de acceso de archivo que se resolverá respecto a la ubicación del archivo del área de trabajo.", + "workspaceConfig.name.description": "Un nombre opcional para la carpeta. ", + "workspaceConfig.uri.description": "URI de la carpeta", + "workspaceConfig.settings.description": "Configuración de área de trabajo", + "workspaceConfig.extensions.description": "Extensiones del área de trabajo" } \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/services/files/electron-browser/remoteFileService.i18n.json b/i18n/esn/src/vs/workbench/services/files/electron-browser/remoteFileService.i18n.json new file mode 100644 index 00000000000..1d8632efe35 --- /dev/null +++ b/i18n/esn/src/vs/workbench/services/files/electron-browser/remoteFileService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileBinaryError": "El archivo parece ser binario y no se puede abrir como texto" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/services/files/node/fileService.i18n.json b/i18n/esn/src/vs/workbench/services/files/node/fileService.i18n.json index 64389cb9764..32802572ad8 100644 --- a/i18n/esn/src/vs/workbench/services/files/node/fileService.i18n.json +++ b/i18n/esn/src/vs/workbench/services/files/node/fileService.i18n.json @@ -10,6 +10,7 @@ "fileTooLargeError": "Archivo demasiado grande para abrirlo", "fileBinaryError": "El archivo parece ser binario y no se puede abrir como texto", "fileNotFoundError": "Archivo no encontrado ({0})", + "fileExists": "El archivo a crear ya existe ({0})", "fileMoveConflict": "No se puede mover o copiar. El archivo ya existe en la ubicación de destino. ", "unableToMoveCopyError": "No se puede mover o copiar. El archivo reemplazaría a la carpeta que lo contiene.", "foldersCopyError": "No se pueden copiar carpetas en el área de trabajo. Seleccione archivos individuales para copiarlos.", diff --git a/i18n/fra/extensions/css/package.i18n.json b/i18n/fra/extensions/css/package.i18n.json index 161a45a8a78..a6f0b6d471f 100644 --- a/i18n/fra/extensions/css/package.i18n.json +++ b/i18n/fra/extensions/css/package.i18n.json @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { + "css.title": "CSS", "css.lint.argumentsInColorFunction.desc": "Nombre de paramètres non valide", "css.lint.boxModel.desc": "Ne pas utiliser la largeur ou la hauteur avec une marge intérieure ou une bordure", "css.lint.compatibleVendorPrefixes.desc": "Lors de l'utilisation d'un préfixe spécifique à un fabricant, toujours inclure également toutes les propriétés spécifiques au fabricant", @@ -25,6 +26,7 @@ "css.trace.server.desc": "Trace la communication entre VS Code et le serveur de langage CSS.", "css.validate.title": "Contrôle la validation CSS et la gravité des problèmes.", "css.validate.desc": "Active ou désactive toutes les validations", + "less.title": "LESS", "less.lint.argumentsInColorFunction.desc": "Nombre de paramètres non valide", "less.lint.boxModel.desc": "Ne pas utiliser la largeur ou la hauteur avec une marge intérieure ou une bordure", "less.lint.compatibleVendorPrefixes.desc": "Lors de l'utilisation d'un préfixe spécifique à un fabricant, toujours inclure également toutes les propriétés spécifiques au fabricant", @@ -45,6 +47,7 @@ "less.lint.zeroUnits.desc": "Aucune unité nécessaire pour zéro", "less.validate.title": "Contrôle la validation LESS et la gravité des problèmes.", "less.validate.desc": "Active ou désactive toutes les validations", + "scss.title": "SCSS (Sass)", "scss.lint.argumentsInColorFunction.desc": "Nombre de paramètres non valide", "scss.lint.boxModel.desc": "Ne pas utiliser la largeur ou la hauteur avec une marge intérieure ou une bordure", "scss.lint.compatibleVendorPrefixes.desc": "Lors de l'utilisation d'un préfixe spécifique à un fabricant, toujours inclure également toutes les propriétés spécifiques au fabricant", diff --git a/i18n/fra/src/vs/code/electron-main/menus.i18n.json b/i18n/fra/src/vs/code/electron-main/menus.i18n.json index 4b6ddec2020..58e75c2db36 100644 --- a/i18n/fra/src/vs/code/electron-main/menus.i18n.json +++ b/i18n/fra/src/vs/code/electron-main/menus.i18n.json @@ -151,6 +151,10 @@ "mZoom": "Zoom", "mBringToFront": "Mettre tout au premier plan", "miSwitchWindow": "Changer de &&fenêtre...", + "mShowPreviousTab": "Afficher l’onglet précédent", + "mShowNextTab": "Afficher l’onglet suivant", + "mMoveTabToNewWindow": "Déplacer l’onglet vers une nouvelle fenêtre", + "mMergeAllWindows": "Fusionner toutes les fenêtres", "miToggleDevTools": "Activer/désactiver les ou&&tils de développement", "miAccessibilityOptions": "&&Options d'accessibilité", "miReportIssues": "S&&ignaler les problèmes", @@ -171,7 +175,7 @@ "miRunningTask": "Afficher les &&tâches en cours...", "miRestartTask": "R&&edémarrer la tâche en cours d'exécution...", "miTerminateTask": "&&Terminer la tâche...", - "miConfigureTask": "&&Configurer les tâches", + "miConfigureTask": "&&Configurer les tâches...", "miConfigureBuildTask": "Configurer la tâche de génération par dé&&faut", "accessibilityOptionsWindowTitle": "Options d'accessibilité", "miRestartToUpdate": "Redémarrer pour mettre à jour...", diff --git a/i18n/fra/src/vs/code/electron-main/windows.i18n.json b/i18n/fra/src/vs/code/electron-main/windows.i18n.json index 99857324e72..c85aba4cc7b 100644 --- a/i18n/fra/src/vs/code/electron-main/windows.i18n.json +++ b/i18n/fra/src/vs/code/electron-main/windows.i18n.json @@ -17,6 +17,8 @@ "open": "Ouvrir", "openFolder": "Ouvrir le dossier", "openFile": "Ouvrir le fichier", + "workspaceOpenedMessage": "Impossible d’enregistrer l’espace de travail '{0}'", + "workspaceOpenedDetail": "L’espace de travail est déjà ouvert dans une autre fenêtre. Veuillez s’il vous plaît d’abord fermer cette fenêtre et puis essayez à nouveau.", "openWorkspace": "&&Ouvrir...", "openWorkspaceTitle": "Ouvrir un espace de travail", "save": "&&Enregistrer", diff --git a/i18n/fra/src/vs/editor/common/config/commonEditorConfig.i18n.json b/i18n/fra/src/vs/editor/common/config/commonEditorConfig.i18n.json index 65942745629..e31fe031927 100644 --- a/i18n/fra/src/vs/editor/common/config/commonEditorConfig.i18n.json +++ b/i18n/fra/src/vs/editor/common/config/commonEditorConfig.i18n.json @@ -88,6 +88,7 @@ "accessibilitySupport": "Contrôle si l'éditeur doit s'exécuter dans un mode optimisé pour les lecteurs d'écran.", "links": "Contrôle si l'éditeur doit détecter les liens et les rendre cliquables", "colorDecorators": "Contrôle si l'éditeur doit afficher les éléments décoratifs de couleurs inline et le sélecteur de couleurs.", + "codeActions": "Active l'ampoule d'action de code", "sideBySide": "Contrôle si l'éditeur de différences affiche les différences en mode côte à côte ou inline", "ignoreTrimWhitespace": "Contrôle si l'éditeur de différences affiche les changements liés aux espaces blancs de début ou de fin comme des différences", "renderIndicators": "Contrôle si l'éditeur de différences affiche les indicateurs +/- pour les modifications ajoutées/supprimées", diff --git a/i18n/fra/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json b/i18n/fra/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json index 4ec0caa12c1..ca7209bfd1d 100644 --- a/i18n/fra/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json +++ b/i18n/fra/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json @@ -13,6 +13,7 @@ "vscode.extension.contributes.menuItem.group": "Groupe auquel cette commande appartient", "vscode.extension.contributes.menus": "Contribue à fournir des éléments de menu à l'éditeur", "menus.commandPalette": "Palette de commandes", + "menus.touchBar": "La touch bar (macOS uniquement)", "menus.editorTitle": "Menu de titre de l'éditeur", "menus.editorContext": "Menu contextuel de l'éditeur", "menus.explorerContext": "Menu contextuel de l'Explorateur de fichiers", diff --git a/i18n/fra/src/vs/platform/environment/node/argv.i18n.json b/i18n/fra/src/vs/platform/environment/node/argv.i18n.json index f8792338e56..2f3ab904009 100644 --- a/i18n/fra/src/vs/platform/environment/node/argv.i18n.json +++ b/i18n/fra/src/vs/platform/environment/node/argv.i18n.json @@ -15,6 +15,7 @@ "reuseWindow": "Forcez l'ouverture d'un fichier ou dossier dans la dernière fenêtre active.", "userDataDir": "Spécifie le répertoire où sont conservées les données des utilisateurs. S'avère utile pour une exécution en tant que root.", "verbose": "Affichez la sortie détaillée (implique --wait).", + "wait": "Attendre que les fichiers soient fermés avant de retourner.", "extensionHomePath": "Définissez le chemin racine des extensions.", "listExtensions": "Listez les extensions installées.", "showVersions": "Affichez les versions des extensions installées, quand --list-extension est utilisé.", diff --git a/i18n/fra/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json b/i18n/fra/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json index 38f12473526..e6693aebb79 100644 --- a/i18n/fra/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json +++ b/i18n/fra/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json @@ -5,10 +5,8 @@ // Do not edit this file. It is machine generated. { "invalidManifest": "Extension non valide : package.json n'est pas un fichier JSON.", - "restartCode": "Redémarrez Code avant de réinstaller {0}.", - "installDependeciesConfirmation": "L'installation de '{0}' entraîne également l'installation de ses dépendances. Voulez-vous continuer ?", - "install": "Oui", - "doNotInstall": "Non", + "restartCodeLocal": "Redémarrez Code avant de réinstaller {0}.", + "restartCodeGallery": "Veuillez s’il vous plaît redémarrer Code avant la réinstallation.", "uninstallDependeciesConfirmation": "Voulez-vous désinstaller uniquement '{0}' ou également ses dépendances ?", "uninstallOnly": "Uniquement", "uninstallAll": "Tout", diff --git a/i18n/fra/src/vs/platform/extensions/common/extensionsRegistry.i18n.json b/i18n/fra/src/vs/platform/extensions/common/extensionsRegistry.i18n.json index 29156c8b5c9..77c19000246 100644 --- a/i18n/fra/src/vs/platform/extensions/common/extensionsRegistry.i18n.json +++ b/i18n/fra/src/vs/platform/extensions/common/extensionsRegistry.i18n.json @@ -16,6 +16,7 @@ "vscode.extension.activationEvents": "Événements d'activation pour l'extension VS Code.", "vscode.extension.activationEvents.onLanguage": "Événement d'activation envoyé quand un fichier résolu dans le langage spécifié est ouvert.", "vscode.extension.activationEvents.onCommand": "Événement d'activation envoyé quand la commande spécifiée est appelée.", + "vscode.extension.activationEvents.onDebug": "Un événement d’activation émis chaque fois qu’un utilisateur est sur le point de démarrer le débogage ou sur le point de la déboguer des configurations.", "vscode.extension.activationEvents.workspaceContains": "Événement d'activation envoyé quand un dossier ouvert contient au moins un fichier correspondant au modèle glob spécifié.", "vscode.extension.activationEvents.onView": "Événement d'activation envoyé quand la vue spécifiée est développée.", "vscode.extension.activationEvents.star": "Événement d'activation envoyé au démarrage de VS Code. Pour garantir la qualité de l'expérience utilisateur, utilisez cet événement d'activation dans votre extension uniquement quand aucune autre combinaison d'événements d'activation ne fonctionne dans votre cas d'utilisation.", diff --git a/i18n/fra/src/vs/platform/theme/common/colorExtensionPoint.i18n.json b/i18n/fra/src/vs/platform/theme/common/colorExtensionPoint.i18n.json index bed03d02fba..9bf03d39312 100644 --- a/i18n/fra/src/vs/platform/theme/common/colorExtensionPoint.i18n.json +++ b/i18n/fra/src/vs/platform/theme/common/colorExtensionPoint.i18n.json @@ -13,6 +13,8 @@ "contributes.defaults.highContrast": "La couleur par défaut pour les thèmes de contraste élevé. Soit une valeur de couleur en hexadécimal (#RRGGBB[AA]) ou l’identifiant d’une couleur dont le thème peut être changé qui fournit la valeur par défaut.", "invalid.colorConfiguration": "'configuration.colors' doit être un tableau", "invalid.default.colorType": "{0} doit être soit une valeur de couleur en hexadécimal (#RRGGBB[AA] ou #RGB[A]) ou l’identifiant d’une couleur dont le thème peut être changé qui fournit la valeur par défaut.", + "invalid.id": "'configuration.colors.id' doit être défini et ne peut pas être vide", "invalid.id.format": "'configuration.colors.id' doit suivre le word[.word]*", + "invalid.description": "'configuration.colors.description' doit être défini et ne peut pas être vide", "invalid.defaults": "'configuration.colors.defaults' doit être défini et doit contenir 'light', 'dark' et 'highContrast'" } \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/browser/actions/workspaceActions.i18n.json b/i18n/fra/src/vs/workbench/browser/actions/workspaceActions.i18n.json index c65ee0c9b33..857489eaa8f 100644 --- a/i18n/fra/src/vs/workbench/browser/actions/workspaceActions.i18n.json +++ b/i18n/fra/src/vs/workbench/browser/actions/workspaceActions.i18n.json @@ -14,9 +14,9 @@ "selectWorkspace": "Sélectionner les dossiers pour l’espace de travail", "removeFolderFromWorkspace": "Supprimer le dossier de l'espace de travail", "saveWorkspaceAsAction": "Enregistrer l’espace de travail sous...", - "saveEmptyWorkspaceNotSupported": "Veuillez d’abord ouvrir un espace de travail pour enregistrer.", "save": "&&Enregistrer", "saveWorkspace": "Enregistrer l’espace de travail", "openWorkspaceAction": "Ouvrir un espace de travail...", - "openWorkspaceConfigFile": "Ouvrir le Fichier de Configuration d’espace de travail" + "openWorkspaceConfigFile": "Ouvrir le Fichier de Configuration d’espace de travail", + "workspaceFolderPickerPlaceholder": "Sélectionner le dossier de l’espace de travail" } \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json b/i18n/fra/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json index 1147d510ce6..1bd02d3dbad 100644 --- a/i18n/fra/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json +++ b/i18n/fra/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json @@ -6,6 +6,7 @@ { "badgeTitle": "{0} - {1}", "titleKeybinding": "{0} ({1})", + "removeFromActivityBar": "Masquer de la barre d’activités", "keepInActivityBar": "Conserver dans la barre d'activités", "additionalViews": "Vues supplémentaires", "numberBadge": "{0} ({1})", diff --git a/i18n/fra/src/vs/workbench/browser/parts/editor/editorActions.i18n.json b/i18n/fra/src/vs/workbench/browser/parts/editor/editorActions.i18n.json index 2ebd48c5bc8..a7e57354829 100644 --- a/i18n/fra/src/vs/workbench/browser/parts/editor/editorActions.i18n.json +++ b/i18n/fra/src/vs/workbench/browser/parts/editor/editorActions.i18n.json @@ -35,6 +35,7 @@ "openPreviousEditorInGroup": "Ouvrir l'éditeur précédent du groupe", "navigateNext": "Suivant", "navigatePrevious": "Précédent", + "navigateLast": "Aller au dernier", "reopenClosedEditor": "Rouvrir l'éditeur fermé", "clearRecentFiles": "Effacer les fichiers récemment ouverts", "showEditorsInFirstGroup": "Afficher les éditeurs du premier groupe", diff --git a/i18n/fra/src/vs/workbench/browser/parts/views/panelViewlet.i18n.json b/i18n/fra/src/vs/workbench/browser/parts/views/panelViewlet.i18n.json new file mode 100644 index 00000000000..4868e0b0e11 --- /dev/null +++ b/i18n/fra/src/vs/workbench/browser/parts/views/panelViewlet.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "viewToolbarAriaLabel": "{0} actions" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/electron-browser/actions.i18n.json b/i18n/fra/src/vs/workbench/electron-browser/actions.i18n.json index 55e38a54c34..fc7be7db920 100644 --- a/i18n/fra/src/vs/workbench/electron-browser/actions.i18n.json +++ b/i18n/fra/src/vs/workbench/electron-browser/actions.i18n.json @@ -42,5 +42,10 @@ "navigateUp": "Naviguer vers l'affichage au-dessus", "navigateDown": "Naviguer vers l'affichage en dessous", "increaseViewSize": "Augmenter la taille de l'affichage actuel", - "decreaseViewSize": "Diminuer la taille de l'affichage actuel" + "decreaseViewSize": "Diminuer la taille de l'affichage actuel", + "showPreviousTab": "Afficher l’onglet de la fenêtre précédente", + "showNextWindowTab": "Afficher l’onglet de la fenêtre suivante", + "moveWindowTabToNewWindow": "Déplacer l’onglet de la fenêtre vers la nouvelle fenêtre", + "mergeAllWindowTabs": "Fusionner toutes les fenêtres", + "toggleWindowTabsBar": "Activer/désactiver la barre de fenêtres d’onglets" } \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/electron-browser/main.contribution.i18n.json b/i18n/fra/src/vs/workbench/electron-browser/main.contribution.i18n.json index cd1097afbbd..5ce67b4843a 100644 --- a/i18n/fra/src/vs/workbench/electron-browser/main.contribution.i18n.json +++ b/i18n/fra/src/vs/workbench/electron-browser/main.contribution.i18n.json @@ -10,6 +10,11 @@ "workspaces": "Espaces de travail", "developer": "Développeur", "showEditorTabs": "Contrôle si les éditeurs ouverts doivent s'afficher ou non sous des onglets.", + "workbench.editor.labelFormat.default": "Indiquer le nom du fichier. Lorsque les onglets sont activés et deux fichiers portent le même nom dans un groupe, les sections distinctes du chemin de chaque fichier sont ajoutées. Lorsque les onglets sont désactivées, le chemin relatif à la racine de l’espace de travail est affiché si l’éditeur est actif.", + "workbench.editor.labelFormat.short": "Indiquer le nom du fichier suivi de son nom de répertoire.", + "workbench.editor.labelFormat.medium": "Indiquer le nom du fichier suivi de son chemin d’accès relatif à la racine de l’espace de travail.", + "workbench.editor.labelFormat.long": "Indiquer le nom du fichier suivi de son chemin d’accès absolu.", + "tabDescription": "Contrôle le format de l’étiquette d’un éditeur. La modification de ce paramètre peut par exemple rendre plus facile la compréhension de l’emplacement d’un fichier :\n- short: 'parent'\n- medium: 'workspace/src/parent'\n- long: '/home/user/workspace/src/parent'\n- default: '.../parent', quand un autre onglet partage le même titre, ou la chemin d’accès relatif à l'espace de travail si les onglets sont désactivés", "editorTabCloseButton": "Contrôle la position des boutons de fermeture des onglets de l'éditeur, ou les désactive quand le paramètre a la valeur 'off'.", "showIcons": "Contrôle si les éditeurs ouverts doivent s'afficher ou non avec une icône. Cela implique notamment l'activation d'un thème d'icône.", "enablePreview": "Contrôle si les éditeurs ouverts s'affichent en mode aperçu. Les éditeurs en mode aperçu sont réutilisés jusqu'à ce qu'ils soient conservés (par exemple, après un double-clic ou une modification).", diff --git a/i18n/fra/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json b/i18n/fra/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json index 7e1c234e5c3..f97dc7aa91f 100644 --- a/i18n/fra/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json +++ b/i18n/fra/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json @@ -36,5 +36,7 @@ "schema.indentationRules.unIndentedLinePattern": "Si une ligne correspond à ce modèle, sa mise en retrait ne doit pas être changée et la ligne ne doit pas être évaluée par rapport aux autres règles.", "schema.indentationRules.unIndentedLinePattern.pattern": "Modèle RegExp pour unIndentedLinePattern.", "schema.indentationRules.unIndentedLinePattern.flags": "Indicateurs RegExp pour unIndentedLinePattern.", - "schema.indentationRules.unIndentedLinePattern.errorMessage": "Doit valider l'expression régulière `/^([gimuy]+)$/`." + "schema.indentationRules.unIndentedLinePattern.errorMessage": "Doit valider l'expression régulière `/^([gimuy]+)$/`.", + "schema.folding": "Paramètres de repliage de la langue.", + "schema.folding.offSide": "Un langage adhère à la règle du hors-champ si les blocs dans ce langage sont exprimées par leur indentation. Si spécifié, les lignes vides appartiennent au bloc suivant." } \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json b/i18n/fra/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json index d60a025a1a0..ff99f2314ff 100644 --- a/i18n/fra/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json +++ b/i18n/fra/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json @@ -12,6 +12,8 @@ "breakpointRemoved": "Point d'arrêt supprimé, ligne {0}, fichier {1}", "compoundMustHaveConfigurations": "L'attribut \"configurations\" du composé doit être défini pour permettre le démarrage de plusieurs configurations.", "configMissing": "Il manque la configuration '{0}' dans 'launch.json'.", + "debugRequestNotSupported": "La requête de débogage configurée '{0}' n'est pas prise en charge.", + "debugRequesMissing": "La requête de débogage est manquante pour la configuration de lancement choisie.", "debugTypeNotSupported": "Le type de débogage '{0}' configuré n'est pas pris en charge.", "debugTypeMissing": "Propriété 'type' manquante pour la configuration de lancement choisie.", "preLaunchTaskErrors": "Des erreurs de build ont été détectées durant le preLaunchTask '{0}'.", diff --git a/i18n/fra/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json b/i18n/fra/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json index c9d61f5102b..d2552c4f0ca 100644 --- a/i18n/fra/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json +++ b/i18n/fra/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json @@ -29,6 +29,13 @@ "view id": "ID", "view name": "Nom", "view location": "Emplacement", + "colorThemes": "Thèmes de couleurs ({0})", + "iconThemes": "Thèmes d’icônes ({0})", + "colors": "Couleurs ({0})", + "colorId": "Id", + "defaultDark": "Défaut pour le thème sombre", + "defaultLight": "Défaut pour le thème clair", + "defaultHC": "Défaut pour le thème de contraste élevé", "JSON Validation": "Validation JSON ({0})", "commands": "Commandes ({0})", "command name": "Nom", diff --git a/i18n/fra/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json b/i18n/fra/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json index bb40909dd1c..2d42d92f27e 100644 --- a/i18n/fra/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json +++ b/i18n/fra/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json @@ -51,9 +51,9 @@ "showLanguageExtensionsShort": "Extensions de langage", "showAzureExtensions": "Afficher les Extensions Azure", "showAzureExtensionsShort": "Extensions Azure", - "configureWorkspaceRecommendedExtensions": "Configurer les extensions recommandées (espace de travail)", - "ConfigureWorkspaceRecommendations.noWorkspace": "Les recommandations ne sont disponibles que pour un dossier d'espace de travail.", "OpenExtensionsFile.failed": "Impossible de créer le fichier 'extensions.json' dans le dossier '.vscode' ({0}).", + "configureWorkspaceRecommendedExtensions": "Configurer les extensions recommandées (espace de travail)", + "configureWorkspaceFolderRecommendedExtensions": "Configurer les extensions recommandées (Dossier d'espace de travail)", "builtin": "Intégrée", "disableAll": "Désactiver toutes les extensions installées", "disableAllWorkspace": "Désactiver toutes les extensions installées pour cet espace de travail", diff --git a/i18n/fra/src/vs/workbench/parts/files/browser/files.contribution.i18n.json b/i18n/fra/src/vs/workbench/parts/files/browser/files.contribution.i18n.json index c3da7244da9..db6ba3d69b2 100644 --- a/i18n/fra/src/vs/workbench/parts/files/browser/files.contribution.i18n.json +++ b/i18n/fra/src/vs/workbench/parts/files/browser/files.contribution.i18n.json @@ -14,6 +14,8 @@ "files.exclude.boolean": "Modèle Glob auquel les chemins de fichiers doivent correspondre. Affectez la valeur true ou false pour activer ou désactiver le modèle.", "files.exclude.when": "Vérification supplémentaire des frères d'un fichier correspondant. Utilisez $(basename) comme variable pour le nom de fichier correspondant.", "associations": "Configurez les associations entre les fichiers et les langages (par exemple, \"*.extension\": \"html\"). Celles-ci ont priorité sur les associations par défaut des langages installés.", + "encoding": "L'encodage du jeu de caractères par défaut à utiliser durant la lecture et l'écriture des fichiers. Ce paramètre peut également être configuré par langage.", + "autoGuessEncoding": "Quand cette option est activée, tente de deviner l'encodage du jeu de caractères à l'ouverture des fichiers. Ce paramètre peut également être configuré par langage.", "eol": "Caractère de fin de ligne par défaut. Utilisez \\n pour LF et \\r\\n pour CRLF.", "trimTrailingWhitespace": "Si l'option est activée, l'espace blanc de fin est supprimé au moment de l'enregistrement d'un fichier.", "insertFinalNewline": "Quand l'option est activée, une nouvelle ligne finale est insérée à la fin du fichier au moment de son enregistrement.", diff --git a/i18n/fra/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json b/i18n/fra/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json index 0a89ce8a7c8..457666ad7bb 100644 --- a/i18n/fra/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json +++ b/i18n/fra/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json @@ -4,5 +4,6 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { + "dirtyFile": "1 fichier non enregistré", "dirtyFiles": "{0} fichiers non enregistrés" } \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json b/i18n/fra/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json index 829d423c32e..740f133866c 100644 --- a/i18n/fra/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json +++ b/i18n/fra/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json @@ -9,7 +9,6 @@ "openGlobalKeybindingsFile": "Ouvrir le fichier des raccourcis clavier", "openWorkspaceSettings": "Ouvrir les paramètres d'espace de travail", "openFolderSettings": "Ouvrir le dossier Paramètres", - "pickFolder": "Sélectionner le dossier", "configureLanguageBasedSettings": "Configurer les paramètres spécifiques au langage...", "languageDescriptionConfigured": "({0})", "pickLanguage": "Sélectionner un langage" diff --git a/i18n/fra/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json b/i18n/fra/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json index df36de368d3..46bb91f2672 100644 --- a/i18n/fra/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json +++ b/i18n/fra/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json @@ -6,7 +6,5 @@ { "relaunchSettingMessage": "Un paramètre a changé et nécessite un redémarrage pour être appliqué.", "relaunchSettingDetail": "Appuyez sur le bouton de redémarrage pour redémarrer {0} et activer le paramètre.", - "restart": "Redémarrer", - "relaunchWorkspaceMessage": "Ce changement d’espace de travail nécessite un rechargement de notre système d’extension.", - "reload": "Recharger" + "restart": "Redémarrer" } \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json b/i18n/fra/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json index a7fc2987a18..f2a4104bb7f 100644 --- a/i18n/fra/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json +++ b/i18n/fra/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json @@ -4,8 +4,10 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { + "scm providers": "Fournisseurs de contrôle de code source", "commitMessage": "Message (press {0} to commit)", "installAdditionalSCMProviders": "Installer des fournisseurs SCM supplémentaires...", + "no open repo": "Il n’y a aucun fournisseur de contrôle de code source actif.", "source control": "Contrôle de code source", "viewletTitle": "{0} : {1}" } \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json b/i18n/fra/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json index 27c0397de03..b8f73f7acad 100644 --- a/i18n/fra/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json +++ b/i18n/fra/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json @@ -4,5 +4,7 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "snippet.suggestions.label": "Insérer un extrait de code" + "snippet.suggestions.label": "Insérer un extrait de code", + "sep.userSnippet": "Extraits de code de l'utilisateur", + "sep.extSnippet": "Extraits de code d’extension" } \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json b/i18n/fra/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json index 7f0f8335a8c..b340098147f 100644 --- a/i18n/fra/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json +++ b/i18n/fra/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json @@ -10,6 +10,7 @@ "vscode.extension.contributes.snippets": "Ajoute des extraits de code.", "vscode.extension.contributes.snippets-language": "Identificateur de langage pour lequel cet extrait de code est ajouté.", "vscode.extension.contributes.snippets-path": "Chemin du fichier d'extraits de code. Le chemin est relatif au dossier d'extensions et commence généralement par './snippets/'.", + "badFile": "Le fichier d’extrait \"{0}\" n’a pas pu être lu.", "badVariableUse": "L'extrait de code \"{0}\" confond très probablement les variables et les espaces réservés d'extrait de code. Consultez https://code.visualstudio.com/docs/editor/userdefinedsnippets#_snippet-syntax pour plus d'informations.", "source.snippet": "Extrait de code utilisateur", "detail.snippet": "{0} ({1})", diff --git a/i18n/fra/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json b/i18n/fra/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json index be9fba5ac79..9c8b1eb40b1 100644 --- a/i18n/fra/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json +++ b/i18n/fra/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json @@ -5,13 +5,7 @@ // Do not edit this file. It is machine generated. { "tasksCategory": "Tâches", - "ConfigureTaskRunnerAction.noWorkspace": "Les tâches ne sont disponibles que dans un dossier d'espace de travail.", - "ConfigureTaskRunnerAction.quickPick.template": "Sélectionner un exécuteur de tâches", - "ConfigureTaskRunnerAction.autoDetecting": "Détection automatique des tâches pour {0}", - "ConfigureTaskRunnerAction.autoDetect": "En raison de l'échec de la détection automatique du système de tâche, le modèle par défaut va être utilisé. Pour plus d'informations, consultez la sortie de la tâche.", - "ConfigureTaskRunnerAction.autoDetectError": "La détection automatique du système de tâche a produit des erreurs. Consultez la sortie de la tâche pour plus d'informations.", - "ConfigureTaskRunnerAction.failed": "Impossible de créer le fichier 'tasks.json' dans le dossier '.vscode'. Pour plus d'informations, consultez la sortie de la tâche.", - "ConfigureTaskRunnerAction.label": "Configurer l'exécuteur de tâches", + "ConfigureTaskRunnerAction.label": "Configurer une tâche", "ConfigureBuildTaskAction.label": "Configurer une tâche de build", "CloseMessageAction.label": "Fermer", "ShowTerminalAction.label": "Afficher le terminal", @@ -19,11 +13,13 @@ "manyMarkers": "99", "runningTasks": "Afficher les tâches en cours d'exécution", "tasks": "Tâches", + "TaskSystem.noHotSwap": "Changer le moteur d’exécution de tâches avec une tâche active en cours d’exécution nécessite de recharger la fenêtre", "TaskService.noBuildTask1": "Aucune tâche de build définie. Marquez une tâche avec 'isBuildCommand' dans le fichier tasks.json.", "TaskService.noBuildTask2": "Aucune tâche de génération définie. Marquez une tâche comme groupe 'build' dans le fichier tasks.json.", "TaskService.noTestTask1": "Aucune tâche de test définie. Marquez une tâche avec 'isTestCommand' dans le fichier tasks.json.", "TaskService.noTestTask2": "Aucune tâche de test définie. Marquez une tâche comme groupe 'test' dans le fichier tasks.json.", "TaskServer.noTask": "La tâche {0} à exécuter est introuvable.", + "TaskService.associate": "associer", "TaskService.attachProblemMatcher.continueWithout": "Continuer sans analyser la sortie de la tâche", "TaskService.attachProblemMatcher.never": "Ne jamais vérifier la sortie de la tâche", "TaskService.attachProblemMatcher.learnMoreAbout": "En savoir plus sur la sortie de la tâche de numérisation", @@ -35,6 +31,7 @@ "TaskSystem.active": "Une tâche est déjà en cours d'exécution. Terminez-la avant d'exécuter une autre tâche.", "TaskSystem.restartFailed": "Échec de la fin de l'exécution de la tâche {0}", "TaskSystem.configurationErrors": "Erreur : la configuration de tâche fournie comporte des erreurs de validation et ne peut pas être utilisée. Corrigez d'abord les erreurs.", + "taskService.ignoreingFolder": "Ignorer les tâches de configuration pour le dossier de l’espace de travail {0}. Le support de dossiers racine multiples requiert que tous les dossiers utilisent task version 2.0.0\n", "TaskSystem.invalidTaskJson": "Erreur : le fichier tasks.json contient des erreurs de syntaxe. Corrigez-les avant d'exécuter une tâche.\n", "TaskSystem.runningTask": "Une tâche est en cours d'exécution. Voulez-vous la terminer ?", "TaskSystem.terminateTask": "&&Terminer la tâche", @@ -46,24 +43,30 @@ "recentlyUsed": "tâches récemment utilisées", "configured": "tâches configurées", "detected": "tâches détectées", + "TaskService.pickRunTask": "Sélectionner la tâche à exécuter", + "TaslService.noEntryToRun": "Aucune tâche à exécuter n'a été trouvée. Configurer les tâches...", "TaskService.fetchingBuildTasks": "Récupération des tâches de génération...", - "TaskService.noBuildTaskTerminal": "Aucune tâche de génération. Appuyez sur 'Configurer une tâche de génération' pour en définir une.", "TaskService.pickBuildTask": "Sélectionner la tâche de génération à exécuter", + "TaskService.noBuildTask": "Aucune tâche de génération à exécuter n'a été trouvée. Configurer les tâches...", "TaskService.fetchingTestTasks": "Récupération des tâches de test...", - "TaskService.noTestTaskTerminal": "Aucune tâche de test. Appuyez sur 'Configurer un exécuteur de tâches' pour en définir un.", "TaskService.pickTestTask": "Sélectionner la tâche de test à exécuter", - "TaskService.noTaskRunning": "Aucune tâche en cours d'exécution.", + "TaskService.noTestTaskTerminal": "Aucune tâche de test à exécuter n'a été trouvée. Configurer les tâches...", "TaskService.tastToTerminate": "Sélectionner une tâche à terminer", + "TaskService.noTaskRunning": "Aucune tâche en cours d’exécution", "TerminateAction.noProcess": "Le processus lancé n'existe plus. Si la tâche a engendré des tâches en arrière-plan, la sortie de VS Code risque de donner lieu à des processus orphelins.", "TerminateAction.failed": "Échec de la fin de l'exécution de la tâche", - "TaskService.noTaskToRestart": "Aucune tâche à redémarrer.", "TaskService.tastToRestart": "Sélectionner la tâche à redémarrer", - "TaskService.defaultBuildTaskExists": "{0} est déjà marquée comme tâche de génération par défaut.", + "TaskService.noTaskToRestart": "Aucune tâche à redémarrer.", + "TaskService.template": "Sélectionner un modèle de tâche", + "TaskService.createJsonFile": "Créer le fichier tasks.json à partir d'un modèle", + "TaskService.openJsonFile": "Ouvrir le fichier tasks.json", + "TaskService.pickTask": "Sélectionner une tâche à configurer", + "TaskService.defaultBuildTaskExists": "{0} est déjà marquée comme la tâche de génération par défaut", "TaskService.pickDefaultBuildTask": "Sélectionner la tâche à utiliser comme tâche de génération par défaut", "TaskService.defaultTestTaskExists": "{0} est déjà marquée comme tâche de test par défaut.", "TaskService.pickDefaultTestTask": "Sélectionner la tâche à utiliser comme tâche de test par défaut", - "TaskService.noTaskIsRunning": "Aucune tâche n’est en cours d’exécution.", "TaskService.pickShowTask": "Sélectionner la tâche pour montrer sa sortie", + "TaskService.noTaskIsRunning": "Aucune tâche n’est en cours d’exécution", "ShowLogAction.label": "Afficher le journal des tâches", "RunTaskAction.label": "Exécuter la tâche", "RestartTaskAction.label": "Redémarrer la tâche en cours d'exécution", diff --git a/i18n/fra/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json b/i18n/fra/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json index 413680671bf..2265e06f9f3 100644 --- a/i18n/fra/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json +++ b/i18n/fra/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json @@ -5,6 +5,7 @@ // Do not edit this file. It is machine generated. { "TerminalTaskSystem.unknownError": "Une erreur inconnue s'est produite durant l'exécution d'une tâche. Pour plus d'informations, consultez le journal de sortie des tâches.", + "dependencyFailed": "Impossible de résoudre la tâche dépendante '{0}' dans le dossier de l’espace de travail '{1}'", "TerminalTaskSystem.terminalName": "Tâche - {0}", "reuseTerminal": "Le terminal sera réutilisé par les tâches, appuyez sur une touche pour le fermer.", "TerminalTaskSystem": "Impossible d'exécuter une commande d'interpréteur de commandes sur un lecteur UNC.", diff --git a/i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json b/i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json index bd855cdf8da..c06b522fc53 100644 --- a/i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json +++ b/i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json @@ -38,5 +38,6 @@ "workbench.action.terminal.focusFindWidget": "Focus sur le widget de recherche", "workbench.action.terminal.hideFindWidget": "Masquer le widget de recherche", "nextTerminalFindTerm": "Afficher le terme de recherche suivant", - "previousTerminalFindTerm": "Afficher le terme de recherche précédent" + "previousTerminalFindTerm": "Afficher le terme de recherche précédent", + "quickOpenTerm": "Changer de terminal actif" } \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json b/i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json index 55c06f2a2f3..89f1b826995 100644 --- a/i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json +++ b/i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json @@ -8,5 +8,6 @@ "terminal.foreground": "Couleur de premier plan du terminal.", "terminalCursor.foreground": "La couleur de premier plan du curseur du terminal.", "terminalCursor.background": "La couleur d’arrière-plan du curseur terminal. Permet de personnaliser la couleur d’un caractère recouvert par un curseur de bloc.", + "terminal.selectionBackground": "La couleur d’arrière-plan de sélection du terminal.", "terminal.ansiColor": "Couleur ansi '{0}' dans le terminal." } \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json b/i18n/fra/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json index 8f490ebe701..10ab78c5e42 100644 --- a/i18n/fra/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json +++ b/i18n/fra/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json @@ -10,6 +10,7 @@ "welcomePage.newFile": "Nouveau fichier", "welcomePage.openFolder": "Ouvrir un dossier...", "welcomePage.cloneGitRepository": "Cloner le dépôt Git...", + "welcomePage.addWorkspaceFolder": "Ajouter un dossier d’espace de travail...", "welcomePage.recent": "Récent", "welcomePage.moreRecent": "Plus...", "welcomePage.noRecentFolders": "Aucun dossier récent", diff --git a/i18n/fra/src/vs/workbench/services/configuration/node/configuration.i18n.json b/i18n/fra/src/vs/workbench/services/configuration/node/configuration.i18n.json index 82a641c7444..08d373ab90a 100644 --- a/i18n/fra/src/vs/workbench/services/configuration/node/configuration.i18n.json +++ b/i18n/fra/src/vs/workbench/services/configuration/node/configuration.i18n.json @@ -13,7 +13,11 @@ "invalid.title": "'configuration.title' doit être une chaîne", "vscode.extension.contributes.defaultConfiguration": "Contribue aux paramètres de configuration d'éditeur par défaut en fonction du langage.", "invalid.properties": "'configuration.properties' doit être un objet", - "workspaceConfig.folders.description": "Liste des dossiers à charger dans l’espace de travail. Doit être un chemin de fichier, par exemple, '/root/folderA' ou './folderA' pour un chemin relatif résolu selon l’emplacement du fichier d’espace de travail.", - "workspaceConfig.folder.description": "Un chemin de fichier, par exemple, '/root/folderA' ou './folderA' pour un chemin relatif résolu selon l’emplacement du fichier d’espace de travail.", - "workspaceConfig.settings.description": "Paramètres de l’espace de travail" + "invalid.allOf": "'configuration.allOf' est obsolète et ne doit plus être utilisé. Au lieu de cela, passez plusieurs sections de configuration sous forme de tableau au point de contribution 'configuration'.", + "workspaceConfig.folders.description": "Liste des dossiers à être chargés dans l’espace de travail.", + "workspaceConfig.path.description": "Un chemin de fichier, par exemple, '/root/folderA' ou './folderA' pour un chemin relatif résolu selon l’emplacement du fichier d’espace de travail.", + "workspaceConfig.name.description": "Un nom facultatif pour le dossier. ", + "workspaceConfig.uri.description": "URI du dossier", + "workspaceConfig.settings.description": "Paramètres de l’espace de travail", + "workspaceConfig.extensions.description": "Extensions de l’espace de travail" } \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/services/files/electron-browser/remoteFileService.i18n.json b/i18n/fra/src/vs/workbench/services/files/electron-browser/remoteFileService.i18n.json new file mode 100644 index 00000000000..8ca568a2f40 --- /dev/null +++ b/i18n/fra/src/vs/workbench/services/files/electron-browser/remoteFileService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileBinaryError": "Il semble que le fichier soit binaire. Impossible de l'ouvrir en tant que texte" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/services/files/node/fileService.i18n.json b/i18n/fra/src/vs/workbench/services/files/node/fileService.i18n.json index 53f8c612cf7..e5bf54a3941 100644 --- a/i18n/fra/src/vs/workbench/services/files/node/fileService.i18n.json +++ b/i18n/fra/src/vs/workbench/services/files/node/fileService.i18n.json @@ -5,10 +5,12 @@ // Do not edit this file. It is machine generated. { "fileInvalidPath": "Ressource de fichier non valide ({0})", + "fileIsDirectoryError": "Le fichier est un répertoire", "fileNotModifiedError": "Fichier non modifié depuis", "fileTooLargeError": "Fichier trop volumineux pour être ouvert", "fileBinaryError": "Il semble que le fichier soit binaire. Impossible de l'ouvrir en tant que texte", "fileNotFoundError": "Fichier introuvable ({0})", + "fileExists": "Le fichier à créer existe déjà ({0})", "fileMoveConflict": "Déplacement/copie impossible. Le fichier existe déjà dans la destination.", "unableToMoveCopyError": "Impossible de déplacer/copier. Le fichier remplace le dossier qui le contient.", "foldersCopyError": "Impossible de copier des dossiers dans l'espace de travail. Sélectionnez les fichiers à copier individuellement.", diff --git a/i18n/hun/src/vs/code/electron-main/menus.i18n.json b/i18n/hun/src/vs/code/electron-main/menus.i18n.json index 5c7640234ef..62fae0da197 100644 --- a/i18n/hun/src/vs/code/electron-main/menus.i18n.json +++ b/i18n/hun/src/vs/code/electron-main/menus.i18n.json @@ -175,8 +175,8 @@ "miRunningTask": "&&Futó feladatok megjelenítése...", "miRestartTask": "Futó f&&eladat újraindítása...", "miTerminateTask": "Felada&&t megszakítása...", - "miConfigureTask": "Feladatok &&konfigurálása", - "miConfigureBuildTask": "Alapértelmezett buildelési &&feladat beállítása", + "miConfigureTask": "Feladatok &&konfigurálása...", + "miConfigureBuildTask": "Alapértelmezett buildelési &&feladat beállítása...", "accessibilityOptionsWindowTitle": "Kisegítő lehetőségek beállításai", "miRestartToUpdate": "Újraindítás a frissítéshez...", "miCheckingForUpdates": "Frissítések keresése...", diff --git a/i18n/hun/src/vs/code/electron-main/windows.i18n.json b/i18n/hun/src/vs/code/electron-main/windows.i18n.json index 46466fb1dfe..ca0e590ceea 100644 --- a/i18n/hun/src/vs/code/electron-main/windows.i18n.json +++ b/i18n/hun/src/vs/code/electron-main/windows.i18n.json @@ -18,6 +18,7 @@ "openFolder": "Mappa megnyitása", "openFile": "Fájl megnyitása", "workspaceOpenedMessage": "Nem sikerült menteni a(z) '{0}' munkaterületet", + "workspaceOpenedDetail": "A munkaterület már meg van nyitva egy másik ablakban. Zárja be azt az ablakot, majd próbálja újra!", "openWorkspace": "&&Megnyitás", "openWorkspaceTitle": "Munkaterület megnyitása", "save": "Menté&&s", diff --git a/i18n/hun/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json b/i18n/hun/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json index e935364b995..a2afa5b9e5e 100644 --- a/i18n/hun/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json +++ b/i18n/hun/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json @@ -13,6 +13,7 @@ "vscode.extension.contributes.menuItem.group": "A csoport, amibe a parancs tartozik", "vscode.extension.contributes.menus": "Menüket szolgáltat a szerkesztőhöz", "menus.commandPalette": "A parancskatalógus", + "menus.touchBar": "A Touch Bar (csak macOS-en)", "menus.editorTitle": "A szerkesztőablak címsora menüje", "menus.editorContext": "A szerkesztőablak helyi menüje", "menus.explorerContext": "A fájlkezelő helyi menüje", diff --git a/i18n/hun/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json b/i18n/hun/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json index 9a21167685a..339d7b60e78 100644 --- a/i18n/hun/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json +++ b/i18n/hun/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json @@ -5,10 +5,8 @@ // Do not edit this file. It is machine generated. { "invalidManifest": "A kiegészítő érvénytelen: a package.json nem egy JSON-fájl.", - "restartCode": "Indítsa újra a Code-ot a(z) {0} újratelepítése előtt.", - "installDependeciesConfirmation": "A(z) '{0}' teleítése során annak függőségei is telepítve lesznek. Szeretné folytatni?", - "install": "Igen", - "doNotInstall": "Nem", + "restartCodeLocal": "Indítsa újra a Code-ot a(z) {0} újratelepítése előtt.", + "restartCodeGallery": "Indítsa újra a Code-ot az újratelepítés előtt!", "uninstallDependeciesConfirmation": "Csak a(z) '{0}' kiegészítőt szeretné eltávolítani vagy annak függőségeit is?", "uninstallOnly": "Csak ezt", "uninstallAll": "Mindent", diff --git a/i18n/hun/src/vs/workbench/browser/actions/workspaceActions.i18n.json b/i18n/hun/src/vs/workbench/browser/actions/workspaceActions.i18n.json index c7a76624e3d..64c1ec455b3 100644 --- a/i18n/hun/src/vs/workbench/browser/actions/workspaceActions.i18n.json +++ b/i18n/hun/src/vs/workbench/browser/actions/workspaceActions.i18n.json @@ -14,9 +14,9 @@ "selectWorkspace": "Válassza ki a munkaterület mappáit!", "removeFolderFromWorkspace": "Mappa eltávolítása a munkaterületről", "saveWorkspaceAsAction": "Munkaterület mentése másként...", - "saveEmptyWorkspaceNotSupported": "A mentéshez először nyisson meg egy munkaterületet.", "save": "Menté&&s", "saveWorkspace": "Munkaterület mentése", "openWorkspaceAction": "Munkaterület megnyitása...", - "openWorkspaceConfigFile": "Munkaterület konfigurációs fájljának megnyitása" + "openWorkspaceConfigFile": "Munkaterület konfigurációs fájljának megnyitása", + "workspaceFolderPickerPlaceholder": "Válasszon munkaterület-mappát!" } \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/browser/parts/views/panelViewlet.i18n.json b/i18n/hun/src/vs/workbench/browser/parts/views/panelViewlet.i18n.json new file mode 100644 index 00000000000..30ba124e291 --- /dev/null +++ b/i18n/hun/src/vs/workbench/browser/parts/views/panelViewlet.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "viewToolbarAriaLabel": "{0} művelet" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json b/i18n/hun/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json index 92cdf300946..4e6291acf38 100644 --- a/i18n/hun/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json +++ b/i18n/hun/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json @@ -36,5 +36,6 @@ "schema.indentationRules.unIndentedLinePattern": "Ha egy sor illeszkedik erre a mintára, akkor az indentálása nem változik, és nem lesz kiértékelve más szabályok alapján.", "schema.indentationRules.unIndentedLinePattern.pattern": "Az unIndentedLinePatternhöz tartozó reguláris kifejezés.", "schema.indentationRules.unIndentedLinePattern.flags": "Az unIndentedLinePatternhöz tartozó reguláris kifejezés beállításai.", - "schema.indentationRules.unIndentedLinePattern.errorMessage": "Illeszkednie kell a következő mintára: `/^([gimuy]+)$/`." + "schema.indentationRules.unIndentedLinePattern.errorMessage": "Illeszkednie kell a következő mintára: `/^([gimuy]+)$/`.", + "schema.folding": "A nyelv kódrészek bezárásával kapcsolatos beállításai." } \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json b/i18n/hun/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json index 01fc6a8fbf9..01cf97bbd71 100644 --- a/i18n/hun/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json +++ b/i18n/hun/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json @@ -51,9 +51,9 @@ "showLanguageExtensionsShort": "Nyelvi kiegészítők", "showAzureExtensions": "Azure-kiegészítők megjelenítése", "showAzureExtensionsShort": "Azure-kiegészítők", - "configureWorkspaceRecommendedExtensions": "Ajánlott kiegészítők konfigurálása (munkaterületre vonatkozóan)", - "ConfigureWorkspaceRecommendations.noWorkspace": "Az ajánlatok csak egy munkaterület mappájára vonatkozóan érhetők el.", "OpenExtensionsFile.failed": "Nem sikerült létrehozni az 'extensions.json' fájlt a '.vscode' mappánan ({0}).", + "configureWorkspaceRecommendedExtensions": "Ajánlott kiegészítők konfigurálása (munkaterületre vonatkozóan)", + "configureWorkspaceFolderRecommendedExtensions": "Ajánlott kiegészítők konfigurálása (munkaterület-mappára vonatkozóan)", "builtin": "Beépített", "disableAll": "Összes telepített kiegészítő letiltása", "disableAllWorkspace": "Összes telepített kiegészítő letiltása a munkaterületre vonatkozóan", diff --git a/i18n/hun/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json b/i18n/hun/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json index d7bd56c34cc..4d1cfca9683 100644 --- a/i18n/hun/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json +++ b/i18n/hun/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json @@ -9,7 +9,6 @@ "openGlobalKeybindingsFile": "Billentyűparancsfájl megnyitása", "openWorkspaceSettings": "Munkaterület beállításainak megnyitása", "openFolderSettings": "Mappa beállításainak megnyitása", - "pickFolder": "Válasszon mappát!", "configureLanguageBasedSettings": "Nyelvspecifikus beállítások konfigurálása...", "languageDescriptionConfigured": "(({0})", "pickLanguage": "Nyelv kiválasztása" diff --git a/i18n/hun/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json b/i18n/hun/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json index 2100b3de8db..2c74ca18207 100644 --- a/i18n/hun/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json +++ b/i18n/hun/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json @@ -6,7 +6,5 @@ { "relaunchSettingMessage": "Egy olyan beállítás változott, melynek hatályba lépéséhez újraindítás szükséges.", "relaunchSettingDetail": "A beállítás engedélyezéséhez nyomja meg az újraindítás gombot a {0} újraindításához.", - "restart": "Újraindítás", - "relaunchWorkspaceMessage": "A munkaterület módosítása miatt a kiegészítőrendszer újratöltésére van szükség.", - "reload": "Újratöltés" + "restart": "Újraindítás" } \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json b/i18n/hun/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json index 5fe5fb61f23..ba0f414b2c2 100644 --- a/i18n/hun/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json +++ b/i18n/hun/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { + "scm providers": "Verziókezelő rendszerek", "commitMessage": "Üzenet (nyomja meg a következőt a commithoz: {0})", "installAdditionalSCMProviders": "További verziókezelő rendszerek telepítése...", "no open repo": "Nincs aktív verziókezelő rendszer.", diff --git a/i18n/hun/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json b/i18n/hun/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json index d6497ea60ab..ffce26d0024 100644 --- a/i18n/hun/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json +++ b/i18n/hun/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json @@ -5,13 +5,7 @@ // Do not edit this file. It is machine generated. { "tasksCategory": "Feladatok", - "ConfigureTaskRunnerAction.noWorkspace": "A feladatok csak egy munkaterület mappájára vonatkozóan érhetők el.", - "ConfigureTaskRunnerAction.quickPick.template": "Feladatfuttató rendszer választása", - "ConfigureTaskRunnerAction.autoDetecting": "Feladatok automatikus felderítése a következőhöz: {0}", - "ConfigureTaskRunnerAction.autoDetect": "A feladatfuttató rendszer automatikus felderítése nem sikerült. Az alapértelmezett sablon használata. Tekintse meg a feladatkimenetet a részletekért.", - "ConfigureTaskRunnerAction.autoDetectError": "A feladatfuttató rendszer automatikus felderítése hibákat eredményezett. Tekintse meg a feladatkimenetet a részletekért.", - "ConfigureTaskRunnerAction.failed": "Nem sikerült létrehozni a 'tasks.json' fájlt a '.vscode' mappában. Tekintse meg a feladatkimenetet a részletekért.", - "ConfigureTaskRunnerAction.label": "Feladatfuttató rendszer beállítása", + "ConfigureTaskRunnerAction.label": "Feladat beállítása", "ConfigureBuildTaskAction.label": "Buildelési feladat beállítása", "CloseMessageAction.label": "Bezárás", "ShowTerminalAction.label": "Terminál megtekintése", @@ -19,6 +13,7 @@ "manyMarkers": "99+", "runningTasks": "Futó feladatok megjelenítése", "tasks": "Feladatok", + "TaskSystem.noHotSwap": "A feladatvégrehajtó motor megváltoztatása egy futó, aktív feladat esetén az ablak újraindítását igényli.", "TaskService.noBuildTask1": "Nincs buildelési feladat definiálva. Jelöljön meg egy feladatot az 'isBuildCommand' tulajdonsággal a tasks.json fájlban!", "TaskService.noBuildTask2": "Nincs buildelési feladat definiálva. Jelöljön meg egy feladatot a 'build' csoporttal a tasks.json fájlban!", "TaskService.noTestTask1": "Nincs tesztelési feladat definiálva. Jelöljön meg egy feladatot az 'isTestCommand' tulajdonsággal a tasks.json fájlban!", @@ -47,22 +42,16 @@ "configured": "konfigurált feladatok", "detected": "talált feladatok", "TaskService.fetchingBuildTasks": "Buildelési feladatok lekérése...", - "TaskService.noBuildTaskTerminal": "Buildelési feladat nem található. Futtassa a 'Buildelési feladat beállítása' parancsot a létrehozáshoz!", "TaskService.pickBuildTask": "Válassza ki a futtatandó buildelési feladatot!", "TaskService.fetchingTestTasks": "Tesztelési feladatok lekérése...", - "TaskService.noTestTaskTerminal": "Buildelési feladat nem található. Futtassa a 'Feladatfuttató rendszer beállítása' parancsot a létrehozáshoz!", "TaskService.pickTestTask": "Válassza ki a futtatandó tesztelési feladatot", - "TaskService.noTaskRunning": "Jelenleg nem fut feladat.", "TaskService.tastToTerminate": "Válassza ki a megszakítandó feladatot!", "TerminateAction.noProcess": "Az elindított folyamat már nem létezik. Ha a feladat háttérfeladatokat indított, a VS Code-ból való kilépés árva folyamatokat eredményezhet. ", "TerminateAction.failed": "Nem sikerült megszakítani a futó feladatot", - "TaskService.noTaskToRestart": "Nincs újraindítható feladat.", "TaskService.tastToRestart": "Válassza ki az újraindítandó feladatot!", - "TaskService.defaultBuildTaskExists": "A(z) {0} már meg van jelölve alapértelmezett buildelési feladatként.", "TaskService.pickDefaultBuildTask": "Válassza ki az alpértelmezett buildelési feladatként használt feladatot!", "TaskService.defaultTestTaskExists": "A(z) {0} már meg van jelölve alapértelmezett tesztelési feladatként.", "TaskService.pickDefaultTestTask": "Válassza ki az alpértelmezett tesztelési feladatként használt feladatot!", - "TaskService.noTaskIsRunning": "Nem fut feladat.", "TaskService.pickShowTask": "Válassza ki a feladatot a kimenet megjelenítéséhez!", "ShowLogAction.label": "Feladatnapló megtekintése", "RunTaskAction.label": "Feladat futtatása", diff --git a/i18n/hun/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json b/i18n/hun/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json index cbc547d3e4a..496170af36e 100644 --- a/i18n/hun/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json +++ b/i18n/hun/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json @@ -5,6 +5,7 @@ // Do not edit this file. It is machine generated. { "TerminalTaskSystem.unknownError": "Ismeretlen hiba történt a feladat végrehajtása közben. Részletek a feladat kimeneti naplójában találhatók.", + "dependencyFailed": "Nem sikerült feloldani a(z) '{0}' függő feladatot a(z) '{1}' munkaterületi mappában", "TerminalTaskSystem.terminalName": "Feladat – {0}", "reuseTerminal": "A terminál újra lesz hasznosítva a feladatok által. Nyomjon meg egy billentyűt a bezáráshoz.", "TerminalTaskSystem": "Rendszerparancsok nem hajthatók végre UNC-meghajtókon.", diff --git a/i18n/hun/src/vs/workbench/services/configuration/node/configuration.i18n.json b/i18n/hun/src/vs/workbench/services/configuration/node/configuration.i18n.json index 4ec73bfcb21..3936a1aba7b 100644 --- a/i18n/hun/src/vs/workbench/services/configuration/node/configuration.i18n.json +++ b/i18n/hun/src/vs/workbench/services/configuration/node/configuration.i18n.json @@ -14,7 +14,9 @@ "vscode.extension.contributes.defaultConfiguration": "Adott nyelvre vonatkozóan szerkesztőbeállításokat szolgáltat.", "invalid.properties": "A 'configuration.properties' értékét egy objektumként kell megadni", "invalid.allOf": "A 'configuration.allOf' elavult, és használata nem javasolt. Helyette több konfigurációs szakaszt kell átadni tömbként a 'configuration' értékeként.", - "workspaceConfig.folders.description": "A munkaterületre betöltött mappák listája. Elérési útnak kell lennie, pl. `/root/folderA` vagy `./folderA` relatív elérési út esetén, ami a munkaterületfájl helye alapján lesz feloldva.", - "workspaceConfig.folder.description": "Egy fájl elérési útja, pl. `/root/folderA` vagy `./folderA` relatív elérési út esetén, ami a munkaterületfájl helye alapján lesz feloldva.", - "workspaceConfig.settings.description": "Munkaterület-beállítások" + "workspaceConfig.path.description": "Egy fájl elérési útja, pl. `/root/folderA` vagy `./folderA` relatív elérési út esetén, ami a munkaterületfájl helye alapján lesz feloldva.", + "workspaceConfig.name.description": "A mappa neve. Nem kötelező megadni.", + "workspaceConfig.uri.description": "A mappa URI-ja", + "workspaceConfig.settings.description": "Munkaterület-beállítások", + "workspaceConfig.extensions.description": "Munkaterület-kiegészítők" } \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/services/files/electron-browser/remoteFileService.i18n.json b/i18n/hun/src/vs/workbench/services/files/electron-browser/remoteFileService.i18n.json new file mode 100644 index 00000000000..f77948f5276 --- /dev/null +++ b/i18n/hun/src/vs/workbench/services/files/electron-browser/remoteFileService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileBinaryError": "A fájl binárisnak tűnik és nem nyitható meg szövegként" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/services/files/node/fileService.i18n.json b/i18n/hun/src/vs/workbench/services/files/node/fileService.i18n.json index 94c9d3c7732..5dbccfabb8d 100644 --- a/i18n/hun/src/vs/workbench/services/files/node/fileService.i18n.json +++ b/i18n/hun/src/vs/workbench/services/files/node/fileService.i18n.json @@ -10,6 +10,7 @@ "fileTooLargeError": "A fájl túl nagy a megnyitáshoz", "fileBinaryError": "A fájl binárisnak tűnik és nem nyitható meg szövegként", "fileNotFoundError": "Fájl nem található ({0})", + "fileExists": "A létrehozandó fájl már létezik ({0})", "fileMoveConflict": "Nem lehet áthelyezni vagy másolni. A fájl már létezik a célhelyen.", "unableToMoveCopyError": "Nem lehet áthelyezni vagy másolni. A fájl felülírná a mappát, amiben található.", "foldersCopyError": "A munkaterületre nem másolhatók mappák. Válasszon ki egyedi fájlokat a másoláshoz.", diff --git a/i18n/ita/src/vs/code/electron-main/menus.i18n.json b/i18n/ita/src/vs/code/electron-main/menus.i18n.json index fbec1e5bf9c..d950c064592 100644 --- a/i18n/ita/src/vs/code/electron-main/menus.i18n.json +++ b/i18n/ita/src/vs/code/electron-main/menus.i18n.json @@ -171,8 +171,6 @@ "miRunningTask": "Mostra attività in esec&&uzione...", "miRestartTask": "Ria&&vvia attività in esecuzione...", "miTerminateTask": "&&Termina attività...", - "miConfigureTask": "&&Configura attività", - "miConfigureBuildTask": "Configura atti&&vità di compilazione predefinita", "accessibilityOptionsWindowTitle": "Opzioni accessibilità", "miRestartToUpdate": "Riavvia per aggiornare...", "miCheckingForUpdates": "Verifica della disponibilità di aggiornamenti...", diff --git a/i18n/ita/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json b/i18n/ita/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json index 2c46cc8fbb5..943d334ccda 100644 --- a/i18n/ita/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json +++ b/i18n/ita/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json @@ -5,10 +5,7 @@ // Do not edit this file. It is machine generated. { "invalidManifest": "Estensione non valida: package.json non è un file JSON.", - "restartCode": "Riavviare Code prima di reinstallare {0}.", - "installDependeciesConfirmation": "Se si installa '{0}', verranno installate anche le relative dipendenze. Continuare?", - "install": "Sì", - "doNotInstall": "No", + "restartCodeLocal": "Riavviare Code prima di reinstallare {0}.", "uninstallDependeciesConfirmation": "Disinstallare solo '{0}' o anche le relative dipendenze?", "uninstallOnly": "Solo", "uninstallAll": "Tutto", diff --git a/i18n/ita/src/vs/workbench/browser/actions/workspaceActions.i18n.json b/i18n/ita/src/vs/workbench/browser/actions/workspaceActions.i18n.json index 86e9486ae88..0c7b03c458a 100644 --- a/i18n/ita/src/vs/workbench/browser/actions/workspaceActions.i18n.json +++ b/i18n/ita/src/vs/workbench/browser/actions/workspaceActions.i18n.json @@ -14,7 +14,6 @@ "selectWorkspace": "Seleziona cartelle per l'area di lavoro", "removeFolderFromWorkspace": "Rimuovi cartella dall'area di lavoro", "saveWorkspaceAsAction": "Salva area di lavoro come...", - "saveEmptyWorkspaceNotSupported": "Aprire prima un'area di lavoro da salvare.", "save": "&&Salva", "saveWorkspace": "Salva area di lavoro", "openWorkspaceAction": "Apri area di lavoro...", diff --git a/i18n/ita/src/vs/workbench/browser/parts/views/panelViewlet.i18n.json b/i18n/ita/src/vs/workbench/browser/parts/views/panelViewlet.i18n.json new file mode 100644 index 00000000000..a7b1c661726 --- /dev/null +++ b/i18n/ita/src/vs/workbench/browser/parts/views/panelViewlet.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "viewToolbarAriaLabel": "Azioni di {0}" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json b/i18n/ita/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json index f3e714c0677..fe25758722c 100644 --- a/i18n/ita/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json +++ b/i18n/ita/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json @@ -51,9 +51,8 @@ "showLanguageExtensionsShort": "Estensioni del linguaggio", "showAzureExtensions": "Mostra estensioni di Azure", "showAzureExtensionsShort": "Estensioni di Azure", - "configureWorkspaceRecommendedExtensions": "Configura estensioni consigliate (area di lavoro)", - "ConfigureWorkspaceRecommendations.noWorkspace": "Gli elementi consigliati sono disponibili solo per una cartella dell'area di lavoro.", "OpenExtensionsFile.failed": "Non è possibile creare il file 'extensions.json' all'interno della cartella '.vscode' ({0}).", + "configureWorkspaceRecommendedExtensions": "Configura estensioni consigliate (area di lavoro)", "builtin": "Predefinita", "disableAll": "Disabilita tutte le estensioni installate", "disableAllWorkspace": "Disabilita tutte le estensioni installate per questa area di lavoro", diff --git a/i18n/ita/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json b/i18n/ita/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json index b5f2514add4..a5a6e8a1bba 100644 --- a/i18n/ita/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json +++ b/i18n/ita/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json @@ -9,7 +9,6 @@ "openGlobalKeybindingsFile": "Apri file dei tasti di scelta rapida", "openWorkspaceSettings": "Apri impostazioni area di lavoro", "openFolderSettings": "Apri impostazioni cartella", - "pickFolder": "Seleziona cartella", "configureLanguageBasedSettings": "Configura impostazioni specifiche del linguaggio...", "languageDescriptionConfigured": "({0})", "pickLanguage": "Seleziona linguaggio" diff --git a/i18n/ita/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json b/i18n/ita/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json index 0a301a74cb4..fa6e01989cf 100644 --- a/i18n/ita/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json +++ b/i18n/ita/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json @@ -6,7 +6,5 @@ { "relaunchSettingMessage": "È necessario riavviare per rendere effettiva un'impostazione modificata.", "relaunchSettingDetail": "Fare clic sul pulsante di riavvio per riavviare {0} e abilitare l'impostazione.", - "restart": "Riavvia", - "relaunchWorkspaceMessage": "Questo cambiamento di area di lavoro richiede un ricaricamento del nostro sistema di estensione.", - "reload": "Ricarica" + "restart": "Riavvia" } \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json b/i18n/ita/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json index 2d3712bd868..1cb08dc3cfc 100644 --- a/i18n/ita/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json +++ b/i18n/ita/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json @@ -5,13 +5,7 @@ // Do not edit this file. It is machine generated. { "tasksCategory": "Attività", - "ConfigureTaskRunnerAction.noWorkspace": "Le attività sono disponibili solo per una cartella dell'area di lavoro.", - "ConfigureTaskRunnerAction.quickPick.template": "Seleziona strumento di esecuzione attività", - "ConfigureTaskRunnerAction.autoDetecting": "Rilevamento automatico delle attività per {0}", - "ConfigureTaskRunnerAction.autoDetect": "Il rilevamento automatico del sistema dell'attività non è riuscito. Verrà usato il modello predefinito. Per i dettagli, vedere l'output dell'attività.", - "ConfigureTaskRunnerAction.autoDetectError": "Il rilevamento automatico del sistema dell'attività ha restituito errori. Per i dettagli, vedere l'output dell'attività.", - "ConfigureTaskRunnerAction.failed": "Non è possibile creare il file 'tasks.json' all'interno della cartella '.vscode'. Per dettagli, vedere l'output dell'attività.", - "ConfigureTaskRunnerAction.label": "Configura esecuzione attività", + "ConfigureTaskRunnerAction.label": "Configura attività", "ConfigureBuildTaskAction.label": "Configura attività di compilazione", "CloseMessageAction.label": "Chiudi", "ShowTerminalAction.label": "Visualizza terminale", @@ -47,22 +41,16 @@ "configured": "attività configurate", "detected": "attività rilevate", "TaskService.fetchingBuildTasks": "Recupero delle attività di compilazione...", - "TaskService.noBuildTaskTerminal": "Non è stata trovata alcuna attività di compilazione. Fare clic su 'Configura attività di compilazione' per definirne una.", "TaskService.pickBuildTask": "Selezionare l'attività di compilazione da eseguire", "TaskService.fetchingTestTasks": "Recupero delle attività di test...", - "TaskService.noTestTaskTerminal": "Non è stata trovata alcuna attività di test. Fare clic su 'Configura Test Runner' per definirne una.", "TaskService.pickTestTask": "Selezionare l'attività di test da eseguire", - "TaskService.noTaskRunning": "Non ci sono attività attualmente in esecuzione.", "TaskService.tastToTerminate": "Selezionare l'attività da terminare", "TerminateAction.noProcess": "Il processo avviato non esiste più. Se l'attività implica la generazione di attività in background, uscendo da Visual Studio Code potrebbero essere presenti processi orfani.", "TerminateAction.failed": "Non è stato possibile terminare l'attività in esecuzione", - "TaskService.noTaskToRestart": "Non ci sono attività da riavviare.", "TaskService.tastToRestart": "Selezionare l'attività da riavviare", - "TaskService.defaultBuildTaskExists": "{0} è già contrassegnato come attività di compilazione predefinita.", "TaskService.pickDefaultBuildTask": "Selezionare l'attività da usare come attività di compilazione predefinita", "TaskService.defaultTestTaskExists": "{0} è già contrassegnato come attività di test predefinita.", "TaskService.pickDefaultTestTask": "Selezionare l'attività da usare come attività di test predefinita", - "TaskService.noTaskIsRunning": "Nessuna attività è in esecuzione.", "TaskService.pickShowTask": "Selezionare l'attività di cui mostrare l'output", "ShowLogAction.label": "Mostra log attività", "RunTaskAction.label": "Esegui attività", diff --git a/i18n/ita/src/vs/workbench/services/configuration/node/configuration.i18n.json b/i18n/ita/src/vs/workbench/services/configuration/node/configuration.i18n.json index ccaa20ccc11..2a16527c203 100644 --- a/i18n/ita/src/vs/workbench/services/configuration/node/configuration.i18n.json +++ b/i18n/ita/src/vs/workbench/services/configuration/node/configuration.i18n.json @@ -13,7 +13,6 @@ "invalid.title": "'configuration.title' deve essere una stringa", "vscode.extension.contributes.defaultConfiguration": "Aggiunge come contributo le impostazioni di configurazione predefinite dell'editor in base al linguaggio.", "invalid.properties": "'configuration.properties' deve essere un oggetto", - "workspaceConfig.folders.description": "Elenco delle cartelle da caricare nell'area di lavoro. Deve essere un percorso di file, ad esempio `/root/folderA` o `./folderA` per un percorso relativo che verrà risolto in base alla posizione del file dell'area di lavoro.", - "workspaceConfig.folder.description": "Percorso di file, ad esempio `/root/folderA` o `./folderA` per un percorso relativo che verrà risolto in base alla posizione del file dell'area di lavoro.", + "workspaceConfig.path.description": "Percorso di file, ad esempio `/root/folderA` o `./folderA` per un percorso relativo che verrà risolto in base alla posizione del file dell'area di lavoro.", "workspaceConfig.settings.description": "Impostazioni di area di lavoro" } \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/services/files/electron-browser/remoteFileService.i18n.json b/i18n/ita/src/vs/workbench/services/files/electron-browser/remoteFileService.i18n.json new file mode 100644 index 00000000000..6952c245dd7 --- /dev/null +++ b/i18n/ita/src/vs/workbench/services/files/electron-browser/remoteFileService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileBinaryError": "Il file sembra essere binario e non può essere aperto come file di testo" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/css/package.i18n.json b/i18n/jpn/extensions/css/package.i18n.json index e24d5a99b9b..5ff9ba2550a 100644 --- a/i18n/jpn/extensions/css/package.i18n.json +++ b/i18n/jpn/extensions/css/package.i18n.json @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { + "css.title": "CSS", "css.lint.argumentsInColorFunction.desc": "正しくないパラメーターの数", "css.lint.boxModel.desc": "パディングまたは枠線を使用する場合は幅または高さを使用しないでください", "css.lint.compatibleVendorPrefixes.desc": "ベンダー固有のプレフィックスを使用する場合は、他のすべてのベンダー固有のプロパティも必ず含めてください", @@ -25,6 +26,7 @@ "css.trace.server.desc": "VS Code と CSS 言語サーバー間の通信をトレースします。", "css.validate.title": "CSS の検証と問題の重大度を制御します。", "css.validate.desc": "すべての検証を有効または無効にします", + "less.title": "LESS", "less.lint.argumentsInColorFunction.desc": "正しくないパラメーターの数", "less.lint.boxModel.desc": "パディングまたは枠線を使用する場合は幅または高さを使用しないでください", "less.lint.compatibleVendorPrefixes.desc": "ベンダー固有のプレフィックスを使用する場合は、他のすべてのベンダー固有のプロパティも必ず含めてください", @@ -45,6 +47,7 @@ "less.lint.zeroUnits.desc": "0 の単位は必要ありません", "less.validate.title": "LESS の検証と問題の重大度を制御します。", "less.validate.desc": "すべての検証を有効または無効にします", + "scss.title": "SCSS (Sass)", "scss.lint.argumentsInColorFunction.desc": "正しくないパラメーターの数", "scss.lint.boxModel.desc": "パディングまたは枠線を使用する場合は幅または高さを使用しないでください", "scss.lint.compatibleVendorPrefixes.desc": "ベンダー固有のプレフィックスを使用する場合は、他のすべてのベンダー固有のプロパティも必ず含めてください", diff --git a/i18n/jpn/extensions/git/out/commands.i18n.json b/i18n/jpn/extensions/git/out/commands.i18n.json index 072e2fc7089..af7b32febd1 100644 --- a/i18n/jpn/extensions/git/out/commands.i18n.json +++ b/i18n/jpn/extensions/git/out/commands.i18n.json @@ -34,7 +34,7 @@ "delete files": "複数のファイルを削除", "there are untracked files single": "破棄すると次の未追跡ファイルがディスクから削除されます: {0}。", "there are untracked files": "破棄すると {0} 個の未追跡ファイルがディスクから削除されます。", - "confirm discard all 2": "{0}\n\nこの変更は元に戻すことはできません、現在のワーキング セットは永久に失われます。", + "confirm discard all 2": "{0}\n\nこの変更は元に戻すことはできません。現在のワーキング セットは永久に失われます。", "yes discard tracked": "1 つの追跡ファイルを破棄", "yes discard tracked multiple": "{0} 個の追跡ファイルを破棄", "no staged changes": "コミットするステージされた変更がありません。\n\nすべての変更を自動的にステージして、直接コミットしますか?", diff --git a/i18n/jpn/src/vs/code/electron-main/menus.i18n.json b/i18n/jpn/src/vs/code/electron-main/menus.i18n.json index fc66e6ab241..a4f82d97faf 100644 --- a/i18n/jpn/src/vs/code/electron-main/menus.i18n.json +++ b/i18n/jpn/src/vs/code/electron-main/menus.i18n.json @@ -151,6 +151,10 @@ "mZoom": "ズーム", "mBringToFront": "すべてを前面に配置", "miSwitchWindow": "ウィンドウの切り替え(&&W)...", + "mShowPreviousTab": "前のタブを表示", + "mShowNextTab": "次のタブを表示", + "mMoveTabToNewWindow": "タブを新しいウィンドウに移動", + "mMergeAllWindows": "すべてのウィンドウを統合", "miToggleDevTools": "開発者ツールの切り替え(&&T)", "miAccessibilityOptions": "ユーザー補助オプション(&&O)", "miReportIssues": "問題の報告(&&I)", @@ -171,8 +175,6 @@ "miRunningTask": "実行中のタスクを表示(&&G)...", "miRestartTask": "実行中のタスクの再起動(&&E)...", "miTerminateTask": "タスクの終了(&&T)...", - "miConfigureTask": "タスクの構成(&&C)", - "miConfigureBuildTask": "既定のビルド タスクの構成(&&F)", "accessibilityOptionsWindowTitle": "ユーザー補助オプション", "miRestartToUpdate": "再起動して更新...", "miCheckingForUpdates": "更新を確認しています...", diff --git a/i18n/jpn/src/vs/code/electron-main/windows.i18n.json b/i18n/jpn/src/vs/code/electron-main/windows.i18n.json index 83ca2a71403..7558f5d726f 100644 --- a/i18n/jpn/src/vs/code/electron-main/windows.i18n.json +++ b/i18n/jpn/src/vs/code/electron-main/windows.i18n.json @@ -17,6 +17,8 @@ "open": "開く", "openFolder": "フォルダーを開く", "openFile": "ファイルを開く", + "workspaceOpenedMessage": "ワークスペース '{0}' を保存できません", + "workspaceOpenedDetail": "ワークスペースは既に別のウィンドウで開いています。最初にそのウィンドウを閉じててから、もう一度やり直してください。", "openWorkspace": "開く(&&O)", "openWorkspaceTitle": "ワークスペースを開く", "save": "保存(&&S)", diff --git a/i18n/jpn/src/vs/editor/common/config/commonEditorConfig.i18n.json b/i18n/jpn/src/vs/editor/common/config/commonEditorConfig.i18n.json index 28b5711561e..25be0c66adb 100644 --- a/i18n/jpn/src/vs/editor/common/config/commonEditorConfig.i18n.json +++ b/i18n/jpn/src/vs/editor/common/config/commonEditorConfig.i18n.json @@ -88,6 +88,7 @@ "accessibilitySupport": "エディターをスクリーン リーダーに最適化されたモードで実行するかどうかを制御します。", "links": "エディターがリンクを検出してクリック可能な状態にするかどうかを制御します", "colorDecorators": "エディターでインライン カラー デコレーターと色の選択を表示する必要があるかどうかを制御します。", + "codeActions": "コード アクション (lightbulb) を有効にする", "sideBySide": "差分エディターが差分を横に並べて表示するか、行内に表示するかを制御します", "ignoreTrimWhitespace": "差分エディターが、先頭または末尾の空白の変更を差分として表示するかどうかを制御します。", "renderIndicators": "差分エディターが追加/削除された変更に +/- インジケーターを示すかどうかを制御します", diff --git a/i18n/jpn/src/vs/platform/environment/node/argv.i18n.json b/i18n/jpn/src/vs/platform/environment/node/argv.i18n.json index 65f0ae56870..1545142948e 100644 --- a/i18n/jpn/src/vs/platform/environment/node/argv.i18n.json +++ b/i18n/jpn/src/vs/platform/environment/node/argv.i18n.json @@ -15,7 +15,7 @@ "reuseWindow": "最後のアクティブ ウィンドウにファイルまたはフォルダーを強制的に開きます。", "userDataDir": "ユーザー データを保持するディレクトリを指定します。ルートで実行している場合に役立ちます。", "verbose": "詳細出力を表示します (--wait を含みます)。", - "wait": "戻る前にファイルが閉じるまでお待ちください。", + "wait": "現在のファイルが閉じられるまで待機します。", "extensionHomePath": "拡張機能のルート パスを設定します。", "listExtensions": "インストールされている拡張機能を一覧表示します。", "showVersions": "--list-extension と使用するとき、インストールされている拡張機能のバージョンを表示します。", diff --git a/i18n/jpn/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json b/i18n/jpn/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json index 2753d38a6d2..4b1099e0421 100644 --- a/i18n/jpn/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json +++ b/i18n/jpn/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json @@ -5,10 +5,7 @@ // Do not edit this file. It is machine generated. { "invalidManifest": "正しくない拡張機能: package.json は JSON ファイルではありません。", - "restartCode": "{0} を再インストールする前に、Code を再起動してください。", - "installDependeciesConfirmation": "'{0}' をインストールすると、その依存関係もインストールされます。続行してもよろしいですか?", - "install": "はい", - "doNotInstall": "いいえ", + "restartCodeLocal": "{0} を再インストールする前に、Code を再起動してください。", "uninstallDependeciesConfirmation": "'{0}' のみをアンインストールしますか、または依存関係もアンインストールしますか?", "uninstallOnly": "限定", "uninstallAll": "すべて", diff --git a/i18n/jpn/src/vs/workbench/browser/actions/workspaceActions.i18n.json b/i18n/jpn/src/vs/workbench/browser/actions/workspaceActions.i18n.json index a93dd60c185..6d5c23d570a 100644 --- a/i18n/jpn/src/vs/workbench/browser/actions/workspaceActions.i18n.json +++ b/i18n/jpn/src/vs/workbench/browser/actions/workspaceActions.i18n.json @@ -14,7 +14,6 @@ "selectWorkspace": "ワークスペースのフォルダーを選択", "removeFolderFromWorkspace": "ワークスペースからフォルダーを削除", "saveWorkspaceAsAction": "名前を付けてワークスペースを保存...", - "saveEmptyWorkspaceNotSupported": "最初に保存するためのワークスペースを開いてください。", "save": "保存(&&S)", "saveWorkspace": "ワークスペースを保存", "openWorkspaceAction": "ワークスペースを開く...", diff --git a/i18n/jpn/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json b/i18n/jpn/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json index 4a9a751d98c..8cee18d4c69 100644 --- a/i18n/jpn/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json +++ b/i18n/jpn/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json @@ -6,6 +6,7 @@ { "badgeTitle": "{0} - {1}", "titleKeybinding": "{0} ({1})", + "removeFromActivityBar": "アクティビティ バーから非表示", "keepInActivityBar": "アクティビティ バーに保持", "additionalViews": "その他のビュー", "numberBadge": "{0} ({1})", diff --git a/i18n/jpn/src/vs/workbench/browser/parts/editor/editorActions.i18n.json b/i18n/jpn/src/vs/workbench/browser/parts/editor/editorActions.i18n.json index d1ccb18830d..5c802777acf 100644 --- a/i18n/jpn/src/vs/workbench/browser/parts/editor/editorActions.i18n.json +++ b/i18n/jpn/src/vs/workbench/browser/parts/editor/editorActions.i18n.json @@ -35,6 +35,7 @@ "openPreviousEditorInGroup": "グループ内で前のエディターを開く", "navigateNext": "次に進む", "navigatePrevious": "前に戻る", + "navigateLast": "戻る", "reopenClosedEditor": "閉じたエディターを再度開く", "clearRecentFiles": "最近開いた項目をクリア", "showEditorsInFirstGroup": "最初のグループのエディターを表示する", diff --git a/i18n/jpn/src/vs/workbench/browser/parts/views/panelViewlet.i18n.json b/i18n/jpn/src/vs/workbench/browser/parts/views/panelViewlet.i18n.json new file mode 100644 index 00000000000..6bcbcb522f9 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/browser/parts/views/panelViewlet.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "viewToolbarAriaLabel": "{0} 個のアクション" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/electron-browser/actions.i18n.json b/i18n/jpn/src/vs/workbench/electron-browser/actions.i18n.json index 8938e8dd9e8..a31924013b2 100644 --- a/i18n/jpn/src/vs/workbench/electron-browser/actions.i18n.json +++ b/i18n/jpn/src/vs/workbench/electron-browser/actions.i18n.json @@ -42,5 +42,10 @@ "navigateUp": "上のビュー部分に移動", "navigateDown": "下のビュー部分に移動", "increaseViewSize": "現在のビューのサイズの拡大", - "decreaseViewSize": "現在のビューのサイズの縮小" + "decreaseViewSize": "現在のビューのサイズの縮小", + "showPreviousTab": "前のウィンドウ タブを表示", + "showNextWindowTab": "次のウィンドウ タブを表示", + "moveWindowTabToNewWindow": "ウィンドウ タブを新しいウィンドウに移動", + "mergeAllWindowTabs": "すべてのウィンドウを統合", + "toggleWindowTabsBar": "ウィンドウ タブ バーの切り替え" } \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/electron-browser/main.contribution.i18n.json b/i18n/jpn/src/vs/workbench/electron-browser/main.contribution.i18n.json index 6061715ea0e..903ec7ec36c 100644 --- a/i18n/jpn/src/vs/workbench/electron-browser/main.contribution.i18n.json +++ b/i18n/jpn/src/vs/workbench/electron-browser/main.contribution.i18n.json @@ -10,6 +10,11 @@ "workspaces": "ワークスペース", "developer": "開発者", "showEditorTabs": "開いているエディターをタブに表示するかどうかを制御します。", + "workbench.editor.labelFormat.default": "ファイルの名前を表示します。タブが有効かつ1 つのグループの2 つの同名ファイルに各ファイルのパスの区切り記号が追加されます。タブを無効にすると、エディターがアクティブな時にワークスペースのルートへの相対パスが表示されます。", + "workbench.editor.labelFormat.short": "ディレクトリ名に続けてファイル名を表示します。", + "workbench.editor.labelFormat.medium": "ワークスペース ルートからの相対パスに続けてファイル名を表示します。", + "workbench.editor.labelFormat.long": "絶対パスに続けてファイル名を表示します。", + "tabDescription": "エディターのラベルの書式を制御します。例としてこの設定を変更することでファイルの場所を理解しやすくなります:\n- short: 'parent'\n- medium: 'workspace/src/parent'\n- long: '/home/user/workspace/src/parent'\n- default: '.../parent',  別タブで、同じタイトルを共有する場合や、相対的なワークスペース パス タブが無効になっている場合", "editorTabCloseButton": "エディター タブの閉じるボタンの位置を制御するか、[off] に設定した場合に無効にします。", "showIcons": "開いているエディターをアイコンで表示するかどうかを制御します。これには、アイコンのテーマを有効にする必要もあります。", "enablePreview": "開いているエディターをプレビューとして表示するかどうかを制御します。プレビュー エディターは、保持されている間、再利用されます (ダブルクリックまたは編集などによって)。", diff --git a/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json b/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json index 6754c23d67a..e47e23b4326 100644 --- a/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json +++ b/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json @@ -12,6 +12,8 @@ "breakpointRemoved": "ブレークポイントを削除しました。行 {0}、ファイル {1}", "compoundMustHaveConfigurations": "複合構成を開始するには、複合に \"configurations\" 属性が設定されている必要があります。", "configMissing": "構成 '{0}' が 'launch.json' 内にありません。", + "debugRequestNotSupported": "構成されているデバッグ要求の '{0}' はサポートされていません", + "debugRequesMissing": "選択している起動構成にデバッグ要求が含まれていません", "debugTypeNotSupported": "構成されているデバッグの種類 '{0}' はサポートされていません。", "debugTypeMissing": "選択された起動構成のプロパティ 'type' がありません。", "preLaunchTaskErrors": "preLaunchTask '{0}' の実行中にビルド エラーが検出されました。", diff --git a/i18n/jpn/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json b/i18n/jpn/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json index 6681f4c4778..16b98008acb 100644 --- a/i18n/jpn/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json +++ b/i18n/jpn/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json @@ -29,7 +29,13 @@ "view id": "ID", "view name": "名前", "view location": "場所", + "colorThemes": "配色テーマ ({0})", + "iconThemes": "アイコン テーマ ({0})", + "colors": "配色 ({0})", "colorId": "Id", + "defaultDark": "ダーク テーマの既定値", + "defaultLight": "ライト テーマの既定値", + "defaultHC": "ハイ コントラストの既定値", "JSON Validation": "JSON 検証 ({0})", "commands": "コマンド ({0})", "command name": "名前", diff --git a/i18n/jpn/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json b/i18n/jpn/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json index 473ed0e7c04..d53e187ce18 100644 --- a/i18n/jpn/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json +++ b/i18n/jpn/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json @@ -51,9 +51,8 @@ "showLanguageExtensionsShort": "言語の拡張機能", "showAzureExtensions": "Azure 拡張機能の表示", "showAzureExtensionsShort": "Azure 拡張機能", - "configureWorkspaceRecommendedExtensions": "お勧めの拡張機能の構成 (ワークスペース)", - "ConfigureWorkspaceRecommendations.noWorkspace": "推奨事項はワークスペース フォルダーでのみ利用可能です。", "OpenExtensionsFile.failed": "'.vscode' ファルダー ({0}) 内に 'extensions.json' ファイルを作成できません。", + "configureWorkspaceRecommendedExtensions": "お勧めの拡張機能の構成 (ワークスペース)", "builtin": "ビルトイン", "disableAll": "インストール済みのすべての拡張機能を無効にする", "disableAllWorkspace": "このワークスペースのインストール済みの拡張機能をすべて無効にする", diff --git a/i18n/jpn/src/vs/workbench/parts/files/browser/files.contribution.i18n.json b/i18n/jpn/src/vs/workbench/parts/files/browser/files.contribution.i18n.json index 9cfd4305ea8..2729f6aada9 100644 --- a/i18n/jpn/src/vs/workbench/parts/files/browser/files.contribution.i18n.json +++ b/i18n/jpn/src/vs/workbench/parts/files/browser/files.contribution.i18n.json @@ -14,6 +14,8 @@ "files.exclude.boolean": "ファイル パスの照合基準となる glob パターン。これを true または false に設定すると、パターンがそれぞれ有効/無効になります。", "files.exclude.when": "一致するファイルの兄弟をさらにチェックします。一致するファイル名の変数として $(basename) を使用します。", "associations": "言語に対するファイルの関連付け (例 \"*.extension\": \"html\") を構成します。これらの関連付けは、インストールされている言語の既定の関連付けより優先されます。", + "encoding": "ファイルの読み取り/書き込みで使用する既定の文字セット エンコーディング。言語ごとに構成することも可能です。", + "autoGuessEncoding": "有効な場合、ファイルを開くときに文字セット エンコードを推測します。言語ごとに構成することも可能です。", "eol": "既定の改行文字。LF の場合には \\n を CRLF の場合には \\r\\n を使用してください。", "trimTrailingWhitespace": "有効にすると、ファイルの保存時に末尾の空白をトリミングします。", "insertFinalNewline": "有効にすると、ファイルの保存時に最新の行を末尾に挿入します。", diff --git a/i18n/jpn/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json b/i18n/jpn/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json index e8bcfe52d77..a21e553496c 100644 --- a/i18n/jpn/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json +++ b/i18n/jpn/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json @@ -4,5 +4,6 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { + "dirtyFile": "1 つの未保存のファイル", "dirtyFiles": "{0} 個の未保存のファイル" } \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json b/i18n/jpn/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json index c1aa63ce163..3257770db15 100644 --- a/i18n/jpn/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json +++ b/i18n/jpn/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json @@ -9,7 +9,6 @@ "openGlobalKeybindingsFile": "キーボード ショートカット ファイルを開く", "openWorkspaceSettings": "ワークスペース設定を開く", "openFolderSettings": "フォルダーの設定を開く", - "pickFolder": "フォルダーの選択", "configureLanguageBasedSettings": "言語固有の設定を構成します...", "languageDescriptionConfigured": "({0})", "pickLanguage": "言語の選択" diff --git a/i18n/jpn/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json b/i18n/jpn/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json index 34db8f687ec..52988cf3a91 100644 --- a/i18n/jpn/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json +++ b/i18n/jpn/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json @@ -6,7 +6,5 @@ { "relaunchSettingMessage": "再起動が必要な設定を変更しました。", "relaunchSettingDetail": "{0} を再起動ボタンで再起動して、設定を有効にしてください。", - "restart": "再起動", - "relaunchWorkspaceMessage": "このワークスペースの変更には、拡張システムの再読み込みが必要です。", - "reload": "再読み込み" + "restart": "再起動" } \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json b/i18n/jpn/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json index c3de7d0fb34..cc309b1f286 100644 --- a/i18n/jpn/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json +++ b/i18n/jpn/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json @@ -4,5 +4,7 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "snippet.suggestions.label": "スニペットの挿入" + "snippet.suggestions.label": "スニペットの挿入", + "sep.userSnippet": "ユーザー スニペット", + "sep.extSnippet": "拡張機能のスニペット" } \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json b/i18n/jpn/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json index 5543f202fbd..adb4b5b9a1d 100644 --- a/i18n/jpn/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json +++ b/i18n/jpn/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json @@ -10,6 +10,7 @@ "vscode.extension.contributes.snippets": "スニペットを提供します。", "vscode.extension.contributes.snippets-language": "このスニペットの提供先の言語識別子です。", "vscode.extension.contributes.snippets-path": "スニペット ファイルのパス。拡張機能フォルダーの相対パスであり、通常 './snippets/' で始まります。", + "badFile": "スニペット ファイル \"{0}\" を読み込むことができませんでした。", "badVariableUse": "スニペット \"{0}\" は、スニペット変数とスニペット プレースホルダーを混乱させる可能性が非常にあります。詳細については https://code.visualstudio.com/docs/editor/userdefinedsnippets#_snippet-syntax をご覧ください。", "source.snippet": "ユーザー スニペット", "detail.snippet": "{0} ({1})", diff --git a/i18n/jpn/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json b/i18n/jpn/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json index 2308da613b4..ff372f92e2c 100644 --- a/i18n/jpn/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json +++ b/i18n/jpn/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json @@ -5,13 +5,7 @@ // Do not edit this file. It is machine generated. { "tasksCategory": "タスク", - "ConfigureTaskRunnerAction.noWorkspace": "タスクはワークスペース フォルダーでのみ利用可能です。", - "ConfigureTaskRunnerAction.quickPick.template": "タスク ランナーを選択", - "ConfigureTaskRunnerAction.autoDetecting": "{0} のタスクを自動検出", - "ConfigureTaskRunnerAction.autoDetect": "タスク システムの自動検出が失敗しました。既定のテンプレートを使用しています。詳細については、タスク出力を参照してください", - "ConfigureTaskRunnerAction.autoDetectError": "タスク システムの自動検出でエラーが発生しました。詳細については、タスク出力を参照してください。", - "ConfigureTaskRunnerAction.failed": "'.vscode' フォルダー内に 'tasks.json' ファイルを作成できません。詳細については、タスク出力を参照してください。", - "ConfigureTaskRunnerAction.label": "タスク ランナーの構成", + "ConfigureTaskRunnerAction.label": "タスクの構成", "ConfigureBuildTaskAction.label": "ビルド タスクを構成します", "CloseMessageAction.label": "閉じる", "ShowTerminalAction.label": "ターミナルの表示", @@ -19,6 +13,7 @@ "manyMarkers": "99+", "runningTasks": "実行中のタスクを表示", "tasks": "タスク", + "TaskSystem.noHotSwap": "アクティブなタスクを実行しているタスク実行エンジンを変更するには、ウィンドウの再読み込みが必要です", "TaskService.noBuildTask1": "ビルド タスクが定義されていません。tasks.json ファイルでタスクに 'isBuildCommand' というマークを付けてください。", "TaskService.noBuildTask2": "ビルド タスクが定義されていません。tasks.json ファイルでタスクに 'build' グループとしてマークを付けてください。", "TaskService.noTestTask1": "テスト タスクが定義されていません。tasks.json ファイルでタスクに 'isTestCommand' というマークを付けてください。", @@ -47,22 +42,16 @@ "configured": "構成済みのタスク", "detected": "検出されたタスク", "TaskService.fetchingBuildTasks": "ビルド タスクをフェッチしています...", - "TaskService.noBuildTaskTerminal": "ビルド タスクが見つかりません。定義するには 'Configure Build Task' (ビルド タスクを構成します) を押してください。", "TaskService.pickBuildTask": "実行するビルド タスクを選択", "TaskService.fetchingTestTasks": "テスト タスクをフェッチしています...", - "TaskService.noTestTaskTerminal": "テスト タスクが見つかりません。定義するには 'Configure Build Task' (タスク ランナーの構成) を押してください。", "TaskService.pickTestTask": "実行するテスト タスクを選択してください", - "TaskService.noTaskRunning": "現在実行中のタスクはありません。", "TaskService.tastToTerminate": "終了するタスクを選択", "TerminateAction.noProcess": "起動したプロセスは既に存在しません。タスクを起動したバックグラウンド タスクが VS コードで終了すると、プロセスが孤立することがあります。", "TerminateAction.failed": "実行中のタスクの終了に失敗しました", - "TaskService.noTaskToRestart": "再起動するタスクがありません。", "TaskService.tastToRestart": "再起動するタスクを選択してください", - "TaskService.defaultBuildTaskExists": "{0} は既に既定のビルド タスクとしてマークされています。", "TaskService.pickDefaultBuildTask": "既定のビルド タスクとして使用するタスクを選択", "TaskService.defaultTestTaskExists": "{0} は既に既定のテスト タスクとしてマークされています。", "TaskService.pickDefaultTestTask": "既定のテスト タスクとして使用するタスクを選択", - "TaskService.noTaskIsRunning": "実行中のタスクはありません。", "TaskService.pickShowTask": "出力を表示するタスクを選択", "ShowLogAction.label": "タスク ログの表示", "RunTaskAction.label": "タスクの実行", diff --git a/i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json b/i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json index b957e64c6eb..3c51dcd1d5d 100644 --- a/i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json +++ b/i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json @@ -38,5 +38,6 @@ "workbench.action.terminal.focusFindWidget": "検索ウィジェットにフォーカスする", "workbench.action.terminal.hideFindWidget": "検索ウィジェットを非表示にする", "nextTerminalFindTerm": "次の検索語句を表示", - "previousTerminalFindTerm": "前の検索語句を表示" + "previousTerminalFindTerm": "前の検索語句を表示", + "quickOpenTerm": "アクティブなターミナルの切り替え" } \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json b/i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json index b44e4e0fdf7..6cc81e64612 100644 --- a/i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json +++ b/i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json @@ -8,5 +8,6 @@ "terminal.foreground": "ターミナルの前景色。", "terminalCursor.foreground": "ターミナルのカーソル前景色。", "terminalCursor.background": "ターミナルのカーソルの背景色。ブロックカーソルで重ねた文字の色をカスタマイズできます。", + "terminal.selectionBackground": "ターミナルの選択範囲の背景色。", "terminal.ansiColor": "ターミナルの '{0}' ANSI カラー。" } \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json b/i18n/jpn/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json index 6b8b81bf3c1..4228aaf4caf 100644 --- a/i18n/jpn/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json +++ b/i18n/jpn/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json @@ -10,6 +10,7 @@ "welcomePage.newFile": "新しいファイル", "welcomePage.openFolder": "フォルダーを開く...", "welcomePage.cloneGitRepository": "Git リポジトリを複製...", + "welcomePage.addWorkspaceFolder": "ワークスペース フォルダーを追加…", "welcomePage.recent": "最近", "welcomePage.moreRecent": "その他", "welcomePage.noRecentFolders": "最近使用したフォルダーなし", diff --git a/i18n/jpn/src/vs/workbench/services/configuration/node/configuration.i18n.json b/i18n/jpn/src/vs/workbench/services/configuration/node/configuration.i18n.json index 50b9ab7d867..610c4b1b63b 100644 --- a/i18n/jpn/src/vs/workbench/services/configuration/node/configuration.i18n.json +++ b/i18n/jpn/src/vs/workbench/services/configuration/node/configuration.i18n.json @@ -13,7 +13,7 @@ "invalid.title": "'configuration.title' は、文字列である必要があります", "vscode.extension.contributes.defaultConfiguration": "言語ごとに既定のエディター構成の設定を提供します。", "invalid.properties": "'configuration.properties' は、オブジェクトである必要があります", - "workspaceConfig.folders.description": "ワークスペースで読み込まれるフォルダーのリスト。ファイル パスである必要があります。例: `/root/folderA` または `./folderA` のようなワークスペース ファイルの場所に対して解決される相対パス。", - "workspaceConfig.folder.description": "ファイルパス。例: `/root/folderA` または `./folderA` のようなワークスペース ファイルの場所に対して解決される相対パス。", + "invalid.allOf": "'configuration.allOf' は非推奨で使用できなくなります。代わりに 'configuration' コントリビューション ポイントに複数の構成セクションを配列として渡します。", + "workspaceConfig.path.description": "ファイルパス。例: `/root/folderA` または `./folderA` のようなワークスペース ファイルの場所に対して解決される相対パス。", "workspaceConfig.settings.description": "ワークスペースの設定" } \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/services/files/electron-browser/remoteFileService.i18n.json b/i18n/jpn/src/vs/workbench/services/files/electron-browser/remoteFileService.i18n.json new file mode 100644 index 00000000000..26fc5404882 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/services/files/electron-browser/remoteFileService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileBinaryError": "ファイルはバイナリのようなので、テキストとして開くことができません" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/services/files/node/fileService.i18n.json b/i18n/jpn/src/vs/workbench/services/files/node/fileService.i18n.json index 8c65920db65..af58e0c5a88 100644 --- a/i18n/jpn/src/vs/workbench/services/files/node/fileService.i18n.json +++ b/i18n/jpn/src/vs/workbench/services/files/node/fileService.i18n.json @@ -5,10 +5,12 @@ // Do not edit this file. It is machine generated. { "fileInvalidPath": "ファイルのリソース ({0}) が無効です", + "fileIsDirectoryError": "ファイルはディレクトリです", "fileNotModifiedError": "ファイルは次の時点以後に変更されていません:", "fileTooLargeError": "開くファイルが大きすぎます", "fileBinaryError": "ファイルはバイナリのようなので、テキストとして開くことができません", "fileNotFoundError": "ファイルが見つかりません ({0})", + "fileExists": "生成しようとしているファイル ({0}) は既に存在しています", "fileMoveConflict": "移動/コピーできません。移動/コピー先にファイルが既に存在します。", "unableToMoveCopyError": "移動/コピーできません。ファイルが含まれるフォルダーが置き換わることになります。", "foldersCopyError": "フォルダーをワークスペース内にコピーできません。個々のファイルを選択してコピーしてください。", diff --git a/i18n/kor/src/vs/code/electron-main/menus.i18n.json b/i18n/kor/src/vs/code/electron-main/menus.i18n.json index 46251c50573..1e29745ea9b 100644 --- a/i18n/kor/src/vs/code/electron-main/menus.i18n.json +++ b/i18n/kor/src/vs/code/electron-main/menus.i18n.json @@ -171,8 +171,6 @@ "miRunningTask": "실행 중인 작업 표시(&&G)...", "miRestartTask": "실행 중인 작업 다시 시작(&&E)...", "miTerminateTask": "작업 종료(&&T)...", - "miConfigureTask": "작업 구성(&&C)", - "miConfigureBuildTask": "기본 빌드 작업 구성(&&F)", "accessibilityOptionsWindowTitle": "접근성 옵션", "miRestartToUpdate": "다시 시작하여 업데이트...", "miCheckingForUpdates": "업데이트를 확인하는 중...", diff --git a/i18n/kor/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json b/i18n/kor/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json index 7119dd38b12..d726014f30a 100644 --- a/i18n/kor/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json +++ b/i18n/kor/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json @@ -5,10 +5,7 @@ // Do not edit this file. It is machine generated. { "invalidManifest": "잘못된 확장: package.json이 JSON 파일이 아닙니다.", - "restartCode": "{0}을(를) 다시 설치하기 전에 Code를 다시 시작하세요.", - "installDependeciesConfirmation": "'{0}'을(를) 설치하면 종속성도 설치됩니다. 계속할까요?", - "install": "예", - "doNotInstall": "아니요", + "restartCodeLocal": "{0}을(를) 다시 설치하기 전에 Code를 다시 시작하세요.", "uninstallDependeciesConfirmation": "'{0}'만 제거할까요, 아니면 종속성도 제거할까요?", "uninstallOnly": "만", "uninstallAll": "모두", diff --git a/i18n/kor/src/vs/workbench/browser/actions/workspaceActions.i18n.json b/i18n/kor/src/vs/workbench/browser/actions/workspaceActions.i18n.json index 82079af3e4a..ae23e97e23b 100644 --- a/i18n/kor/src/vs/workbench/browser/actions/workspaceActions.i18n.json +++ b/i18n/kor/src/vs/workbench/browser/actions/workspaceActions.i18n.json @@ -14,7 +14,6 @@ "selectWorkspace": "작업 영역 폴더 선택", "removeFolderFromWorkspace": "작업 영역에서 폴더 삭제", "saveWorkspaceAsAction": "작업 영역을 다른 이름으로 저장", - "saveEmptyWorkspaceNotSupported": "저장하려면 먼저 작업 영역을 여세요.", "save": "저장(&&S)", "saveWorkspace": "작업 영역 저장", "openWorkspaceAction": "작업 영역 열기...", diff --git a/i18n/kor/src/vs/workbench/browser/parts/views/panelViewlet.i18n.json b/i18n/kor/src/vs/workbench/browser/parts/views/panelViewlet.i18n.json new file mode 100644 index 00000000000..8d20ba83c9a --- /dev/null +++ b/i18n/kor/src/vs/workbench/browser/parts/views/panelViewlet.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "viewToolbarAriaLabel": "{0} 동작" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json b/i18n/kor/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json index 8c1877159e1..4740dad8c10 100644 --- a/i18n/kor/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json +++ b/i18n/kor/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json @@ -51,9 +51,8 @@ "showLanguageExtensionsShort": "언어 확장", "showAzureExtensions": "Azure 확장 표시", "showAzureExtensionsShort": "Azure 확장", - "configureWorkspaceRecommendedExtensions": "권장 확장 구성(작업 영역)", - "ConfigureWorkspaceRecommendations.noWorkspace": "권장 사항은 작업 영역 폴더에서만 사용할 수 있습니다.", "OpenExtensionsFile.failed": "'.vscode' 폴더({0}) 내에 'extensions.json' 파일을 만들 수 없습니다.", + "configureWorkspaceRecommendedExtensions": "권장 확장 구성(작업 영역)", "builtin": "기본 제공", "disableAll": "설치된 모든 확장 사용 안 함", "disableAllWorkspace": "이 작업 영역에 대해 설치된 모든 확장 사용 안 함", diff --git a/i18n/kor/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json b/i18n/kor/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json index 392c06bedeb..3ded239f7d2 100644 --- a/i18n/kor/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json +++ b/i18n/kor/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json @@ -9,7 +9,6 @@ "openGlobalKeybindingsFile": "바로 가기 키 파일 열기", "openWorkspaceSettings": "작업 영역 설정 열기", "openFolderSettings": "폴더 설정 열기", - "pickFolder": "폴더 선택", "configureLanguageBasedSettings": "언어별 설정 구성...", "languageDescriptionConfigured": "({0})", "pickLanguage": "언어 선택" diff --git a/i18n/kor/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json b/i18n/kor/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json index f2e2d333694..9aafe17b284 100644 --- a/i18n/kor/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json +++ b/i18n/kor/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json @@ -6,7 +6,5 @@ { "relaunchSettingMessage": "설정이 변경되어 다시 시작해야만 적용됩니다.", "relaunchSettingDetail": "[다시 시작] 단추를 눌러 {0}을(를) 다시 시작하고 설정을 사용하도록 설정하세요.", - "restart": "다시 시작", - "relaunchWorkspaceMessage": "이 작업 영역을 변경하려면 확장 시스템을 다시 로드해야 합니다.", - "reload": "다시 로드" + "restart": "다시 시작" } \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json b/i18n/kor/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json index f3414039077..00792047669 100644 --- a/i18n/kor/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json +++ b/i18n/kor/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json @@ -5,13 +5,7 @@ // Do not edit this file. It is machine generated. { "tasksCategory": "작업", - "ConfigureTaskRunnerAction.noWorkspace": "작업은 작업 영역 폴더에서만 사용할 수 있습니다.", - "ConfigureTaskRunnerAction.quickPick.template": "Task Runner 선택", - "ConfigureTaskRunnerAction.autoDetecting": "{0} 작업을 자동 검색 중", - "ConfigureTaskRunnerAction.autoDetect": "작업 시스템을 자동으로 감지하지 못했습니다. 기본 템플릿을 사용하는 중입니다. 자세한 내용은 작업 출력을 참조하세요.", - "ConfigureTaskRunnerAction.autoDetectError": "작업 시스템을 자동으로 감지하는 중 오류가 발생했습니다. 자세한 내용은 작업 출력을 참조하세요.", - "ConfigureTaskRunnerAction.failed": "'.vscode' 폴더 내에 'tasks.json' 파일을 만들 수 없습니다. 자세한 내용은 작업 출력을 참조하세요.", - "ConfigureTaskRunnerAction.label": "Task Runner 구성", + "ConfigureTaskRunnerAction.label": "작업 구성", "ConfigureBuildTaskAction.label": "빌드 작업 구성", "CloseMessageAction.label": "닫기", "ShowTerminalAction.label": "터미널 보기", @@ -47,22 +41,16 @@ "configured": "구성된 작업", "detected": "감지된 작업", "TaskService.fetchingBuildTasks": "빌드 작업을 페치하는 중...", - "TaskService.noBuildTaskTerminal": "빌드 작업을 찾을 수 없습니다. '빌드 작업 구성'을 눌러서 빌드 작업을 정의하세요.", "TaskService.pickBuildTask": "실행할 빌드 작업 선택", "TaskService.fetchingTestTasks": "테스트 작업을 페치하는 중...", - "TaskService.noTestTaskTerminal": "테스트 작업을 찾을 수 없습니다. 'Task Runner 구성'을 눌러서 테스트 작업을 정의하세요.", "TaskService.pickTestTask": "실행할 테스트 작업 선택", - "TaskService.noTaskRunning": "현재 실행 중인 작업이 없습니다.", "TaskService.tastToTerminate": "종료할 작업 선택", "TerminateAction.noProcess": "시작된 프로세스가 더 이상 존재하지 않습니다. 작업에서 생성된, VS Code를 끝내는 백그라운드 작업이 분리된 프로세스가 될 수 있습니다.", "TerminateAction.failed": "실행 중인 작업을 종료하지 못했습니다.", - "TaskService.noTaskToRestart": "다시 시작할 작업이 없습니다.", "TaskService.tastToRestart": "다시 시작할 작업 선택", - "TaskService.defaultBuildTaskExists": "{0}은(는) 이미 기본 빌드 작업으로 표시되어 있습니다.", "TaskService.pickDefaultBuildTask": "기본 빌드 작업으로 사용할 작업을 선택", "TaskService.defaultTestTaskExists": "{0}은(는) 이미 기본 테스트 작업으로 표시되어 있습니다.", "TaskService.pickDefaultTestTask": "기본 테스트 작업으로 사용할 작업 선택", - "TaskService.noTaskIsRunning": "실행 중인 작업이 없습니다.", "TaskService.pickShowTask": "출력을 표시할 작업 선택", "ShowLogAction.label": "작업 로그 표시", "RunTaskAction.label": "작업 실행", diff --git a/i18n/kor/src/vs/workbench/services/configuration/node/configuration.i18n.json b/i18n/kor/src/vs/workbench/services/configuration/node/configuration.i18n.json index 529c4144533..0082a801e64 100644 --- a/i18n/kor/src/vs/workbench/services/configuration/node/configuration.i18n.json +++ b/i18n/kor/src/vs/workbench/services/configuration/node/configuration.i18n.json @@ -13,7 +13,6 @@ "invalid.title": "'configuration.title'은 문자열이어야 합니다.", "vscode.extension.contributes.defaultConfiguration": "언어별로 기본 편집기 구성 설정을 적용합니다.", "invalid.properties": "'configuration.properties'는 개체여야 합니다.", - "workspaceConfig.folders.description": "작업 영역에 로드할 폴더 목록입니다. 파일 경로를 사용해야 합니다. 예: `/root/folderA` 또는 `./folderA`(작업 영역 파일의 위치를 기준으로 확인할 상대 경로인 경우)", - "workspaceConfig.folder.description": "파일 경로입니다. 예: `/root/folderA` 또는 `./folderA`(작업 영역 파일의 위치를 기준으로 확인할 상대 경로인 경우)", + "workspaceConfig.path.description": "파일 경로입니다. 예: `/root/folderA` 또는 `./folderA`(작업 영역 파일의 위치를 기준으로 확인할 상대 경로인 경우)", "workspaceConfig.settings.description": "작업 영역 설정" } \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/services/files/electron-browser/remoteFileService.i18n.json b/i18n/kor/src/vs/workbench/services/files/electron-browser/remoteFileService.i18n.json new file mode 100644 index 00000000000..d48fe3d053e --- /dev/null +++ b/i18n/kor/src/vs/workbench/services/files/electron-browser/remoteFileService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileBinaryError": "파일이 이진인 것 같으므로 테스트로 열 수 없습니다." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/code/electron-main/menus.i18n.json b/i18n/ptb/src/vs/code/electron-main/menus.i18n.json index 709c9b4bbc4..286ab62d136 100644 --- a/i18n/ptb/src/vs/code/electron-main/menus.i18n.json +++ b/i18n/ptb/src/vs/code/electron-main/menus.i18n.json @@ -175,8 +175,6 @@ "miRunningTask": "Mostrar &&Tarefas Em Execução...", "miRestartTask": "R&&einiciar Tarefa em Execução", "miTerminateTask": "&&Finalizar Tarefa", - "miConfigureTask": "&&Configurar tarefas", - "miConfigureBuildTask": "Configurar Tarefa de Compilação &&Padrão", "accessibilityOptionsWindowTitle": "Opções de Acessibilidade", "miRestartToUpdate": "Reinicie para Atualizar...", "miCheckingForUpdates": "Verificando Atualizações...", diff --git a/i18n/ptb/src/vs/code/electron-main/windows.i18n.json b/i18n/ptb/src/vs/code/electron-main/windows.i18n.json index c8c821ec8a6..1921b2686e9 100644 --- a/i18n/ptb/src/vs/code/electron-main/windows.i18n.json +++ b/i18n/ptb/src/vs/code/electron-main/windows.i18n.json @@ -18,6 +18,7 @@ "openFolder": "Abrir Pasta", "openFile": "Abrir Arquivo", "workspaceOpenedMessage": "Não é possível salvar espaço de trabalho '{0}'", + "workspaceOpenedDetail": "O espaço de trabalho já está aberto em outra janela. Por favor, feche a janela primeiro e tente novamente.", "openWorkspace": "&&Abrir", "openWorkspaceTitle": "Abrir o Espaço de Trabalho", "save": "&&Salvar", diff --git a/i18n/ptb/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json b/i18n/ptb/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json index d7ef91038db..cea37b5ae9c 100644 --- a/i18n/ptb/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json +++ b/i18n/ptb/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json @@ -13,6 +13,7 @@ "vscode.extension.contributes.menuItem.group": "Grupo ao qual pertence este comando", "vscode.extension.contributes.menus": "Contribui itens de menu ao editor", "menus.commandPalette": "Paleta de comandos", + "menus.touchBar": "A barra de toque (somente macOS)", "menus.editorTitle": "Meno do título editor", "menus.editorContext": "Mostrar o menu de contexto do editor", "menus.explorerContext": "Menu no contexto de explorador de arquivos", diff --git a/i18n/ptb/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json b/i18n/ptb/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json index a6c5a37ab05..e90957df5d7 100644 --- a/i18n/ptb/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json +++ b/i18n/ptb/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json @@ -5,10 +5,8 @@ // Do not edit this file. It is machine generated. { "invalidManifest": "Extensão inválida: pacote.json nao é um arquivo JSON válido", - "restartCode": "Por favor reinicie Code antes de reinstalar {0}.", - "installDependeciesConfirmation": "A instalação de '{0}' também inclui suas dependências. Gostaria de continuar?", - "install": "Sim", - "doNotInstall": "Não", + "restartCodeLocal": "Por favor reinicie Code antes de reinstalar {0}.", + "restartCodeGallery": "Por favor reinicie o Code antes de reinstalar.", "uninstallDependeciesConfirmation": "Gostaria de desinstalar '{0}' somente, ou suas dependências também?", "uninstallOnly": "Apenas", "uninstallAll": "Todos", diff --git a/i18n/ptb/src/vs/workbench/browser/actions/workspaceActions.i18n.json b/i18n/ptb/src/vs/workbench/browser/actions/workspaceActions.i18n.json index 64f25e29725..5663c3ba47c 100644 --- a/i18n/ptb/src/vs/workbench/browser/actions/workspaceActions.i18n.json +++ b/i18n/ptb/src/vs/workbench/browser/actions/workspaceActions.i18n.json @@ -14,7 +14,6 @@ "selectWorkspace": "Selecionar pastas para espaço de trabalho", "removeFolderFromWorkspace": "Remover pasta da área de trabalho", "saveWorkspaceAsAction": "Salvar o espaço de trabalho como...", - "saveEmptyWorkspaceNotSupported": "Por favor, abra um espaço de trabalho antes de salvar.", "save": "&&Salvar", "saveWorkspace": "Salvar o espaço de trabalho", "openWorkspaceAction": "Abrir o Espaço de Trabalho...", diff --git a/i18n/ptb/src/vs/workbench/browser/parts/views/panelViewlet.i18n.json b/i18n/ptb/src/vs/workbench/browser/parts/views/panelViewlet.i18n.json new file mode 100644 index 00000000000..32207b28d40 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/browser/parts/views/panelViewlet.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "viewToolbarAriaLabel": "{0} ações " +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json b/i18n/ptb/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json index fc84602f2f7..3bbf66ae160 100644 --- a/i18n/ptb/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json +++ b/i18n/ptb/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json @@ -51,9 +51,8 @@ "showLanguageExtensionsShort": "Extensões de Linguagem", "showAzureExtensions": "Mostrar extensões para o Azure", "showAzureExtensionsShort": "Extensões do Azure", - "configureWorkspaceRecommendedExtensions": "Configurar Extensões Recomendadas (Espaço de Trabalho)", - "ConfigureWorkspaceRecommendations.noWorkspace": "As recomendações somente estão disponíveis em uma pasta do espaço de trabalho.", "OpenExtensionsFile.failed": "Não foi possível criar o arquivo 'extensions.json' na pasta '.vscode' ({0}).", + "configureWorkspaceRecommendedExtensions": "Configurar Extensões Recomendadas (Espaço de Trabalho)", "builtin": "Intrínseco", "disableAll": "Desabilitar Todas as Extensões Instaladas", "disableAllWorkspace": "Desabilitar Todas as Extensões Instaladas para este Espaço de Trabalho", diff --git a/i18n/ptb/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json b/i18n/ptb/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json index 57cd480b3ed..0abf761f3b5 100644 --- a/i18n/ptb/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json +++ b/i18n/ptb/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json @@ -9,7 +9,6 @@ "openGlobalKeybindingsFile": "Abrir Arquivo de Atalhos de Teclado", "openWorkspaceSettings": "Abrir as configurações do espaço de trabalho", "openFolderSettings": "Abrir configurações da pasta", - "pickFolder": "Selecionar a Pasta", "configureLanguageBasedSettings": "Definir Configurações Específicas de Linguagem...", "languageDescriptionConfigured": "({0})", "pickLanguage": "Selecionar Linguagem" diff --git a/i18n/ptb/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json b/i18n/ptb/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json index 0b8f8925f7c..0f11464989e 100644 --- a/i18n/ptb/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json +++ b/i18n/ptb/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json @@ -6,7 +6,5 @@ { "relaunchSettingMessage": "Uma configuração que requer uma reinicialização foi alterada.", "relaunchSettingDetail": "Pressione o botão de reinicialização para reiniciar {0} e habilitar a configuração.", - "restart": "Reiniciar", - "relaunchWorkspaceMessage": "Esta mudança de espaço de trabalho exige o recarregamento do nosso sistema de extensão.", - "reload": "Recarregar" + "restart": "Reiniciar" } \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json b/i18n/ptb/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json index 69380915390..92d57ef9275 100644 --- a/i18n/ptb/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json +++ b/i18n/ptb/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json @@ -6,7 +6,6 @@ { "commitMessage": "Mensagem (tecle {0} para confirmar)", "installAdditionalSCMProviders": "Instalar provedores de SCM adicionais...", - "no open repo": "Não há nenhum provedor de controle de fonte ativo.", "source control": "Controle de código-fonte", "viewletTitle": "{0}: {1}" } \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json b/i18n/ptb/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json index a14587dfc3c..dd52522c3a8 100644 --- a/i18n/ptb/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json +++ b/i18n/ptb/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json @@ -5,13 +5,7 @@ // Do not edit this file. It is machine generated. { "tasksCategory": "Tarefas", - "ConfigureTaskRunnerAction.noWorkspace": "Tarefas somente estão disponíveis em uma pasta da área de trabalho.", - "ConfigureTaskRunnerAction.quickPick.template": "Selecione um gerenciador de tarefa", - "ConfigureTaskRunnerAction.autoDetecting": "Tarefas de auto detecção para {0}", - "ConfigureTaskRunnerAction.autoDetect": "A tarefa de sistema de auto detecção falhou. Usando o modelo padrão. Consulte a saída da tarefa para detalhes.", - "ConfigureTaskRunnerAction.autoDetectError": "A tarefa de sistema de auto detecção produziu erros. Consulte a saída da tarefa para detalhes.", - "ConfigureTaskRunnerAction.failed": "Não é possível criar o arquivo 'tasks.json' na pasta '.vscode'. Consulte a saída da tarefa para detalhes.", - "ConfigureTaskRunnerAction.label": "Configure o gerenciador de tarefas", + "ConfigureTaskRunnerAction.label": "Configurar a tarefa", "ConfigureBuildTaskAction.label": "Configurar Tarefa de Compilação", "CloseMessageAction.label": "Fechar", "ShowTerminalAction.label": "Terminal Visualização", @@ -19,6 +13,7 @@ "manyMarkers": "99+", "runningTasks": "Mostrar tarefas em execução", "tasks": "Tarefas", + "TaskSystem.noHotSwap": "Alterar o mecanismo de execução da tarefa com uma tarefa ativa executando exige que a janela seja recarregada", "TaskService.noBuildTask1": "Nenhuma tarefa de compilação definida. Marque uma tarefa com 'isBuildCommand' no arquivo tasks.json.", "TaskService.noBuildTask2": "Nenhuma tarefa de compilação definida. Marque uma tarefa como um grupo 'build' no arquivo tasks.json.", "TaskService.noTestTask1": "Nenhuma tarefa de teste definida. Marque uma tarefa com 'isTestCommand' no arquivo tasks.json.", @@ -47,22 +42,16 @@ "configured": "tarefas configuradas", "detected": "tarefas detectadas", "TaskService.fetchingBuildTasks": "Buscando tarefas de compilação...", - "TaskService.noBuildTaskTerminal": "Nenhuma tarefa de compilação encontrada. Pressione 'Configurar Tarefa de Compilação' para definir um.", "TaskService.pickBuildTask": "Selecione a tarefa de compilação para executar", "TaskService.fetchingTestTasks": "Buscando tarefas de teste...", - "TaskService.noTestTaskTerminal": "Nenhuma tarefa de teste encontrada. Pressione 'Configurar Tarefa de Execução' para definir uma.", "TaskService.pickTestTask": "Selecione a tarefa de teste para executar", - "TaskService.noTaskRunning": "Nenhuma tarefa está sendo executada.", "TaskService.tastToTerminate": "Selecione a tarefa para terminar", "TerminateAction.noProcess": "O processo executado não existe mais. Se a tarefa produziu processos em background, finalizar o VS Code pode resultar em processos órfãos.", "TerminateAction.failed": "Falha ao finalizar a tarefa sendo executada", - "TaskService.noTaskToRestart": "Não há tarefa para reiniciar.", "TaskService.tastToRestart": "Selecione a tarefa para reiniciar", - "TaskService.defaultBuildTaskExists": "{0} já está marcado como a tarefa de compilação padrão.", "TaskService.pickDefaultBuildTask": "Selecione a tarefa a ser usada como a tarefa de compilação padrão", "TaskService.defaultTestTaskExists": "{0} já está marcado como a tarefa de teste padrão.", "TaskService.pickDefaultTestTask": "Selecione a tarefa a ser usada como a tarefa de teste padrão", - "TaskService.noTaskIsRunning": "Nenhuma tarefa em execução.", "TaskService.pickShowTask": "Selecione a tarefa para mostrar sua saída", "ShowLogAction.label": "Visualizar o Log de Tarefas", "RunTaskAction.label": "Executar Tarefa", diff --git a/i18n/ptb/src/vs/workbench/services/configuration/node/configuration.i18n.json b/i18n/ptb/src/vs/workbench/services/configuration/node/configuration.i18n.json index 3eda0fa5544..6681047110e 100644 --- a/i18n/ptb/src/vs/workbench/services/configuration/node/configuration.i18n.json +++ b/i18n/ptb/src/vs/workbench/services/configuration/node/configuration.i18n.json @@ -14,7 +14,6 @@ "vscode.extension.contributes.defaultConfiguration": "Contribui às definições de configuração padrão do editor por linguagem.", "invalid.properties": "'configuration.properties' deve ser um objeto", "invalid.allOf": "'configuration.allOf' está obsoleto e não deve ser usado. Em vez disso, passe várias seções de configuração como uma matriz para o ponto de contribuição 'configuration'.", - "workspaceConfig.folders.description": "Lista de pastas para ser carregada no espaço de trabalho. Deve ser um caminho para um arquivo. Por exemplo '/root/pastaA' ou './pastaA' para um caminho relativo que será determinado de acordo com o local do arquivo no espaço de trabalho.", - "workspaceConfig.folder.description": "Um caminho para um arquivo. Por exemplo, '/root /pastaA' ou './pastaA' para um caminho relativo que será determinado de acordo com o local do arquivo no espaço de trabalho.", + "workspaceConfig.path.description": "Um caminho para um arquivo. Por exemplo, '/root /pastaA' ou './pastaA' para um caminho relativo que será determinado de acordo com o local do arquivo no espaço de trabalho.", "workspaceConfig.settings.description": "Configurações de espaço de trabalho" } \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/services/files/electron-browser/remoteFileService.i18n.json b/i18n/ptb/src/vs/workbench/services/files/electron-browser/remoteFileService.i18n.json new file mode 100644 index 00000000000..7b91deaec2e --- /dev/null +++ b/i18n/ptb/src/vs/workbench/services/files/electron-browser/remoteFileService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileBinaryError": "Arquivo parece ser binário e não pode ser aberto como texto" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/services/files/node/fileService.i18n.json b/i18n/ptb/src/vs/workbench/services/files/node/fileService.i18n.json index 1eeed79c955..e6423a5b25c 100644 --- a/i18n/ptb/src/vs/workbench/services/files/node/fileService.i18n.json +++ b/i18n/ptb/src/vs/workbench/services/files/node/fileService.i18n.json @@ -10,6 +10,7 @@ "fileTooLargeError": "Arquivo muito grande para abrir", "fileBinaryError": "Arquivo parece ser binário e não pode ser aberto como texto", "fileNotFoundError": "Arquivo não encontrado ({0})", + "fileExists": "Arquivo a ser criado já existe ({0})", "fileMoveConflict": "Não é possível mover/copiar. Arquivo já existe no destino.", "unableToMoveCopyError": "Não é possível mover/copiar. Arquivo poderia substituir a pasta em que está contida.", "foldersCopyError": "Pastas não podem ser copiadas para a área de trabalho. Por favor selecione arquivos individuais para serem copiados.", diff --git a/i18n/rus/src/vs/code/electron-main/menus.i18n.json b/i18n/rus/src/vs/code/electron-main/menus.i18n.json index 696280aa197..d5553161f35 100644 --- a/i18n/rus/src/vs/code/electron-main/menus.i18n.json +++ b/i18n/rus/src/vs/code/electron-main/menus.i18n.json @@ -171,8 +171,6 @@ "miRunningTask": "Показать выполняющ&&иеся задачи...", "miRestartTask": "П&&ерезапустить запущенную задачу...", "miTerminateTask": "&&Завершить задачу...", - "miConfigureTask": "&&Настроить задачи", - "miConfigureBuildTask": "Настроить задачу сборки по у&&молчанию", "accessibilityOptionsWindowTitle": "Специальные возможности", "miRestartToUpdate": "Перезапустить программу для обновления...", "miCheckingForUpdates": "Идет проверка наличия обновлений...", diff --git a/i18n/rus/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json b/i18n/rus/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json index 60652be3521..0e8a5193ce9 100644 --- a/i18n/rus/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json +++ b/i18n/rus/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json @@ -5,10 +5,7 @@ // Do not edit this file. It is machine generated. { "invalidManifest": "Недопустимое расширение: package.json не является файлом JSON.", - "restartCode": "Перезапустите код перед переустановкой {0}.", - "installDependeciesConfirmation": "При установке \"{0}\" также устанавливаются зависимости. Вы хотите продолжить?", - "install": "Да", - "doNotInstall": "Нет", + "restartCodeLocal": "Перезапустите код перед переустановкой {0}.", "uninstallDependeciesConfirmation": "Вы хотите удалить \"{0}\" отдельно или вместе с зависимостями?", "uninstallOnly": "Только", "uninstallAll": "Все", diff --git a/i18n/rus/src/vs/workbench/browser/actions/workspaceActions.i18n.json b/i18n/rus/src/vs/workbench/browser/actions/workspaceActions.i18n.json index 9c00cba356f..b82df283bae 100644 --- a/i18n/rus/src/vs/workbench/browser/actions/workspaceActions.i18n.json +++ b/i18n/rus/src/vs/workbench/browser/actions/workspaceActions.i18n.json @@ -14,7 +14,6 @@ "selectWorkspace": "Выбрать папки для рабочей области", "removeFolderFromWorkspace": "Удалить папку из рабочей области", "saveWorkspaceAsAction": "Сохранить рабочую область как...", - "saveEmptyWorkspaceNotSupported": "Перед сохранением рабочей области откройте ее. ", "save": "Сохранить", "saveWorkspace": "Сохранить рабочую область", "openWorkspaceAction": "Открыть рабочую область...", diff --git a/i18n/rus/src/vs/workbench/browser/parts/views/panelViewlet.i18n.json b/i18n/rus/src/vs/workbench/browser/parts/views/panelViewlet.i18n.json new file mode 100644 index 00000000000..761072edd67 --- /dev/null +++ b/i18n/rus/src/vs/workbench/browser/parts/views/panelViewlet.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "viewToolbarAriaLabel": "Действий: {0}" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json b/i18n/rus/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json index d49f2b5aee8..6c4879d3c3f 100644 --- a/i18n/rus/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json +++ b/i18n/rus/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json @@ -51,9 +51,8 @@ "showLanguageExtensionsShort": "Расширения языка", "showAzureExtensions": "Показать расширения Azure", "showAzureExtensionsShort": "Расширения Azure", - "configureWorkspaceRecommendedExtensions": "Настроить рекомендуемые расширения (рабочая область)", - "ConfigureWorkspaceRecommendations.noWorkspace": "Рекомендации доступны только для папки рабочей области.", "OpenExtensionsFile.failed": "Не удается создать файл \"extensions.json\" в папке \".vscode\" ({0}).", + "configureWorkspaceRecommendedExtensions": "Настроить рекомендуемые расширения (рабочая область)", "builtin": "Встроенное", "disableAll": "Отключить все установленные расширения", "disableAllWorkspace": "Отключить все установленные расширения для этой рабочей области", diff --git a/i18n/rus/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json b/i18n/rus/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json index e7a2b6b75ec..db8d5850260 100644 --- a/i18n/rus/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json +++ b/i18n/rus/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json @@ -9,7 +9,6 @@ "openGlobalKeybindingsFile": "Открыть файл сочетаний клавиш", "openWorkspaceSettings": "Открыть параметры рабочей области", "openFolderSettings": "Открыть параметры папок", - "pickFolder": "Выбрать папку", "configureLanguageBasedSettings": "Настроить параметры языка...", "languageDescriptionConfigured": "({0})", "pickLanguage": "Выбрать язык" diff --git a/i18n/rus/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json b/i18n/rus/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json index cb591fb1f5f..74fdb766725 100644 --- a/i18n/rus/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json +++ b/i18n/rus/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json @@ -6,7 +6,5 @@ { "relaunchSettingMessage": "После изменения параметра необходима выполнить перезагрузку, чтобы изменения вступили в силу.", "relaunchSettingDetail": "Нажмите кнопку \"Перезагрузить\", чтобы перезагрузить {0} и включить параметр.", - "restart": "Перезапустить", - "relaunchWorkspaceMessage": "Для изменения этой рабочей области требуется перезагрузить нашу систему расширений.", - "reload": "Перезагрузка" + "restart": "Перезапустить" } \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json b/i18n/rus/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json index edbb663b01e..60bc03c5518 100644 --- a/i18n/rus/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json +++ b/i18n/rus/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json @@ -5,13 +5,7 @@ // Do not edit this file. It is machine generated. { "tasksCategory": "Задачи", - "ConfigureTaskRunnerAction.noWorkspace": "Задачи доступны только в папке рабочей области.", - "ConfigureTaskRunnerAction.quickPick.template": "Выбрать средство выполнения задач", - "ConfigureTaskRunnerAction.autoDetecting": "Автообнаружение задач для {0}", - "ConfigureTaskRunnerAction.autoDetect": "Не удалось автоматически определить систему задачи, используется шаблон по умолчанию. Подробности см. в выходных данных задачи.", - "ConfigureTaskRunnerAction.autoDetectError": "При определении системы задачи возникли ошибки. Дополнительные сведения см. в выходных данных задачи.", - "ConfigureTaskRunnerAction.failed": "Не удается создать файл tasks.json в папке .vscode. Подробности см. в выходных данных задачи.", - "ConfigureTaskRunnerAction.label": "Настроить средство выполнения задач", + "ConfigureTaskRunnerAction.label": "Настроить задачу", "ConfigureBuildTaskAction.label": "Настроить задачу сборки", "CloseMessageAction.label": "Закрыть", "ShowTerminalAction.label": "Ознакомиться с терминалом", @@ -47,22 +41,16 @@ "configured": "настроенные задачи", "detected": "обнаруженные задачи", "TaskService.fetchingBuildTasks": "Получение задач сборки...", - "TaskService.noBuildTaskTerminal": "Задача сборки не найдена. Нажмите кнопку \"Настроить задачу сборки\", чтобы определить задачу сборки.", "TaskService.pickBuildTask": "Выберите задачу сборки для запуска", "TaskService.fetchingTestTasks": "Получение задач тестирования...", - "TaskService.noTestTaskTerminal": "Задача тестирования не найдена. Нажмите кнопку \"Настроить задачу тестирования\", чтобы определить задачу сборки. ", "TaskService.pickTestTask": "Выберите задачу тестирования для запуска", - "TaskService.noTaskRunning": "Ни одной задачи не запущено.", "TaskService.tastToTerminate": "Выберите задачи для завершения", "TerminateAction.noProcess": "Запущенный процесс больше не существует. Если задача породила фоновые задачи, выход из Visual Studio Code может привести к появлению потерянных процессов.", "TerminateAction.failed": "Не удалось завершить запущенную задачу", - "TaskService.noTaskToRestart": "Задачи для перезапуска не найдены.", "TaskService.tastToRestart": "Выберите задачу для перезапуска", - "TaskService.defaultBuildTaskExists": "{0} уже помечена как задача сборки по умолчанию.", "TaskService.pickDefaultBuildTask": "Выберите задачу, которая будет использоваться в качестве задачи сборки по умолчанию.", "TaskService.defaultTestTaskExists": "{0} уже помечена как задача сборки по умолчанию. ", "TaskService.pickDefaultTestTask": "Выберите задачу, которая будет использоваться в качестве задачи тестирования по умолчанию. ", - "TaskService.noTaskIsRunning": "Ни одной задачи не запущено.", "TaskService.pickShowTask": "Выберите задачу, выходные данные для которой нужно отобразить", "ShowLogAction.label": "Показать журнал задач", "RunTaskAction.label": "Выполнить задачу", diff --git a/i18n/rus/src/vs/workbench/services/configuration/node/configuration.i18n.json b/i18n/rus/src/vs/workbench/services/configuration/node/configuration.i18n.json index dd33949d1f2..ba1ed604879 100644 --- a/i18n/rus/src/vs/workbench/services/configuration/node/configuration.i18n.json +++ b/i18n/rus/src/vs/workbench/services/configuration/node/configuration.i18n.json @@ -13,7 +13,6 @@ "invalid.title": "configuration.title должно быть строкой", "vscode.extension.contributes.defaultConfiguration": "Предоставляет параметры конфигурации редактора по умолчанию в соответствии с языком.", "invalid.properties": "configuration.properties должно быть объектом", - "workspaceConfig.folders.description": "Список папок, которые будут загружены в рабочую область. Необходимо указывать пути к файлам, например, \"/root/folderA\" или \"./folderA\" для пути по отношению к файлу рабочей области.", - "workspaceConfig.folder.description": "Путь к файлу, например, \"/root/folderA\" или \"./folderA\" для пути по отношению к файлу рабочей области.", + "workspaceConfig.path.description": "Путь к файлу, например, \"/root/folderA\" или \"./folderA\" для пути по отношению к файлу рабочей области.", "workspaceConfig.settings.description": "Параметры рабочей области" } \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/services/files/electron-browser/remoteFileService.i18n.json b/i18n/rus/src/vs/workbench/services/files/electron-browser/remoteFileService.i18n.json new file mode 100644 index 00000000000..54fbd1ada7b --- /dev/null +++ b/i18n/rus/src/vs/workbench/services/files/electron-browser/remoteFileService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileBinaryError": "Похоже, файл является двоичным, и его нельзя открыть как текстовый." +} \ No newline at end of file diff --git a/i18n/trk/extensions/css/package.i18n.json b/i18n/trk/extensions/css/package.i18n.json index c2596bffcb3..c3c20d04321 100644 --- a/i18n/trk/extensions/css/package.i18n.json +++ b/i18n/trk/extensions/css/package.i18n.json @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { + "css.title": "CSS", "css.lint.argumentsInColorFunction.desc": "Geçersiz sayıda parametre", "css.lint.boxModel.desc": "Doldurma veya kenarlık kullanırken genişlik veya yükseklik kullanmayın", "css.lint.compatibleVendorPrefixes.desc": "Satıcıya özgü bir ön ek kullanırken satıcıya özgü diğer tüm özellikleri de dahil ettiğinizden emin olun", @@ -25,6 +26,7 @@ "css.trace.server.desc": "VS Code ve CSS dil sunucusu arasındaki iletişimi izler.", "css.validate.title": "CSS doğrulamasını ve sorunların önem derecelerini denetler.", "css.validate.desc": "Tüm doğrulamaları etkinleştirir veya devre dışı bırakır", + "less.title": "LESS", "less.lint.argumentsInColorFunction.desc": "Geçersiz sayıda parametre", "less.lint.boxModel.desc": "Doldurma veya kenarlık kullanırken genişlik veya yükseklik kullanmayın", "less.lint.compatibleVendorPrefixes.desc": "Satıcıya özgü bir ön ek kullanırken satıcıya özgü diğer tüm özellikleri de dahil ettiğinizden emin olun", @@ -45,6 +47,7 @@ "less.lint.zeroUnits.desc": "Sıfır için birim gerekmez", "less.validate.title": "LESS doğrulamasını ve sorunların önem derecelerini denetler.", "less.validate.desc": "Tüm doğrulamaları etkinleştirir veya devre dışı bırakır", + "scss.title": "SCSS (Sass)", "scss.lint.argumentsInColorFunction.desc": "Geçersiz sayıda parametre", "scss.lint.boxModel.desc": "Doldurma veya kenarlık kullanırken genişlik veya yükseklik kullanmayın", "scss.lint.compatibleVendorPrefixes.desc": "Satıcıya özgü bir ön ek kullanırken satıcıya özgü diğer tüm özellikleri de dahil ettiğinizden emin olun", diff --git a/i18n/trk/src/vs/code/electron-main/menus.i18n.json b/i18n/trk/src/vs/code/electron-main/menus.i18n.json index 2a2c04a9be2..245e33677c8 100644 --- a/i18n/trk/src/vs/code/electron-main/menus.i18n.json +++ b/i18n/trk/src/vs/code/electron-main/menus.i18n.json @@ -151,6 +151,10 @@ "mZoom": "Yakınlaştırma", "mBringToFront": "Tümünü Öne Getir", "miSwitchWindow": "&&Pencere Değiştir...", + "mShowPreviousTab": "Önceki Sekmeyi Göster", + "mShowNextTab": "Sonraki Sekmeyi Göster", + "mMoveTabToNewWindow": "Sekmeyi Yeni Pencereye Taşı", + "mMergeAllWindows": "Tüm Pencereleri Birleştir", "miToggleDevTools": "&&Geliştirici Araçlarını Aç/Kapat", "miAccessibilityOptions": "&&Erişilebilirlik Seçenekleri", "miReportIssues": "So&&run Bildir", @@ -171,8 +175,8 @@ "miRunningTask": "Ça&&lışan Görevleri Göster...", "miRestartTask": "Çalışan Görevi &&Yeniden Başlat...", "miTerminateTask": "&&Görevi Sonlandır...", - "miConfigureTask": "Görevleri Ya&&pılandır", - "miConfigureBuildTask": "&&Varsayılan Derleme Görevini Yapılandır", + "miConfigureTask": "Görevleri Ya&&pılandır...", + "miConfigureBuildTask": "&&Varsayılan Derleme Görevini Yapılandır...", "accessibilityOptionsWindowTitle": "Erişilebilirlik Seçenekleri", "miRestartToUpdate": "Güncelleştirmek için Yeniden Başlat...", "miCheckingForUpdates": "Güncelleştirmeler Denetleniyor...", diff --git a/i18n/trk/src/vs/code/electron-main/windows.i18n.json b/i18n/trk/src/vs/code/electron-main/windows.i18n.json index 1331e489083..d5deb1a8663 100644 --- a/i18n/trk/src/vs/code/electron-main/windows.i18n.json +++ b/i18n/trk/src/vs/code/electron-main/windows.i18n.json @@ -17,6 +17,8 @@ "open": "Aç", "openFolder": "Klasör Aç", "openFile": "Dosya Aç", + "workspaceOpenedMessage": "Çalışma alanı '{0}' kaydedilemiyor", + "workspaceOpenedDetail": "Çalışma alanı zaten başka bir pencerede açılmış. Lütfen ilk olarak o pencereyi kapatın ve tekrar deneyin.", "openWorkspace": "&&Aç", "openWorkspaceTitle": "Çalışma Alanı Aç", "save": "&&Kaydet", diff --git a/i18n/trk/src/vs/editor/common/config/commonEditorConfig.i18n.json b/i18n/trk/src/vs/editor/common/config/commonEditorConfig.i18n.json index f638cd37b77..e5aafd9e455 100644 --- a/i18n/trk/src/vs/editor/common/config/commonEditorConfig.i18n.json +++ b/i18n/trk/src/vs/editor/common/config/commonEditorConfig.i18n.json @@ -88,6 +88,7 @@ "accessibilitySupport": "Düzenleyicinin ekran okuyucular için optimize edilmiş bir modda çalışıp çalışmayacağını denetler.", "links": "Düzenleyicinin bağlantıları otomatik algılayıp, onları tıklanabilir yapıp yapmayacağını denetler", "colorDecorators": "Düzenleyicinin satır içi renk dekoratörlerini ve renk seçiciyi gösterip göstermemesini denetler.", + "codeActions": "Kod eylemleri ampulunu etkinleştirir", "sideBySide": "Karşılaştırma düzenleyicisinin farklılıkları yan yana mı yoksa satır içinde mi göstereceğini denetler", "ignoreTrimWhitespace": "Karşılaştırma düzenleyicisinin baştaki veya sondaki boşluklardaki değişmeleri farklılık olarak gösterip göstermemesini denetler", "renderIndicators": "Karşılaştırma düzenleyicisinin ekleme/çıkarma değişiklikleri için +/- göstergeleri gösterip göstermemesini denetler.", diff --git a/i18n/trk/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json b/i18n/trk/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json index 1fc7a735690..cd79d1e6d7c 100644 --- a/i18n/trk/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json +++ b/i18n/trk/src/vs/platform/actions/electron-browser/menusExtensionPoint.i18n.json @@ -13,6 +13,7 @@ "vscode.extension.contributes.menuItem.group": "Bu komutun ait olduğu gruba ekle", "vscode.extension.contributes.menus": "Düzenleyiciye menü ögeleri ekler", "menus.commandPalette": "Komut Paleti", + "menus.touchBar": "Touch bar (sadece macOS)", "menus.editorTitle": "Düzenleyici başlık menüsü", "menus.editorContext": "Düzenleyici bağlam menüsü", "menus.explorerContext": "Dosya gezgini bağlam menüsü", diff --git a/i18n/trk/src/vs/platform/environment/node/argv.i18n.json b/i18n/trk/src/vs/platform/environment/node/argv.i18n.json index 2954082001b..0e8cbe0d850 100644 --- a/i18n/trk/src/vs/platform/environment/node/argv.i18n.json +++ b/i18n/trk/src/vs/platform/environment/node/argv.i18n.json @@ -15,6 +15,7 @@ "reuseWindow": "Bir dosya veya klasörü son etkin pencerede açmaya zorlayın.", "userDataDir": "Kullanıcı verilerinin tutulacağı klasörü belirtir, root olarak çalışırken yararlıdır.", "verbose": "Ayrıntılı çıktı oluştur (--wait anlamına gelir).", + "wait": "Geri dönmeden önce dosyaların kapanmasını bekle.", "extensionHomePath": "Eklentilerin kök dizinini belirle.", "listExtensions": "Yüklü eklentileri listele.", "showVersions": "--list-extensions'u kullanırken, yüklü eklentilerin sürümlerini gösterir.", diff --git a/i18n/trk/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json b/i18n/trk/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json index 46113aaf483..c32c5815cff 100644 --- a/i18n/trk/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json +++ b/i18n/trk/src/vs/platform/extensionManagement/node/extensionManagementService.i18n.json @@ -5,10 +5,8 @@ // Do not edit this file. It is machine generated. { "invalidManifest": "Eklenti geçersiz: package.json bir JSON dosyası değil.", - "restartCode": "{0} eklentisini yeniden yüklemeden önce lütfen Code'u yeniden başlatın.", - "installDependeciesConfirmation": "'{0}' eklentisini yüklediğinizde onun bağımlılıkları da yüklenir. Devam etmek istiyor musunuz?", - "install": "Evet", - "doNotInstall": "Hayır", + "restartCodeLocal": "{0} eklentisini yeniden yüklemeden önce lütfen Code'u yeniden başlatın.", + "restartCodeGallery": "Yeniden yüklemeden önce lütfen Code'u yeniden başlatın.", "uninstallDependeciesConfirmation": "Yalnızca '{0}' eklentisini mi yoksa bağımlılıklarını da kaldırmak ister misiniz?", "uninstallOnly": "Sadece Eklenti", "uninstallAll": "Tümü", diff --git a/i18n/trk/src/vs/platform/extensions/common/extensionsRegistry.i18n.json b/i18n/trk/src/vs/platform/extensions/common/extensionsRegistry.i18n.json index ed2a8dac4ad..bf95a5cb19e 100644 --- a/i18n/trk/src/vs/platform/extensions/common/extensionsRegistry.i18n.json +++ b/i18n/trk/src/vs/platform/extensions/common/extensionsRegistry.i18n.json @@ -16,6 +16,7 @@ "vscode.extension.activationEvents": "VS Code eklentisi için etkinleştirme olayları.", "vscode.extension.activationEvents.onLanguage": "Belirtilen dilde çözümlenen bir dosya her açıldığında bir etkinleştirme olayı yayınlanır.", "vscode.extension.activationEvents.onCommand": "Belirtilen komut her çağrıldığında bir etkinleştirme olayı yayınlanır.", + "vscode.extension.activationEvents.onDebug": "Bir kullanıcının hata ayıklamaya başlamak veya hata ayıklama yapılandırmasını ayarlamak üzere olduğu her an bir etkinleştirme olayı yayınlanır.", "vscode.extension.activationEvents.workspaceContains": "Belirtilen glob deseni ile eşleşen en az bir dosya içeren bir klasör her açıldığında bir etkinleştirme olayı yayınlanır.", "vscode.extension.activationEvents.onView": "Belirtilen görünüm her genişletildiğinde bir etkinleştirme olayı yayınlanır.", "vscode.extension.activationEvents.star": "VS Code başlatıldığında yayılan etkinleştirme olayı. Mükemmel bir son kullanıcı deneyimi sağlandığından emin olmak için, lütfen bu etkinleştirme olayını eklentinizde sadece kullanım durumunuzda başka hiçbir aktivasyon olayı kombinasyonu çalışmıyorsa kullanın.", diff --git a/i18n/trk/src/vs/platform/theme/common/colorExtensionPoint.i18n.json b/i18n/trk/src/vs/platform/theme/common/colorExtensionPoint.i18n.json index 9f47144f8a2..bf71ff9729b 100644 --- a/i18n/trk/src/vs/platform/theme/common/colorExtensionPoint.i18n.json +++ b/i18n/trk/src/vs/platform/theme/common/colorExtensionPoint.i18n.json @@ -13,6 +13,8 @@ "contributes.defaults.highContrast": "Yüksek karşıtlık temalarının varsayılan rengi. Ya hex biçiminde bir renk değeri (#RRGGBB[AA]) ya da varsayılanı sağlayan bir tema olarak kullanılabilir rengin tanımlayıcısı olabilir.", "invalid.colorConfiguration": "'configuration.colors' bir dizi olmalıdır", "invalid.default.colorType": "{0}, ya hex biçiminde bir renk değeri (#RRGGBB[AA] veya #RGB[A]) ya da varsayılanı sağlayan bir tema olarak kullanılabilir rengin tanımlayıcısı olabilir.", + "invalid.id": "'configuration.colors.id' tanımlanmalı ve boş olmamalıdır", "invalid.id.format": "'configuration.colors.id' sözcük[.sözcük]* şeklinde olmalıdır", + "invalid.description": "'configuration.colors.description' tanımlanmalı ve boş olmamalıdır", "invalid.defaults": "'configuration.colors.defaults' tanımlanmalı ve 'light', 'dark' ve 'highContrast' değerlerini içermelidir" } \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/browser/actions/workspaceActions.i18n.json b/i18n/trk/src/vs/workbench/browser/actions/workspaceActions.i18n.json index 543577ab634..ed0caa5b58f 100644 --- a/i18n/trk/src/vs/workbench/browser/actions/workspaceActions.i18n.json +++ b/i18n/trk/src/vs/workbench/browser/actions/workspaceActions.i18n.json @@ -14,9 +14,9 @@ "selectWorkspace": "Çalışma Alanı İçin Klasörleri Seçin", "removeFolderFromWorkspace": "Çalışma Alanından Klasör Kaldır", "saveWorkspaceAsAction": "Çalışma Alanını Farklı Kaydet...", - "saveEmptyWorkspaceNotSupported": "Lütfen kaydetmek için ilk olarak bir çalışma alanı açın.", "save": "&&Kaydet", "saveWorkspace": "Çalışma Alanını Kaydet", "openWorkspaceAction": "Çalışma Alanı Aç...", - "openWorkspaceConfigFile": "Çalışma Alanı Yapılandırma Dosyasını Aç" + "openWorkspaceConfigFile": "Çalışma Alanı Yapılandırma Dosyasını Aç", + "workspaceFolderPickerPlaceholder": "Çalışma alanı klasörü seçin" } \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json b/i18n/trk/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json index 087f6d6789f..4dac5bcdce6 100644 --- a/i18n/trk/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json +++ b/i18n/trk/src/vs/workbench/browser/parts/activitybar/activitybarActions.i18n.json @@ -6,6 +6,7 @@ { "badgeTitle": "{0} - {1}", "titleKeybinding": "{0} ({1})", + "removeFromActivityBar": "Etkinlik Çubuğunda Gizle", "keepInActivityBar": "Etkinlik Çubuğunda Tut", "additionalViews": "Ek Görünümler", "numberBadge": "{0} ({1})", diff --git a/i18n/trk/src/vs/workbench/browser/parts/editor/editorActions.i18n.json b/i18n/trk/src/vs/workbench/browser/parts/editor/editorActions.i18n.json index 54fedd4e20f..a5fd9ca0033 100644 --- a/i18n/trk/src/vs/workbench/browser/parts/editor/editorActions.i18n.json +++ b/i18n/trk/src/vs/workbench/browser/parts/editor/editorActions.i18n.json @@ -35,6 +35,7 @@ "openPreviousEditorInGroup": "Gruptaki Önceki Düzenleyiciyi Aç", "navigateNext": "İleri Git", "navigatePrevious": "Geri Dön", + "navigateLast": "Bir Öncekine Git", "reopenClosedEditor": "Kapatılan Düzenleyiciyi Yeniden Aç", "clearRecentFiles": "Son Açılanları Temizle", "showEditorsInFirstGroup": "İlk Gruptaki Düzenleyicileri Göster", diff --git a/i18n/trk/src/vs/workbench/browser/parts/views/panelViewlet.i18n.json b/i18n/trk/src/vs/workbench/browser/parts/views/panelViewlet.i18n.json new file mode 100644 index 00000000000..13520a7bd48 --- /dev/null +++ b/i18n/trk/src/vs/workbench/browser/parts/views/panelViewlet.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "viewToolbarAriaLabel": "{0} eylem" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/electron-browser/actions.i18n.json b/i18n/trk/src/vs/workbench/electron-browser/actions.i18n.json index 6e119bd4a9c..84919f4ec14 100644 --- a/i18n/trk/src/vs/workbench/electron-browser/actions.i18n.json +++ b/i18n/trk/src/vs/workbench/electron-browser/actions.i18n.json @@ -42,5 +42,10 @@ "navigateUp": "Üstteki Görünüme Git", "navigateDown": "Alttaki Görünüme Git", "increaseViewSize": "Geçerli Görünüm Boyutunu Artır", - "decreaseViewSize": "Geçerli Görünüm Boyutunu Azalt" + "decreaseViewSize": "Geçerli Görünüm Boyutunu Azalt", + "showPreviousTab": "Önceki Pencere Sekmesini Göster", + "showNextWindowTab": "Sonraki Pencere Sekmesini Göster", + "moveWindowTabToNewWindow": "Pencere Sekmesini Yeni Pencereye Taşı", + "mergeAllWindowTabs": "Tüm Pencereleri Birleştir", + "toggleWindowTabsBar": "Pencere Sekmeleri Çubuğunu Gizle/Göster" } \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/electron-browser/main.contribution.i18n.json b/i18n/trk/src/vs/workbench/electron-browser/main.contribution.i18n.json index 9681d8dffc6..3bbf02cf526 100644 --- a/i18n/trk/src/vs/workbench/electron-browser/main.contribution.i18n.json +++ b/i18n/trk/src/vs/workbench/electron-browser/main.contribution.i18n.json @@ -10,6 +10,11 @@ "workspaces": "Çalışma Alanları", "developer": "Geliştirici", "showEditorTabs": "Açık düzenleyicilerin sekmelerde gösterilip gösterilmeyeceğini denetler", + "workbench.editor.labelFormat.default": "Dosyanın adını göster. Sekmeler etkinleştirilmiş ve bir grupta iki dosya aynı ada sahiplerse, her dosyanın yolundaki ayırt edici bölümler eklenir. Sekmeler devre dışı ve düzenleyici aktifse, çalışma alanı kök klasörüne göreli yol gösterilir.", + "workbench.editor.labelFormat.short": "Dosyanın adını ve ardından dizin adını göster.", + "workbench.editor.labelFormat.medium": "Dosyanın adını ve ardından çalışma alanı kök klasörüne göreli yolunu göster.", + "workbench.editor.labelFormat.long": "Dosyanın adını ve ardından mutlak yolunu göster.", + "tabDescription": "Bir düzenleyici için etiketin biçimini denetler. Bu ayarı değiştirmek; örneğin, bir dosyanın konumunun daha kolay anlaşılmasını sağlar:\n- short: 'ustklasor'\n- medium: 'calismaalani/src/ustklasor'\n- long: '/home/user/calismaalani/src/ustklasor'\n- default: diğer bir sekme aynı başlığı paylaşıyorsa '.../ustklasor' veya sekmeler devre dışı ise göreli çalışma alanı yolu", "editorTabCloseButton": "Düzenleyici sekmelerinin kapat butonlarının konumunu denetler veya 'off' olarak ayarlandığında devre dışı bırakır.", "showIcons": "Açık düzenleyicilerin bir simge ile gösterilip gösterilmemelerini denetler. Bu, bir simge temasının etkinleştirilmesini de gerektirir.", "enablePreview": "Açık düzenleyicilerin önizleme olarak gösterilip gösterilmeyeceğini denetler. Önizleme düzenleyicileri kalıcı olarak açılana kadar (ör. çift tıklama veya düzenleme ile) tekrar kullanılırlar.", diff --git a/i18n/trk/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json b/i18n/trk/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json index efc71dc00cf..aa4162006c6 100644 --- a/i18n/trk/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json +++ b/i18n/trk/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.i18n.json @@ -36,5 +36,7 @@ "schema.indentationRules.unIndentedLinePattern": "Bir satır bu kalıpla eşleşirse, o satırın girintisi değiştirilmemelidir ve diğer kurallara karşı değerlendirilmemelidir.", "schema.indentationRules.unIndentedLinePattern.pattern": "unIndentedLinePattern için Düzenli İfade.", "schema.indentationRules.unIndentedLinePattern.flags": "unIndentedLinePattern için Düzenli İfade işaretleri.", - "schema.indentationRules.unIndentedLinePattern.errorMessage": "`/^([gimuy]+)$/` kalıbı ile eşleşmelidir." + "schema.indentationRules.unIndentedLinePattern.errorMessage": "`/^([gimuy]+)$/` kalıbı ile eşleşmelidir.", + "schema.folding": "Dilin katlama ayarları.", + "schema.folding.offSide": "Bir dildeki bloklar girintilendirmeleriyle ifade edilirse, o dil için off-side kuralı uygulanır. Eğer ayarlanırsa, boş satırlar sonraki bloğa ait olur." } \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json b/i18n/trk/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json index c7e63743c9f..48c31c797e6 100644 --- a/i18n/trk/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json +++ b/i18n/trk/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json @@ -12,6 +12,8 @@ "breakpointRemoved": "Kesme noktası kaldırıldı, {0}. satır, {1} dosyası", "compoundMustHaveConfigurations": "Bileşik, birden çok yapılandırmayı başlatmak için \"configurations\" özniteliği bulundurmalıdır.", "configMissing": "'launch.json' dosyasında '{0}' yapılandırması eksik.", + "debugRequestNotSupported": "Yapılandırılan hata ayıklama isteği '{0}' desteklenmiyor.", + "debugRequesMissing": "Hata ayıklama isteği, seçilen başlatma yapılandırılmasında eksik", "debugTypeNotSupported": "Yapılandırılan hata ayıklama türü '{0}', desteklenmiyor.", "debugTypeMissing": "Seçilen başlatma yapılandırması için 'type' özelliği eksik.", "preLaunchTaskErrors": "'{0}' ön başlatma görevi sırasında derleme hataları algılandı.", diff --git a/i18n/trk/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json b/i18n/trk/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json index 39b6be390d3..4c3d5d2ef5f 100644 --- a/i18n/trk/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json +++ b/i18n/trk/src/vs/workbench/parts/extensions/browser/extensionEditor.i18n.json @@ -29,6 +29,13 @@ "view id": "ID", "view name": "Adı", "view location": "Yeri", + "colorThemes": "Renk Temaları ({0})", + "iconThemes": "Simge Temaları ({0})", + "colors": "Renkler ({0})", + "colorId": "Kimlik", + "defaultDark": "Koyu Varsayılan", + "defaultLight": "Açık Varsayılan", + "defaultHC": "Yüksek Karşıtlık Varsayılan", "JSON Validation": "JSON Doğrulama ({0})", "commands": "Komutlar ({0})", "command name": "Adı", diff --git a/i18n/trk/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json b/i18n/trk/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json index 71c438668c3..aae0444fd86 100644 --- a/i18n/trk/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json +++ b/i18n/trk/src/vs/workbench/parts/extensions/browser/extensionsActions.i18n.json @@ -51,9 +51,9 @@ "showLanguageExtensionsShort": "Dil Eklentileri", "showAzureExtensions": "Azure Eklentilerini Göster", "showAzureExtensionsShort": "Azure Eklentileri", - "configureWorkspaceRecommendedExtensions": "Tavsiye Edilen Eklentileri Yapılandır (Çalışma Alanı)", - "ConfigureWorkspaceRecommendations.noWorkspace": "Tavsiyeler, sadece çalışma alanı klasöründe mevcuttur.", "OpenExtensionsFile.failed": " '.vscode' klasörü içinde 'extensions.json' dosyası oluşturulamıyor ({0}).", + "configureWorkspaceRecommendedExtensions": "Tavsiye Edilen Eklentileri Yapılandır (Çalışma Alanı)", + "configureWorkspaceFolderRecommendedExtensions": "Tavsiye Edilen Eklentileri Yapılandır (Çalışma Alanı Klasörü)", "builtin": "Yerleşik", "disableAll": "Yüklü Tüm Eklentileri Devre Dışı Bırak", "disableAllWorkspace": "Bu Çalışma Alanı için Yüklü Tüm Eklentileri Devre Dışı Bırak", diff --git a/i18n/trk/src/vs/workbench/parts/files/browser/files.contribution.i18n.json b/i18n/trk/src/vs/workbench/parts/files/browser/files.contribution.i18n.json index dbc5b99d24f..55fe27e5276 100644 --- a/i18n/trk/src/vs/workbench/parts/files/browser/files.contribution.i18n.json +++ b/i18n/trk/src/vs/workbench/parts/files/browser/files.contribution.i18n.json @@ -14,6 +14,8 @@ "files.exclude.boolean": "Dosya yollarının eşleştirileceği glob deseni. Deseni etkinleştirmek veya devre dışı bırakmak için true veya false olarak ayarlayın.", "files.exclude.when": "Eşleşen bir dosyanın eşdüzey dosyalarında ek denetim. Eşleşen dosya adı için değişken olarak $(basename) kullanın.", "associations": "Dillerle dosya ilişkilendirmelerini yapılandırın (ör. \"*.uzanti\": \"html\"). Bunların, kurulu olan dillerin varsayılan ilişkilendirmeleri karşısında önceliği vardır.", + "encoding": "Dosyalar okunurken ve yazılırken kullanılacak varsayılan karakter kümesi kodlaması. Bu ayar her bir dil için de yapılandırılabilir.", + "autoGuessEncoding": "Etkinleştirildiğinde, dosyaları açarken karakter kümesini tahmin etmeye çalışır. Bu ayar her bir dil için de yapılandırılabilir.", "eol": "Varsayılan satır sonu karakteri. LF için \\n ve CRLF için \\r\\n kullan.", "trimTrailingWhitespace": "Etkinleştirildiğinde, bir dosyayı kaydettiğinizde sondaki boşluk kırpılır.", "insertFinalNewline": "Etkinleştirildiğinde, bir dosyayı kaydederken dosya sonuna bir boş satır ekler.", diff --git a/i18n/trk/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json b/i18n/trk/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json index 0f88a5b2332..1a8d190f659 100644 --- a/i18n/trk/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json +++ b/i18n/trk/src/vs/workbench/parts/files/common/dirtyFilesTracker.i18n.json @@ -4,5 +4,6 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { + "dirtyFile": "1 kaydedilmemiş dosya", "dirtyFiles": "{0} kaydedilmemiş dosya" } \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json b/i18n/trk/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json index 37e5169f3a8..fad9ab4f40b 100644 --- a/i18n/trk/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json +++ b/i18n/trk/src/vs/workbench/parts/preferences/browser/preferencesActions.i18n.json @@ -9,7 +9,6 @@ "openGlobalKeybindingsFile": "Klavye Kısayolları Dosyasını Aç", "openWorkspaceSettings": "Çalışma Alanı Ayarlarını Aç", "openFolderSettings": "Klasör Ayarlarını Aç", - "pickFolder": "Klasör Seç", "configureLanguageBasedSettings": "Dile Özel Ayarları Yapılandır...", "languageDescriptionConfigured": "({0})", "pickLanguage": "Dili Seç" diff --git a/i18n/trk/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json b/i18n/trk/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json index 31778d735c7..3375526320d 100644 --- a/i18n/trk/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json +++ b/i18n/trk/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.i18n.json @@ -6,7 +6,5 @@ { "relaunchSettingMessage": "Yürürlüğe girmesi için yeniden başlatma gerektiren bir ayar değişti.", "relaunchSettingDetail": "{0} uygulamasını yeniden başlatmak ve bu ayarı etkinleştirmek için lütfen yeniden başlat butonuna basın.", - "restart": "Yeniden Başlat", - "relaunchWorkspaceMessage": "Bu çalışma alanı değişikliği eklenti sistemimizi yeniden başlatmayı gerektirir.", - "reload": "Yeniden Yükle" + "restart": "Yeniden Başlat" } \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json b/i18n/trk/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json index 1aac20564f2..1b8eebb304e 100644 --- a/i18n/trk/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json +++ b/i18n/trk/src/vs/workbench/parts/scm/electron-browser/scmViewlet.i18n.json @@ -4,8 +4,10 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { + "scm providers": "Kaynak Kontrolü Sağlayıcıları", "commitMessage": "Mesaj (commit'lemek için {0} tuşlarına basın)", "installAdditionalSCMProviders": "Ek SCM Sağlayıcıları Yükle...", + "no open repo": "Aktif bir kaynak kontrolü sağlayıcısı yok.", "source control": "Kaynak Kontrolü", "viewletTitle": "{0}: {1}" } \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json b/i18n/trk/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json index c8734bde48f..e8d5b2a4732 100644 --- a/i18n/trk/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json +++ b/i18n/trk/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.i18n.json @@ -4,5 +4,7 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "snippet.suggestions.label": "Parçacık Ekle" + "snippet.suggestions.label": "Parçacık Ekle", + "sep.userSnippet": "Kullanıcı Parçacıkları", + "sep.extSnippet": "Eklenti Parçacıkları" } \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json b/i18n/trk/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json index 432caec4bb9..52c84a18c00 100644 --- a/i18n/trk/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json +++ b/i18n/trk/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json @@ -10,6 +10,7 @@ "vscode.extension.contributes.snippets": "Parçacıklara ekleme yapar.", "vscode.extension.contributes.snippets-language": "Bu parçacığın ekleneceği dilin tanımlayıcısı.", "vscode.extension.contributes.snippets-path": "Parçacıklar dosyasının yolu. Yol, eklenti klasörüne görecelidir ve genellikle './snippets/' ile başlar.", + "badFile": "Parçacık dosyası \"{0}\" okunamadı.", "badVariableUse": "\"{0}\"-parçacığı yüksek olasılıkla parçacık değişkenleri ile parçacık yer tutucularını karıştırıyor. Daha fazla bilgi için https://code.visualstudio.com/docs/editor/userdefinedsnippets#_snippet-syntax adresini ziyaret edin.", "source.snippet": "Kullanıcı Parçacığı", "detail.snippet": "{0} ({1})", diff --git a/i18n/trk/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json b/i18n/trk/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json index 8998948fe3b..1821c3a3b7c 100644 --- a/i18n/trk/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json +++ b/i18n/trk/src/vs/workbench/parts/tasks/electron-browser/task.contribution.i18n.json @@ -5,13 +5,7 @@ // Do not edit this file. It is machine generated. { "tasksCategory": "Görevler", - "ConfigureTaskRunnerAction.noWorkspace": "Görevler, sadece çalışma alanı klasöründe mevcuttur.", - "ConfigureTaskRunnerAction.quickPick.template": "Bir Görev Çalıştırıcısı Seç", - "ConfigureTaskRunnerAction.autoDetecting": "{0} görevleri otomatik algılanıyor", - "ConfigureTaskRunnerAction.autoDetect": "Görev sisteminin otomatik algılanması başarısız oldu. Varsayılan şablon kullanılıyor. Ayrıntılar için görev çıktısına bakın.", - "ConfigureTaskRunnerAction.autoDetectError": "Görev sisteminin otomatik algılanması sırasında hatalar oluştu. Ayrıntılar için görev çıktısına bakın.", - "ConfigureTaskRunnerAction.failed": " '.vscode' klasörü içinde 'tasks.json' dosyası oluşturulamıyor. Ayrıntılar için görev çıktısına bakın.", - "ConfigureTaskRunnerAction.label": "Görev Çalıştırıcısını Yapılandır", + "ConfigureTaskRunnerAction.label": "Görevi Yapılandır", "ConfigureBuildTaskAction.label": "Derleme Görevini Yapılandır", "CloseMessageAction.label": "Kapat", "ShowTerminalAction.label": "Terminali Görüntüle", @@ -19,11 +13,13 @@ "manyMarkers": "99+", "runningTasks": "Çalışan Görevleri Göster", "tasks": "Görevler", + "TaskSystem.noHotSwap": "Aktif bir görev çalıştıran görev yürütme motorunu değiştirmek pencereyi yeniden yüklemeyi gerektirir", "TaskService.noBuildTask1": "Derleme görevi tanımlanmamış. tasks.json dosyasındaki bir görevi 'isBuildCommand' ile işaretleyin.", "TaskService.noBuildTask2": "Derleme görevi tanımlanmamış. tasks.json dosyasındaki bir görevi, bir 'build' grubu olarak işaretleyin.", "TaskService.noTestTask1": "Test görevi tanımlanmamış. tasks.json dosyasındaki bir testi 'isTestCommand' ile işaretleyin.", "TaskService.noTestTask2": "Test görevi tanımlanmamış. tasks.json dosyasındaki bir görevi, bir 'test' grubu olarak işaretleyin.", "TaskServer.noTask": " Çalıştırılmak istenen {0} görevi bulunamadı.", + "TaskService.associate": "ilişkili", "TaskService.attachProblemMatcher.continueWithout": "Görev çıktısını taramadan devam et", "TaskService.attachProblemMatcher.never": "Hiçbir zaman görev çıktısını tarama", "TaskService.attachProblemMatcher.learnMoreAbout": "Görev çıktısını tarama hakkında daha fazla bilgi edin", @@ -35,6 +31,7 @@ "TaskSystem.active": "Çalışan bir görev zaten var. Bir başkasını çalıştırmadan önce bu görevi sonlandırın.", "TaskSystem.restartFailed": "{0} görevini sonlandırma ve yeniden başlatma başarısız oldu", "TaskSystem.configurationErrors": "Hata: belirtilen görev yapılandırmasında doğrulama hataları var ve kullanılamıyor. Lütfen ilk olarak hataları düzeltin.", + "taskService.ignoreingFolder": "{0} çalışma alanı klasörü için görev yapılandırmaları yok sayılıyor. Çoklu kök klasör desteği, tüm klasörlerin 2.0.0 görev sürümünü kullanmasını gerektirir\n", "TaskSystem.invalidTaskJson": "Hata: tasks.json dosyasının içeriğinde sentaks hataları var. Lütfen, bir görevi çalıştırmadan önce hataları düzeltin.\n", "TaskSystem.runningTask": "Çalışan bir görev var. Bu görevi sonlandırmak istiyor musunuz?", "TaskSystem.terminateTask": "&&Görevi Sonlandır", @@ -46,24 +43,30 @@ "recentlyUsed": "yakınlarda kullanılan görevler", "configured": "yapılandırılmış görevler", "detected": "algılanan görevler", + "TaskService.pickRunTask": "Çalıştırılacak görevi seçin", + "TaslService.noEntryToRun": "Çalıştırılacak hiçbir görev bulunamadı. Görevleri Yapılandır...", "TaskService.fetchingBuildTasks": "Derleme görevleri alınıyor...", - "TaskService.noBuildTaskTerminal": "Derleme görevi bulunamadı. Yeni bir tane tanımlamak için 'Derleme Görevini Yapılandır'a basın.", "TaskService.pickBuildTask": "Çalıştırılacak derleme görevini seçin", + "TaskService.noBuildTask": "Çalıştırılacak hiçbir derleme görevi bulunamadı. Görevleri Yapılandır...", "TaskService.fetchingTestTasks": "Test görevleri alınıyor...", - "TaskService.noTestTaskTerminal": "Test görevi bulunamadı. Yeni bir tane tanımlamak için 'Görev Çalıştırıcısını Yapılandır'a basın.", "TaskService.pickTestTask": "Çalıştırılacak test görevini seçin", - "TaskService.noTaskRunning": "Şu an çalışan bir görev yok.", + "TaskService.noTestTaskTerminal": "Çalıştırılacak hiçbir test görevi bulunamadı. Görevleri Yapılandır...", "TaskService.tastToTerminate": "Sonlandırılacak görevi seçin", + "TaskService.noTaskRunning": "Şu an çalışan bir görev yok", "TerminateAction.noProcess": "Başlatılan işlem artık mevcut değil. Eğer görev arka plan görevleri oluşturduysa, VS Code'dan çıkmak işlemlerin sahipsiz kalmasına neden olabilir.", "TerminateAction.failed": "Çalışan görevi sonlandırma başarısız oldu.", - "TaskService.noTaskToRestart": "Yeniden başlatılacak bir görev yok.", "TaskService.tastToRestart": "Yeniden başlatılacak görevi seçin", - "TaskService.defaultBuildTaskExists": "{0} zaten varsayılan derleme görevi olarak işaretlenmiş.", + "TaskService.noTaskToRestart": "Yeniden başlatılacak bir görev yok", + "TaskService.template": "Bir Görev Şablonu Seçin", + "TaskService.createJsonFile": "Şablondan tasks.json dosyası oluştur", + "TaskService.openJsonFile": "tasks.json dosyasını aç", + "TaskService.pickTask": "Yapılandırmak için bir görev seçin", + "TaskService.defaultBuildTaskExists": "{0} zaten varsayılan derleme görevi olarak işaretlenmiş", "TaskService.pickDefaultBuildTask": "Varsayılan derleme görevi olarak kullanılacak görevi seçin", "TaskService.defaultTestTaskExists": "{0} zaten varsayılan test görevi olarak işaretlenmiş.", "TaskService.pickDefaultTestTask": "Varsayılan test görevi olarak kullanılacak görevi seçin", - "TaskService.noTaskIsRunning": "Çalışan bir görev yok.", "TaskService.pickShowTask": "Çıktısını göstermek için görev seçin", + "TaskService.noTaskIsRunning": "Çalışan bir görev yok", "ShowLogAction.label": "Görev Günlüğünü Göster", "RunTaskAction.label": "Görevi Çalıştır", "RestartTaskAction.label": "Çalışan Görevi Yeniden Başlat", diff --git a/i18n/trk/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json b/i18n/trk/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json index d3492257f35..2e339c2d029 100644 --- a/i18n/trk/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json +++ b/i18n/trk/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.i18n.json @@ -5,6 +5,7 @@ // Do not edit this file. It is machine generated. { "TerminalTaskSystem.unknownError": "Görev çalıştırılırken bir hata oluştu. Detaylar için görev çıktısı günlüğüne bakın.", + "dependencyFailed": "'{1}' çalışma alanı klasöründe, '{0}' bağımlı görevi çözümlenemiyor", "TerminalTaskSystem.terminalName": "Görev - {0}", "reuseTerminal": "Terminal görevler tarafından tekrar kullanılacak, kapatmak için herhangi bir tuşa basın.", "TerminalTaskSystem": "UNC sürücüsünde kabuk komutu yürütülemez.", diff --git a/i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json b/i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json index 8325d1451ce..8d8d4cb77d3 100644 --- a/i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json +++ b/i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json @@ -38,5 +38,6 @@ "workbench.action.terminal.focusFindWidget": "Bulma Aracına Odakla", "workbench.action.terminal.hideFindWidget": "Bulma Aracını Gizle", "nextTerminalFindTerm": "Sonraki Arama Terimini Göster", - "previousTerminalFindTerm": "Önceki Arama Terimini Göster" + "previousTerminalFindTerm": "Önceki Arama Terimini Göster", + "quickOpenTerm": "Aktif Terminali Değiştir" } \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json b/i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json index 69009eea2db..a994ebe0909 100644 --- a/i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json +++ b/i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminalColorRegistry.i18n.json @@ -8,5 +8,6 @@ "terminal.foreground": "Terminalin ön plan rengi.", "terminalCursor.foreground": "Terminal imlecinin ön plan rengi.", "terminalCursor.background": "Terminal imlecinin arka plan rengi. Bir blok imlecinin kapladığı bir karakterin rengini özelleştirmeyi sağlar.", + "terminal.selectionBackground": "Terminalin seçim arkaplanı rengi.", "terminal.ansiColor": "Terminalde '{0}' ANSI rengi." } \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json b/i18n/trk/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json index 8098995a486..5370096e708 100644 --- a/i18n/trk/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json +++ b/i18n/trk/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.i18n.json @@ -10,6 +10,7 @@ "welcomePage.newFile": "Yeni dosya", "welcomePage.openFolder": "Klasör aç...", "welcomePage.cloneGitRepository": "Git deposu kopyala...", + "welcomePage.addWorkspaceFolder": "Çalışma alanı klasörü ekle...", "welcomePage.recent": "Son Kullanılanlar", "welcomePage.moreRecent": "Diğerleri...", "welcomePage.noRecentFolders": "Son kullanılan klasör yok", diff --git a/i18n/trk/src/vs/workbench/services/configuration/node/configuration.i18n.json b/i18n/trk/src/vs/workbench/services/configuration/node/configuration.i18n.json index 22fb2b8b560..a2e1bebe9d9 100644 --- a/i18n/trk/src/vs/workbench/services/configuration/node/configuration.i18n.json +++ b/i18n/trk/src/vs/workbench/services/configuration/node/configuration.i18n.json @@ -13,7 +13,11 @@ "invalid.title": "'configuration.title' bir dize olmalıdır", "vscode.extension.contributes.defaultConfiguration": "Varsayılan düzenleyici yapılandırma ayarlarına dil bazında ekleme yapar.", "invalid.properties": "'configuration.properties' bir nesne olmalıdır", - "workspaceConfig.folders.description": "Çalışma alanında yüklenecek klasörler listesi. Bir dosya yolu olmalıdır. ör. `/root/folderA` veya çalışma alanı dosyasının konumuna karşı çözümlenecek göreceli bir yol için `./folderA`.", - "workspaceConfig.folder.description": "Bir dosya yolu. ör. `/root/folderA` veya çalışma alanı dosyasının konumuna karşı çözümlenecek göreceli bir yol için `./folderA`.", - "workspaceConfig.settings.description": "Çalışma alanı ayarları" + "invalid.allOf": "'configuration.allOf' kullanım dışıdır ve artık kullanılmamalıdır. Bunun yerine, birden çok yapılandırma bölümlerini bir dizi olarak 'configuration' ekleme noktasına geçirin.", + "workspaceConfig.folders.description": "Çalışma alanına yüklenecek klasörler listesi.", + "workspaceConfig.path.description": "Bir dosya yolu. ör. `/root/folderA` veya çalışma alanı dosyasının konumuna karşı çözümlenecek göreceli bir yol için `./folderA`.", + "workspaceConfig.name.description": "Klasör için isteğe bağlı bir ad.", + "workspaceConfig.uri.description": "Klasörün URI'si", + "workspaceConfig.settings.description": "Çalışma alanı ayarları", + "workspaceConfig.extensions.description": "Çalışma alanı eklentileri" } \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/services/files/electron-browser/remoteFileService.i18n.json b/i18n/trk/src/vs/workbench/services/files/electron-browser/remoteFileService.i18n.json new file mode 100644 index 00000000000..320c074b5d2 --- /dev/null +++ b/i18n/trk/src/vs/workbench/services/files/electron-browser/remoteFileService.i18n.json @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "fileBinaryError": "Dosya ikili olarak görünüyor ve metin olarak açılamıyor" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/services/files/node/fileService.i18n.json b/i18n/trk/src/vs/workbench/services/files/node/fileService.i18n.json index 8b8dee30b02..0c8d70cb473 100644 --- a/i18n/trk/src/vs/workbench/services/files/node/fileService.i18n.json +++ b/i18n/trk/src/vs/workbench/services/files/node/fileService.i18n.json @@ -5,10 +5,12 @@ // Do not edit this file. It is machine generated. { "fileInvalidPath": "Geçersiz dosya kaynağı ({0})", + "fileIsDirectoryError": "Dosya bir dizindir", "fileNotModifiedError": "Dosya şu tarihten beri değiştirilmemiş:", "fileTooLargeError": "Dosya, açmak için çok büyük", "fileBinaryError": "Dosya ikili olarak görünüyor ve metin olarak açılamıyor", "fileNotFoundError": "Dosya bulunamadı ({0})", + "fileExists": "Oluşturulacak dosya zaten mevcut ({0})", "fileMoveConflict": "Taşıma/kopyalama yapılamadı. Dosya, hedefte zaten mevcut.", "unableToMoveCopyError": "Taşıma/kopyalama yapılamadı. Dosya, içinde bulunduğu klasörü değiştiriyor.", "foldersCopyError": "Klasörler çalışma alanına kopyalanamaz. Lütfen kopyalamak için dosyaları tek tek seçin.", diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index ad603585240..e5f51982d1d 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -574,7 +574,7 @@ "xterm": { "version": "3.0.0", "from": "Tyriar/xterm.js#vscode-release/1.17", - "resolved": "git+https://github.com/Tyriar/xterm.js.git#35088059e61ba654ac78df453633c7a9272ed8bd" + "resolved": "git+https://github.com/Tyriar/xterm.js.git#875b219802d116106d3e05a1731bf895bc95851b" }, "yauzl": { "version": "2.8.0", diff --git a/package.json b/package.json index bea37d0dda1..dba3a582d9e 100644 --- a/package.json +++ b/package.json @@ -132,4 +132,4 @@ "windows-mutex": "^0.2.0", "fsevents": "0.3.8" } -} \ No newline at end of file +} diff --git a/resources/linux/debian/postrm.template b/resources/linux/debian/postrm.template index c43a2b16ae3..1dfa892a0ea 100755 --- a/resources/linux/debian/postrm.template +++ b/resources/linux/debian/postrm.template @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. diff --git a/scripts/env.sh b/scripts/env.sh index 35d09f66bb2..f530bf28369 100755 --- a/scripts/env.sh +++ b/scripts/env.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash export npm_config_disturl=https://atom.io/download/electron export npm_config_target=$(node -p "require('./package.json').electronVersion") export npm_config_runtime=electron diff --git a/scripts/npm.sh b/scripts/npm.sh index 69c6d0c48ae..02268eafd6e 100755 --- a/scripts/npm.sh +++ b/scripts/npm.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash if [[ "$OSTYPE" == "darwin"* ]]; then realpath() { [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"; } diff --git a/scripts/test-integration.sh b/scripts/test-integration.sh index d0154a4101d..2bfd21a20c7 100755 --- a/scripts/test-integration.sh +++ b/scripts/test-integration.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -e if [[ "$OSTYPE" == "darwin"* ]]; then diff --git a/scripts/test-mocha.sh b/scripts/test-mocha.sh index 9aa16fa3241..5d1d71a2da2 100755 --- a/scripts/test-mocha.sh +++ b/scripts/test-mocha.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash if [[ "$OSTYPE" == "darwin"* ]]; then realpath() { [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"; } diff --git a/scripts/test.sh b/scripts/test.sh index ce1e5e11856..157c6da2cc7 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash if [[ "$OSTYPE" == "darwin"* ]]; then diff --git a/src/bootstrap.js b/src/bootstrap.js index dad68bf5c80..af713da7509 100644 --- a/src/bootstrap.js +++ b/src/bootstrap.js @@ -14,10 +14,11 @@ process.noAsar = true; if (!!process.send && process.env.PIPE_LOGGING === 'true') { var MAX_LENGTH = 100000; - // Prevent circular stringify - function safeStringify(args) { + // Prevent circular stringify and convert arguments to real array + function safeToArray(args) { var seen = []; var res; + var argsArray = []; // Massage some arguments with special treatment if (args.length) { @@ -40,11 +41,20 @@ if (!!process.send && process.env.PIPE_LOGGING === 'true') { args[i] = errorObj.toString(); } } + + argsArray.push(args[i]); } } + // Add the stack trace as payload if we are told so. We remove the message and the 2 top frames + // to start the stacktrace where the console message was being written + if (process.env.VSCODE_LOG_STACK === 'true') { + const stack = new Error().stack; + argsArray.push({ __$stack: stack.split('\n').slice(3).join('\n') }); + } + try { - res = JSON.stringify(args, function (key, value) { + res = JSON.stringify(argsArray, function (key, value) { // Objects get special treatment to prevent circles if (value && Object.prototype.toString.call(value) === '[object Object]') { @@ -78,16 +88,16 @@ if (!!process.send && process.env.PIPE_LOGGING === 'true') { // Pass console logging to the outside so that we have it in the main side if told so if (process.env.VERBOSE_LOGGING === 'true') { - console.log = function () { safeSend({ type: '__$console', severity: 'log', arguments: safeStringify(arguments) }); }; - console.info = function () { safeSend({ type: '__$console', severity: 'log', arguments: safeStringify(arguments) }); }; - console.warn = function () { safeSend({ type: '__$console', severity: 'warn', arguments: safeStringify(arguments) }); }; + console.log = function () { safeSend({ type: '__$console', severity: 'log', arguments: safeToArray(arguments) }); }; + console.info = function () { safeSend({ type: '__$console', severity: 'log', arguments: safeToArray(arguments) }); }; + console.warn = function () { safeSend({ type: '__$console', severity: 'warn', arguments: safeToArray(arguments) }); }; } else { console.log = function () { /* ignore */ }; console.warn = function () { /* ignore */ }; console.info = function () { /* ignore */ }; } - console.error = function () { safeSend({ type: '__$console', severity: 'error', arguments: safeStringify(arguments) }); }; + console.error = function () { safeSend({ type: '__$console', severity: 'error', arguments: safeToArray(arguments) }); }; } if (!process.env['VSCODE_ALLOW_IO']) { diff --git a/src/vs/base/common/glob.ts b/src/vs/base/common/glob.ts index cc14eea9d51..fb9ce79e05d 100644 --- a/src/vs/base/common/glob.ts +++ b/src/vs/base/common/glob.ts @@ -16,6 +16,11 @@ export interface IExpression { [pattern: string]: boolean | SiblingClause | any; } +export interface IRelativePattern { + base: string; + pattern: string; +} + export function getEmptyExpression(): IExpression { return Object.create(null); } @@ -28,6 +33,8 @@ export interface SiblingClause { when: string; } +const GLOBSTAR = '**'; +const GLOB_SPLIT = '/'; const PATH_REGEX = '[/\\\\]'; // any slash or backslash const NO_PATH_REGEX = '[^/\\\\]'; // any non-slash and non-backslash const ALL_FORWARD_SLASHES = /\//g; @@ -103,10 +110,10 @@ function parseRegExp(pattern: string): string { let regEx = ''; // Split up into segments for each slash found - let segments = splitGlobAware(pattern, '/'); + let segments = splitGlobAware(pattern, GLOB_SPLIT); // Special case where we only have globstars - if (segments.every(s => s === '**')) { + if (segments.every(s => s === GLOBSTAR)) { regEx = '.*'; } @@ -116,7 +123,7 @@ function parseRegExp(pattern: string): string { segments.forEach((segment, index) => { // Globstar is special - if (segment === '**') { + if (segment === GLOBSTAR) { // if we have more than one globstar after another, just ignore it if (!previousSegmentWasGlobStar) { @@ -207,7 +214,7 @@ function parseRegExp(pattern: string): string { } // Tail: Add the slash we had split on if there is more to come and the next one is not a globstar - if (index < segments.length - 1 && segments[index + 1] !== '**') { + if (index < segments.length - 1 && segments[index + 1] !== GLOBSTAR) { regEx += PATH_REGEX; } @@ -264,11 +271,39 @@ const NULL = function (): string { return null; }; -function parsePattern(pattern: string, options: IGlobOptions): ParsedStringPattern { - if (!pattern) { +function toAbsolutePattern(relativePattern: IRelativePattern | string): string { + + // Without a base URI, best we can do is add '**' to the pattern + if (typeof relativePattern === 'string') { + if (relativePattern.indexOf(GLOBSTAR) !== 0) { + relativePattern = GLOBSTAR + GLOB_SPLIT + strings.ltrim(relativePattern, GLOB_SPLIT); + } + + return relativePattern; + } + + // Guard against null/undefined + if (!relativePattern) { + return undefined; + } + + // With a base URI, we can append the path to the relative glob as prefix + return relativePattern.base + GLOB_SPLIT + strings.ltrim(relativePattern.pattern, GLOB_SPLIT); +} + +function parsePattern(arg1: string | IRelativePattern, options: IGlobOptions): ParsedStringPattern { + if (!arg1) { return NULL; } + // Handle IRelativePattern + let pattern: string; + if (typeof arg1 !== 'string') { + pattern = toAbsolutePattern(arg1.pattern); + } else { + pattern = arg1; + } + // Whitespace trimming pattern = pattern.trim(); @@ -276,7 +311,7 @@ function parsePattern(pattern: string, options: IGlobOptions): ParsedStringPatte const patternKey = `${pattern}_${!!options.trimForExclusions}`; let parsedPattern = CACHE.get(patternKey); if (parsedPattern) { - return parsedPattern; + return wrapRelativePattern(parsedPattern, pattern); } // Check for Trivias @@ -304,7 +339,21 @@ function parsePattern(pattern: string, options: IGlobOptions): ParsedStringPatte // Cache CACHE.set(patternKey, parsedPattern); - return parsedPattern; + return wrapRelativePattern(parsedPattern, pattern); +} + +function wrapRelativePattern(parsedPattern: ParsedStringPattern, arg2: string | IRelativePattern): ParsedStringPattern { + if (typeof arg2 === 'string') { + return parsedPattern; + } + + return function (path, basename) { + if (!paths.isEqualOrParent(path, arg2.base)) { + return null; + } + + return parsedPattern(path, basename); + }; } function trimForExclusions(pattern: string, options: IGlobOptions): string { @@ -395,9 +444,9 @@ function toRegExp(pattern: string): ParsedStringPattern { * - simple brace expansion ({js,ts} => js or ts) * - character ranges (using [...]) */ -export function match(pattern: string, path: string): boolean; +export function match(pattern: string | IRelativePattern, path: string): boolean; export function match(expression: IExpression, path: string, siblingsFn?: () => string[]): string /* the matching pattern */; -export function match(arg1: string | IExpression, path: string, siblingsFn?: () => string[]): any { +export function match(arg1: string | IExpression | IRelativePattern, path: string, siblingsFn?: () => string[]): any { if (!arg1 || !path) { return false; } @@ -413,16 +462,16 @@ export function match(arg1: string | IExpression, path: string, siblingsFn?: () * - simple brace expansion ({js,ts} => js or ts) * - character ranges (using [...]) */ -export function parse(pattern: string, options?: IGlobOptions): ParsedPattern; +export function parse(pattern: string | IRelativePattern, options?: IGlobOptions): ParsedPattern; export function parse(expression: IExpression, options?: IGlobOptions): ParsedExpression; -export function parse(arg1: string | IExpression, options: IGlobOptions = {}): any { +export function parse(arg1: string | IExpression | IRelativePattern, options: IGlobOptions = {}): any { if (!arg1) { return FALSE; } // Glob with String - if (typeof arg1 === 'string') { - const parsedPattern = parsePattern(arg1, options); + if (typeof arg1 === 'string' || (arg1 as IRelativePattern).base) { + const parsedPattern = parsePattern(arg1 as string | IRelativePattern, options); if (parsedPattern === NULL) { return FALSE; } diff --git a/src/vs/base/common/htmlContent.ts b/src/vs/base/common/htmlContent.ts index ab7ae6065f1..ce013b916cc 100644 --- a/src/vs/base/common/htmlContent.ts +++ b/src/vs/base/common/htmlContent.ts @@ -56,7 +56,7 @@ export function isEmptyMarkdownString(oneOrMany: IMarkdownString | IMarkdownStri export function isMarkdownString(thing: any): thing is IMarkdownString { if (thing instanceof MarkdownString) { return true; - } else if (typeof thing === 'object') { + } else if (thing && typeof thing === 'object') { return typeof (thing).value === 'string' && (typeof (thing).isTrusted === 'boolean' || (thing).isTrusted === void 0); } diff --git a/src/vs/base/common/labels.ts b/src/vs/base/common/labels.ts index 86b6fad0efa..443ca60c1f0 100644 --- a/src/vs/base/common/labels.ts +++ b/src/vs/base/common/labels.ts @@ -36,6 +36,9 @@ export function getPathLabel(resource: URI | string, rootProvider?: IWorkspaceFo if (typeof resource === 'string') { resource = URI.file(resource); } + if (resource.scheme !== 'file' && resource.scheme !== 'untitled') { + return resource.authority + resource.path; + } // return early if we can resolve a relative path label from the root const baseResource = rootProvider ? rootProvider.getWorkspaceFolder(resource) : null; diff --git a/src/vs/base/common/map.ts b/src/vs/base/common/map.ts index 003e893e7f8..24eb661cca8 100644 --- a/src/vs/base/common/map.ts +++ b/src/vs/base/common/map.ts @@ -227,22 +227,16 @@ class Node { readonly children = new Map>(); } -/** - * A trie map that allows for fast look up when keys are substrings - * to the actual search keys (dir/subdir-problem). - */ -export class TrieMap { +export class TrieMap { - static PathSplitter = (s: string) => s.split(/[\\/]/).filter(s => !!s); + private readonly _splitter: (key: K) => string[]; + private _root = new Node(); - private readonly _splitter: (s: string) => string[]; - private _root = new Node(); - - constructor(splitter: (s: string) => string[] = TrieMap.PathSplitter) { - this._splitter = s => splitter(s).filter(s => Boolean(s)); + constructor(splitter: (key: K) => string[]) { + this._splitter = key => splitter(key).filter(part => Boolean(part)); } - insert(path: string, element: E): void { + insert(path: K, element: V): void { const parts = this._splitter(path); let i = 0; @@ -258,9 +252,9 @@ export class TrieMap { } // create new nodes - let newNode: Node; + let newNode: Node; for (; i < parts.length; i++) { - newNode = new Node(); + newNode = new Node(); node.children.set(parts[i], newNode); node = newNode; } @@ -268,11 +262,11 @@ export class TrieMap { node.element = element; } - lookUp(path: string): E { + lookUp(path: K): V { const parts = this._splitter(path); let { children } = this._root; - let node: Node; + let node: Node; for (const part of parts) { node = children.get(part); if (!node) { @@ -284,10 +278,10 @@ export class TrieMap { return node.element; } - findSubstr(path: string): E { + findSubstr(path: K): V { const parts = this._splitter(path); - let lastNode: Node; + let lastNode: Node; let { children } = this._root; for (const part of parts) { const node = children.get(part); @@ -308,11 +302,11 @@ export class TrieMap { return undefined; } - findSuperstr(path: string): TrieMap { + findSuperstr(path: K): TrieMap { const parts = this._splitter(path); let { children } = this._root; - let node: Node; + let node: Node; for (const part of parts) { node = children.get(part); if (!node) { @@ -321,12 +315,26 @@ export class TrieMap { children = node.children; } - const result = new TrieMap(this._splitter); + const result = new TrieMap(this._splitter); result._root = node; return result; } } + +/** + * A trie map that allows for fast look up when keys are substrings + * to the actual search keys (dir/subdir-problem). + */ +export class StringTrieMap extends TrieMap { + + static PathSplitter = (s: string) => s.split(/[\\/]/).filter(s => !!s); + + constructor(splitter = StringTrieMap.PathSplitter) { + super(splitter); + } +} + export class ResourceMap { protected map: Map; diff --git a/src/vs/base/common/resources.ts b/src/vs/base/common/resources.ts new file mode 100644 index 00000000000..50ebdc2069f --- /dev/null +++ b/src/vs/base/common/resources.ts @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as paths from 'vs/base/common/paths'; +import uri from 'vs/base/common/uri'; + +export function basenameOrAuthority(resource: uri): string { + return paths.basename(resource.fsPath) || resource.authority; +} + +export function isEqualOrParent(first: uri, second: uri, ignoreCase?: boolean): boolean { + if (first.scheme === second.scheme && first.authority === second.authority) { + return paths.isEqualOrParent(first.fsPath, second.fsPath, ignoreCase); + } + + return false; +} + +export function dirname(resource: uri): uri { + return resource.with({ + path: paths.dirname(resource.path) + }); +} diff --git a/src/vs/base/common/uri.ts b/src/vs/base/common/uri.ts index 39ad9e80805..b5e8a739f4d 100644 --- a/src/vs/base/common/uri.ts +++ b/src/vs/base/common/uri.ts @@ -21,6 +21,42 @@ function encodeNoop(str: string): string { } +const _schemePattern = /^\w[\w\d+.-]*$/; +const _singleSlashStart = /^\//; +const _doubleSlashStart = /^\/\//; + +function _validateUri(ret: URI): void { + // scheme, https://tools.ietf.org/html/rfc3986#section-3.1 + // ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) + if (ret.scheme && !_schemePattern.test(ret.scheme)) { + throw new Error('[UriError]: Scheme contains illegal characters.'); + } + + // path, http://tools.ietf.org/html/rfc3986#section-3.3 + // If a URI contains an authority component, then the path component + // must either be empty or begin with a slash ("/") character. If a URI + // does not contain an authority component, then the path cannot begin + // with two slash characters ("//"). + if (ret.path) { + if (ret.authority) { + if (!_singleSlashStart.test(ret.path)) { + throw new Error('[UriError]: If a URI contains an authority component, then the path component must either be empty or begin with a slash ("/") character'); + } + } else { + if (_doubleSlashStart.test(ret.path)) { + throw new Error('[UriError]: If a URI does not contain an authority component, then the path cannot begin with two slash characters ("//")'); + } + } + } +} + +const _empty = ''; +const _slash = '/'; +const _regexp = /^(([^:/?#]+?):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/; +const _driveLetterPath = /^\/[a-zA-Z]:/; +const _upperCaseDrive = /^(\/)?([A-Z]:)/; +const _driveLetter = /^[a-zA-Z]:/; + /** * Uniform Resource Identifier (URI) http://tools.ietf.org/html/rfc3986. * This class is a simple parser which creates the basic component paths @@ -53,12 +89,6 @@ export default class URI { && typeof (thing).scheme === 'string'; } - private static _empty = ''; - private static _slash = '/'; - private static _regexp = /^(([^:/?#]+?):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/; - private static _driveLetterPath = /^\/[a-zA-Z]:/; - private static _upperCaseDrive = /^(\/)?([A-Z]:)/; - /** * scheme is the 'http' part of 'http://www.msft.com/some/path?query#fragment'. * The part before the first colon. @@ -86,21 +116,18 @@ export default class URI { */ readonly fragment: string; - private _formatted: string = null; - private _fsPath: string = null; - /** * @internal */ - private constructor(scheme: string, authority: string, path: string, query: string, fragment: string) { + protected constructor(scheme: string, authority: string, path: string, query: string, fragment: string) { - this.scheme = scheme || URI._empty; - this.authority = authority || URI._empty; - this.path = path || URI._empty; - this.query = query || URI._empty; - this.fragment = fragment || URI._empty; + this.scheme = scheme || _empty; + this.authority = authority || _empty; + this.path = path || _empty; + this.query = query || _empty; + this.fragment = fragment || _empty; - this._validate(this); + _validateUri(this); } // ---- filesystem path ----------------------- @@ -112,24 +139,7 @@ export default class URI { * invalid characters and semantics. Will *not* look at the scheme of this URI. */ get fsPath(): string { - if (!this._fsPath) { - let value: string; - if (this.authority && this.path && this.scheme === 'file') { - // unc path: file://shares/c$/far/boo - value = `//${this.authority}${this.path}`; - } else if (URI._driveLetterPath.test(this.path)) { - // windows drive letter: file:///c:/far/boo - value = this.path[1].toLowerCase() + this.path.substr(2); - } else { - // other path - value = this.path; - } - if (platform.isWindows) { - value = value.replace(/\//g, '\\'); - } - this._fsPath = value; - } - return this._fsPath; + throw new Error('not implemented'); } // ---- modify to new ------------------------- @@ -176,60 +186,66 @@ export default class URI { return this; } - return new URI(scheme, authority, path, query, fragment); + return new _URI(scheme, authority, path, query, fragment); } // ---- parse & validate ------------------------ public static parse(value: string): URI { - const match = URI._regexp.exec(value); + const match = _regexp.exec(value); if (!match) { - return new URI(URI._empty, URI._empty, URI._empty, URI._empty, URI._empty); + return new _URI(_empty, _empty, _empty, _empty, _empty); } - return new URI( - match[2] || URI._empty, - decodeURIComponent(match[4] || URI._empty), - decodeURIComponent(match[5] || URI._empty), - decodeURIComponent(match[7] || URI._empty), - decodeURIComponent(match[9] || URI._empty), + return new _URI( + match[2] || _empty, + decodeURIComponent(match[4] || _empty), + decodeURIComponent(match[5] || _empty), + decodeURIComponent(match[7] || _empty), + decodeURIComponent(match[9] || _empty), ); } public static file(path: string): URI { - let authority = URI._empty; + let authority = _empty; // normalize to fwd-slashes on windows, // on other systems bwd-slashes are valid // filename character, eg /f\oo/ba\r.txt if (platform.isWindows) { - path = path.replace(/\\/g, URI._slash); + path = path.replace(/\\/g, _slash); } // check for authority as used in UNC shares // or use the path as given - if (path[0] === URI._slash && path[0] === path[1]) { - let idx = path.indexOf(URI._slash, 2); + if (path[0] === _slash && path[1] === _slash) { + let idx = path.indexOf(_slash, 2); if (idx === -1) { authority = path.substring(2); - path = URI._empty; + path = _slash; } else { authority = path.substring(2, idx); - path = path.substring(idx); + path = path.substring(idx) || _slash; } } // Ensure that path starts with a slash // or that it is at least a slash - if (path[0] !== URI._slash) { - path = URI._slash + path; + if (_driveLetter.test(path)) { + path = _slash + path; + + } else if (path[0] !== _slash) { + // tricky -> makes invalid paths + // but otherwise we have to stop + // allowing relative paths... + path = _slash + path; } - return new URI('file', authority, path, URI._empty, URI._empty); + return new _URI('file', authority, path, _empty, _empty); } public static from(components: { scheme?: string; authority?: string; path?: string; query?: string; fragment?: string }): URI { - return new URI( + return new _URI( components.scheme, components.authority, components.path, @@ -238,50 +254,97 @@ export default class URI { ); } - private static _schemePattern = /^\w[\w\d+.-]*$/; - private static _singleSlashStart = /^\//; - private static _doubleSlashStart = /^\/\//; - - private _validate(ret: URI): void { - // scheme, https://tools.ietf.org/html/rfc3986#section-3.1 - // ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) - if (ret.scheme && !URI._schemePattern.test(ret.scheme)) { - throw new Error('[UriError]: Scheme contains illegal characters.'); - } - - // path, http://tools.ietf.org/html/rfc3986#section-3.3 - // If a URI contains an authority component, then the path component - // must either be empty or begin with a slash ("/") character. If a URI - // does not contain an authority component, then the path cannot begin - // with two slash characters ("//"). - if (ret.path) { - if (ret.authority) { - if (!URI._singleSlashStart.test(ret.path)) { - throw new Error('[UriError]: If a URI contains an authority component, then the path component must either be empty or begin with a slash ("/") character'); - } - } else { - if (URI._doubleSlashStart.test(ret.path)) { - throw new Error('[UriError]: If a URI does not contain an authority component, then the path cannot begin with two slash characters ("//")'); - } - } - } - } - // ---- printing/externalize --------------------------- /** * * @param skipEncoding Do not encode the result, default is `false` */ + public toString(skipEncoding: boolean = false): string { + throw new Error('not implemented'); + } + + public toJSON(): any { + const res = { + $mid: 1, + fsPath: this.fsPath, + external: this.toString(), + }; + + if (this.path) { + res.path = this.path; + } + + if (this.scheme) { + res.scheme = this.scheme; + } + + if (this.authority) { + res.authority = this.authority; + } + + if (this.query) { + res.query = this.query; + } + + if (this.fragment) { + res.fragment = this.fragment; + } + + return res; + } + + static revive(data: any): URI { + let result = new _URI( + (data).scheme, + (data).authority, + (data).path, + (data).query, + (data).fragment + ); + result._fsPath = (data).fsPath; + result._formatted = (data).external; + return result; + } +} + + +// tslint:disable-next-line:class-name +class _URI extends URI { + + _formatted: string = null; + _fsPath: string = null; + + get fsPath(): string { + if (!this._fsPath) { + let value: string; + if (this.authority && this.path && this.scheme === 'file') { + // unc path: file://shares/c$/far/boo + value = `//${this.authority}${this.path}`; + } else if (_driveLetterPath.test(this.path)) { + // windows drive letter: file:///c:/far/boo + value = this.path[1].toLowerCase() + this.path.substr(2); + } else { + // other path + value = this.path; + } + if (platform.isWindows) { + value = value.replace(/\//g, '\\'); + } + this._fsPath = value; + } + return this._fsPath; + } + public toString(skipEncoding: boolean = false): string { if (!skipEncoding) { if (!this._formatted) { - this._formatted = URI._asFormatted(this, false); + this._formatted = _URI._asFormatted(this, false); } return this._formatted; } else { // we don't cache that - return URI._asFormatted(this, true); + return _URI._asFormatted(this, true); } } @@ -323,7 +386,7 @@ export default class URI { } if (path) { // lower-case windows drive letters in /C:/fff or C:/fff - const m = URI._upperCaseDrive.exec(path); + const m = _upperCaseDrive.exec(path); if (m) { if (m[1]) { path = '/' + m[2].toLowerCase() + path.substr(3); // "/c:".length === 3 @@ -338,12 +401,12 @@ export default class URI { // cannot be parsed back again let lastIdx = 0; while (true) { - let idx = path.indexOf(URI._slash, lastIdx); + let idx = path.indexOf(_slash, lastIdx); if (idx === -1) { parts.push(encoder(path.substring(lastIdx))); break; } - parts.push(encoder(path.substring(lastIdx, idx)), URI._slash); + parts.push(encoder(path.substring(lastIdx, idx)), _slash); lastIdx = idx + 1; }; } @@ -354,50 +417,7 @@ export default class URI { parts.push('#', encoder(fragment)); } - return parts.join(URI._empty); - } - - public toJSON(): any { - const res = { - fsPath: this.fsPath, - external: this.toString(), - $mid: 1 - }; - - if (this.path) { - res.path = this.path; - } - - if (this.scheme) { - res.scheme = this.scheme; - } - - if (this.authority) { - res.authority = this.authority; - } - - if (this.query) { - res.query = this.query; - } - - if (this.fragment) { - res.fragment = this.fragment; - } - - return res; - } - - static revive(data: any): URI { - let result = new URI( - (data).scheme, - (data).authority, - (data).path, - (data).query, - (data).fragment - ); - result._fsPath = (data).fsPath; - result._formatted = (data).external; - return result; + return parts.join(_empty); } } diff --git a/src/vs/base/node/console.ts b/src/vs/base/node/console.ts new file mode 100644 index 00000000000..fc3cf3baeab --- /dev/null +++ b/src/vs/base/node/console.ts @@ -0,0 +1,125 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import URI from 'vs/base/common/uri'; + +export interface IRemoteConsoleLog { + type: string; + severity: string; + arguments: string; +} + +interface IStackArgument { + __$stack: string; +} + +export interface IStackFrame { + uri: URI; + line: number; + column: number; +} + +export function isRemoteConsoleLog(obj: any): obj is IRemoteConsoleLog { + const entry = obj as IRemoteConsoleLog; + + return entry && typeof entry.type === 'string' && typeof entry.severity === 'string'; +} + +export function parse(entry: IRemoteConsoleLog): { args: any[], stack?: string } { + const args: any[] = []; + let stack: string; + + // Parse Entry + try { + const parsedArguments: any[] = JSON.parse(entry.arguments); + + // Check for special stack entry as last entry + const stackArgument = parsedArguments[parsedArguments.length - 1] as IStackArgument; + if (stackArgument && stackArgument.__$stack) { + parsedArguments.pop(); // stack is handled specially + stack = stackArgument.__$stack; + } + + args.push(...parsedArguments); + } catch (error) { + args.push('Unable to log remote console arguments', entry.arguments); + } + + return { args, stack }; +} + +export function getFirstFrame(entry: IRemoteConsoleLog): IStackFrame; +export function getFirstFrame(stack: string): IStackFrame; +export function getFirstFrame(arg0: IRemoteConsoleLog | string): IStackFrame { + if (typeof arg0 !== 'string') { + return getFirstFrame(parse(arg0).stack); + } + + // Parse a source information out of the stack if we have one. Format: + // at vscode.commands.registerCommand (/Users/someone/Desktop/test-ts/out/src/extension.js:18:17) + const stack = arg0; + if (stack) { + const matches = /.+\((.+):(\d+):(\d+)\)/.exec(stack); + if (matches.length === 4) { + return { + uri: URI.file(matches[1]), + line: Number(matches[2]), + column: Number(matches[3]) + } as IStackFrame; + } + } + + return void 0; +} + +export function log(entry: IRemoteConsoleLog, label: string): void { + const { args, stack } = parse(entry); + + // Determine suffix based on severity of log entry if we have a stack + let suffixColor = 'blue'; + let suffix = ''; + if (stack) { + switch (entry.severity) { + case 'warn': + suffixColor = 'goldenrod'; + suffix = ' WARNING:'; + break; + case 'error': + suffixColor = 'darkred'; + suffix = ' ERROR:'; + break; + } + } + + let consoleArgs = []; + + // First arg is a string + if (typeof args[0] === 'string') { + consoleArgs = [`%c[${label}]%c${suffix} %c${args[0]}`, color('blue'), color(suffixColor), color('black'), ...args.slice(1)]; + } + + // First arg is something else, just apply all + else { + consoleArgs = [`%c[${label}]%c${suffix}`, color('blue'), color(suffixColor), ...args]; + } + + // Stack: use console group + if (stack) { + console.groupCollapsed.apply(console, consoleArgs); + console.log(stack); + console.groupEnd(); + } + + // No stack: just log message + else { + console[entry.severity].apply(console, consoleArgs); + } +} + +function color(color: string): string { + return `color: ${color}; font-weight: normal;`; +} \ No newline at end of file diff --git a/src/vs/base/node/id.ts b/src/vs/base/node/id.ts index f8b65bfcbc9..a9ebc7c086f 100644 --- a/src/vs/base/node/id.ts +++ b/src/vs/base/node/id.ts @@ -9,7 +9,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import * as errors from 'vs/base/common/errors'; import * as uuid from 'vs/base/common/uuid'; import { networkInterfaces } from 'os'; -import { TrieMap } from 'vs/base/common/map'; +import { StringTrieMap } from 'vs/base/common/map'; // http://www.techrepublic.com/blog/data-center/mac-address-scorecard-for-common-virtual-machine-platforms/ // VMware ESX 3, Server, Workstation, Player 00-50-56, 00-0C-29, 00-05-69 @@ -23,12 +23,12 @@ import { TrieMap } from 'vs/base/common/map'; // Sun xVM VirtualBox 08-00-27 export const virtualMachineHint: { value(): number } = new class { - private _virtualMachineOUIs: TrieMap; + private _virtualMachineOUIs: StringTrieMap; private _value: number; private _isVirtualMachineMacAdress(mac: string): boolean { if (!this._virtualMachineOUIs) { - this._virtualMachineOUIs = new TrieMap(s => s.split(/[-:]/)); + this._virtualMachineOUIs = new StringTrieMap(s => s.split(/[-:]/)); // this._virtualMachineOUIs.insert('00-00-00', true); this._virtualMachineOUIs.insert('00-50-56', true); this._virtualMachineOUIs.insert('00-0C-29', true); diff --git a/src/vs/base/node/mime.ts b/src/vs/base/node/mime.ts index ee5ce368a7a..1e71046cce6 100644 --- a/src/vs/base/node/mime.ts +++ b/src/vs/base/node/mime.ts @@ -56,7 +56,7 @@ const ZERO_BYTE_DETECTION_BUFFER_MAX_LEN = 512; // number of bytes to look at to const NO_GUESS_BUFFER_MAX_LEN = 512; // when not auto guessing the encoding, small number of bytes are enough const AUTO_GUESS_BUFFER_MAX_LEN = 512 * 8; // with auto guessing we want a lot more content to be read for guessing -function maxBufferLen(arg1?: DetectMimesOption | boolean): number { +export function maxBufferLen(arg1?: DetectMimesOption | boolean): number { let autoGuessEncoding: boolean; if (typeof arg1 === 'boolean') { autoGuessEncoding = arg1; diff --git a/src/vs/base/node/terminateProcess.sh b/src/vs/base/node/terminateProcess.sh index acdcbf8ed42..dceeae9745f 100755 --- a/src/vs/base/node/terminateProcess.sh +++ b/src/vs/base/node/terminateProcess.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash terminateTree() { for cpid in $(/usr/bin/pgrep -P $1); do diff --git a/src/vs/base/parts/ipc/node/ipc.cp.ts b/src/vs/base/parts/ipc/node/ipc.cp.ts index 45d401724c4..cb73fe6f937 100644 --- a/src/vs/base/parts/ipc/node/ipc.cp.ts +++ b/src/vs/base/parts/ipc/node/ipc.cp.ts @@ -12,6 +12,7 @@ import { Emitter } from 'vs/base/common/event'; import { fromEventEmitter } from 'vs/base/node/event'; import { createQueuedSender } from 'vs/base/node/processes'; import { ChannelServer as IPCServer, ChannelClient as IPCClient, IChannelClient, IChannel } from 'vs/base/parts/ipc/common/ipc'; +import { isRemoteConsoleLog, log } from 'vs/base/node/console'; export class Server extends IPCServer { constructor() { @@ -151,24 +152,15 @@ export class Client implements IChannelClient, IDisposable { const onRawMessage = fromEventEmitter(this.child, 'message', msg => msg); onRawMessage(msg => { - // Handle console logs specially - if (msg && msg.type === '__$console') { - let args = ['%c[IPC Library: ' + this.options.serverName + ']', 'color: darkgreen']; - try { - const parsed = JSON.parse(msg.arguments); - args = args.concat(Object.getOwnPropertyNames(parsed).map(o => parsed[o])); - } catch (error) { - args.push(msg.arguments); - } - console[msg.severity].apply(console, args); + // Handle remote console logs specially + if (isRemoteConsoleLog(msg)) { + log(msg, `IPC Library: ${this.options.serverName}`); return null; } // Anything else goes to the outside - else { - onMessageEmitter.fire(msg); - } + onMessageEmitter.fire(msg); }); const sender = this.options.useQueue ? createQueuedSender(this.child) : this.child; diff --git a/src/vs/base/test/common/map.test.ts b/src/vs/base/test/common/map.test.ts index b26a2079943..aefb143c080 100644 --- a/src/vs/base/test/common/map.test.ts +++ b/src/vs/base/test/common/map.test.ts @@ -6,7 +6,7 @@ 'use strict'; -import { BoundedMap, TrieMap, ResourceMap } from 'vs/base/common/map'; +import { BoundedMap, StringTrieMap, ResourceMap } from 'vs/base/common/map'; import * as assert from 'assert'; import URI from 'vs/base/common/uri'; @@ -314,7 +314,7 @@ suite('Map', () => { test('TrieMap - basics', function () { - const map = new TrieMap(); + const map = new StringTrieMap(); map.insert('/user/foo/bar', 1); map.insert('/user/foo', 2); @@ -332,7 +332,7 @@ suite('Map', () => { test('TrieMap - lookup', function () { - const map = new TrieMap(); + const map = new StringTrieMap(); map.insert('/user/foo/bar', 1); map.insert('/user/foo', 2); map.insert('/user/foo/flip/flop', 3); @@ -346,7 +346,7 @@ suite('Map', () => { test('TrieMap - superstr', function () { - const map = new TrieMap(); + const map = new StringTrieMap(); map.insert('/user/foo/bar', 1); map.insert('/user/foo', 2); map.insert('/user/foo/flip/flop', 3); diff --git a/src/vs/base/test/node/glob.test.ts b/src/vs/base/test/node/glob.test.ts index 1e3183171bd..1c87c114c4a 100644 --- a/src/vs/base/test/node/glob.test.ts +++ b/src/vs/base/test/node/glob.test.ts @@ -884,4 +884,35 @@ suite('Glob', () => { // Later expressions take precedence assert.deepEqual(glob.mergeExpressions({ 'a': true, 'b': false, 'c': true }, { 'a': false, 'b': true }), { 'a': false, 'b': true, 'c': true }); }); + + test('relative pattern', function () { + let p: glob.IRelativePattern = { base: '/DNXConsoleApp', pattern: '**/*.cs' }; + + assert(glob.match(p, '/DNXConsoleApp/Program.cs')); + assert(glob.match(p, '/DNXConsoleApp/foo/Program.cs')); + assert(!glob.match(p, '/DNXConsoleApp/foo/Program.ts')); + assert(!glob.match(p, '/other/DNXConsoleApp/foo/Program.ts')); + + p = { base: 'C:\\DNXConsoleApp', pattern: '**/*.cs' }; + assert(glob.match(p, 'C:\\DNXConsoleApp\\Program.cs')); + assert(glob.match(p, 'C:\\DNXConsoleApp\\foo\\Program.cs')); + assert(!glob.match(p, 'C:\\DNXConsoleApp\\foo\\Program.ts')); + assert(!glob.match(p, 'C:\\other\\DNXConsoleApp\\foo\\Program.ts')); + + assert(glob.match(p, 'C:/DNXConsoleApp/Program.cs')); + assert(glob.match(p, 'C:/DNXConsoleApp/foo/Program.cs')); + assert(!glob.match(p, 'C:/DNXConsoleApp/foo/Program.ts')); + assert(!glob.match(p, 'C:/other/DNXConsoleApp/foo/Program.ts')); + + p = { base: 'C:/DNXConsoleApp', pattern: '**/*.cs' }; + assert(glob.match(p, 'C:\\DNXConsoleApp\\Program.cs')); + assert(glob.match(p, 'C:\\DNXConsoleApp\\foo\\Program.cs')); + assert(!glob.match(p, 'C:\\DNXConsoleApp\\foo\\Program.ts')); + assert(!glob.match(p, 'C:\\other\\DNXConsoleApp\\foo\\Program.ts')); + + assert(glob.match(p, 'C:/DNXConsoleApp/Program.cs')); + assert(glob.match(p, 'C:/DNXConsoleApp/foo/Program.cs')); + assert(!glob.match(p, 'C:/DNXConsoleApp/foo/Program.ts')); + assert(!glob.match(p, 'C:/other/DNXConsoleApp/foo/Program.ts')); + }); }); \ No newline at end of file diff --git a/src/vs/code/electron-browser/contrib/contributions.ts b/src/vs/code/electron-browser/contrib/contributions.ts new file mode 100644 index 00000000000..acb31a18450 --- /dev/null +++ b/src/vs/code/electron-browser/contrib/contributions.ts @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + + +import { NodeCachedDataCleaner } from 'vs/code/electron-browser/contrib/nodeCachedDataCleaner'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; + +export function createSharedProcessContributions(service: IInstantiationService): void { + service.createInstance(NodeCachedDataCleaner); +} diff --git a/src/vs/code/electron-browser/contrib/nodeCachedDataCleaner.ts b/src/vs/code/electron-browser/contrib/nodeCachedDataCleaner.ts new file mode 100644 index 00000000000..d4d74b8971c --- /dev/null +++ b/src/vs/code/electron-browser/contrib/nodeCachedDataCleaner.ts @@ -0,0 +1,89 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { join, basename, dirname } from 'path'; +import { readdir, rimraf, stat } from 'vs/base/node/pfs'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import product from 'vs/platform/node/product'; + +declare type OnNodeCachedDataArgs = [{ errorCode: string, path: string, detail?: string }, { path: string, length: number }]; +declare const MonacoEnvironment: { onNodeCachedData: OnNodeCachedDataArgs[] }; + +export class NodeCachedDataCleaner { + + private static _DataMaxAge = product.nameLong.indexOf('Insiders') >= 0 + ? 1000 * 60 * 60 * 24 * 7 // roughly 1 week + : 1000 * 60 * 60 * 24 * 30 * 3; // roughly 3 months + + private _disposables: IDisposable[] = []; + + constructor( + @IEnvironmentService private readonly _environmentService: IEnvironmentService + ) { + this._manageCachedDataSoon(); + } + + dispose(): void { + this._disposables = dispose(this._disposables); + } + + private _manageCachedDataSoon(): void { + // Cached data is stored as user data and we run a cleanup task everytime + // the editor starts. The strategy is to delete all files that are older than + // 3 months (1 week respectively) + if (!this._environmentService.nodeCachedDataDir) { + return; + } + + // The folder which contains folders of cached data. Each of these folder is per + // version + const nodeCachedDataRootDir = dirname(this._environmentService.nodeCachedDataDir); + const nodeCachedDataCurrent = basename(this._environmentService.nodeCachedDataDir); + + let handle = setTimeout(() => { + handle = undefined; + + readdir(nodeCachedDataRootDir).then(entries => { + + const now = Date.now(); + const deletes: TPromise[] = []; + + entries.forEach(entry => { + // name check + // * name is a git commit id (40 hex characters) + // * not the current cached data folder + if (entry.match(/^[a-f0-9]{40}$/i) && entry !== nodeCachedDataCurrent) { + + const path = join(nodeCachedDataRootDir, entry); + deletes.push(stat(path).then(stats => { + // stat check + // * only directories + // * only when old enough + if (stats.isDirectory()) { + const diff = now - stats.mtime.getTime(); + if (diff > NodeCachedDataCleaner._DataMaxAge) { + return rimraf(path); + } + } + return undefined; + })); + } + }); + + return TPromise.join(deletes); + + }).done(undefined, onUnexpectedError); + + }, 30 * 1000); + + this._disposables.push({ + dispose() { clearTimeout(handle); } + }); + } +} diff --git a/src/vs/code/electron-browser/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcessMain.ts index ef7a31319cf..f83a1b2669e 100644 --- a/src/vs/code/electron-browser/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcessMain.ts @@ -35,6 +35,7 @@ import { WindowsChannelClient } from 'vs/platform/windows/common/windowsIpc'; import { ipcRenderer } from 'electron'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { StorageService, inMemoryLocalStorageInstance } from 'vs/platform/storage/common/storageService'; +import { createSharedProcessContributions } from 'vs/code/electron-browser/contrib/contributions'; interface ISharedProcessInitData { sharedIPCHandle: string; @@ -98,7 +99,7 @@ function main(server: Server, initData: ISharedProcessInitData): void { server.registerChannel('telemetryAppender', new TelemetryAppenderChannel(appender)); const services = new ServiceCollection(); - const { appRoot, extensionsPath, extensionDevelopmentPath, isBuilt, extensionTestsPath } = accessor.get(IEnvironmentService); + const { appRoot, extensionsPath, extensionDevelopmentPath, isBuilt, extensionTestsPath, installSource } = accessor.get(IEnvironmentService); if (isBuilt && !extensionDevelopmentPath && product.enableTelemetry) { const disableStorage = !!extensionTestsPath; // never keep any state when running extension tests! @@ -107,7 +108,7 @@ function main(server: Server, initData: ISharedProcessInitData): void { const config: ITelemetryServiceConfig = { appender, - commonProperties: resolveCommonProperties(product.commit, pkg.version) + commonProperties: resolveCommonProperties(product.commit, pkg.version, installSource) .then(result => Object.defineProperty(result, 'common.machineId', { get: () => storageService.get(machineIdStorageKey), enumerable: true @@ -132,6 +133,8 @@ function main(server: Server, initData: ISharedProcessInitData): void { // clean up deprecated extensions (extensionManagementService as ExtensionManagementService).removeDeprecatedExtensions(); + + createSharedProcessContributions(instantiationService2); }); }); } diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 51b6db552f4..df39afe3f61 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -228,9 +228,9 @@ export class CodeApplication { }); // Keyboard layout changes - KeyboardLayoutMonitor.INSTANCE.onDidChangeKeyboardLayout(isISOKeyboard => { + KeyboardLayoutMonitor.INSTANCE.onDidChangeKeyboardLayout(() => { if (this.windowsMainService) { - this.windowsMainService.sendToAll('vscode:keyboardLayoutChanged', isISOKeyboard); + this.windowsMainService.sendToAll('vscode:keyboardLayoutChanged', false); } }); } @@ -294,7 +294,7 @@ export class CodeApplication { if (this.environmentService.isBuilt && !this.environmentService.isExtensionDevelopment && !!product.enableTelemetry) { const channel = getDelayedChannel(this.sharedProcessClient.then(c => c.getChannel('telemetryAppender'))); const appender = new TelemetryAppenderClient(channel); - const commonProperties = resolveCommonProperties(product.commit, pkg.version) + const commonProperties = resolveCommonProperties(product.commit, pkg.version, this.environmentService.installSource) .then(result => Object.defineProperty(result, 'common.machineId', { get: () => this.storageService.getItem(machineIdStorageKey), enumerable: true diff --git a/src/vs/code/electron-main/keyboard.ts b/src/vs/code/electron-main/keyboard.ts index 3b19f2fea15..d7d10e70d7b 100644 --- a/src/vs/code/electron-main/keyboard.ts +++ b/src/vs/code/electron-main/keyboard.ts @@ -7,7 +7,6 @@ import * as nativeKeymap from 'native-keymap'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { isMacintosh } from 'vs/base/common/platform'; import { IStorageService } from 'vs/platform/storage/node/storage'; import Event, { Emitter, once } from 'vs/base/common/event'; import { ConfigWatcher } from 'vs/base/node/config'; @@ -21,59 +20,24 @@ export class KeyboardLayoutMonitor { public static readonly INSTANCE = new KeyboardLayoutMonitor(); - private _emitter: Emitter; + private _emitter: Emitter; private _registered: boolean; - private _isISOKeyboard: boolean; private constructor() { - this._emitter = new Emitter(); + this._emitter = new Emitter(); this._registered = false; - this._isISOKeyboard = this._readIsISOKeyboard(); } - public onDidChangeKeyboardLayout(callback: (isISOKeyboard: boolean) => void): IDisposable { + public onDidChangeKeyboardLayout(callback: () => void): IDisposable { if (!this._registered) { this._registered = true; nativeKeymap.onDidChangeKeyboardLayout(() => { - this._emitter.fire(this._isISOKeyboard); + this._emitter.fire(); }); - - if (isMacintosh) { - // See https://github.com/Microsoft/vscode/issues/24153 - // On OSX, on ISO keyboards, Chromium swaps the scan codes - // of IntlBackslash and Backquote. - // - // The C++ methods can give the current keyboard type (ISO or not) - // only after a NSEvent was handled. - // - // We therefore poll. - setInterval(() => { - let newValue = this._readIsISOKeyboard(); - if (this._isISOKeyboard === newValue) { - // no change - return; - } - - this._isISOKeyboard = newValue; - this._emitter.fire(this._isISOKeyboard); - - }, 3000); - } } return this._emitter.event(callback); } - - private _readIsISOKeyboard(): boolean { - // if (isMacintosh) { - // return nativeKeymap.isISOKeyboard(); - // } - return false; - } - - public isISOKeyboard(): boolean { - return this._isISOKeyboard; - } } export interface IKeybinding { diff --git a/src/vs/code/electron-main/menus.ts b/src/vs/code/electron-main/menus.ts index 93a8260dcb5..b796c7e82cd 100644 --- a/src/vs/code/electron-main/menus.ts +++ b/src/vs/code/electron-main/menus.ts @@ -10,7 +10,7 @@ import { isMacintosh, isLinux, isWindows, language } from 'vs/base/common/platfo import * as arrays from 'vs/base/common/arrays'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ipcMain as ipc, app, shell, dialog, Menu, MenuItem, BrowserWindow } from 'electron'; -import { OpenContext } from 'vs/platform/windows/common/windows'; +import { OpenContext, IRunActionInWindowRequest } from 'vs/platform/windows/common/windows'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IFilesConfiguration, AutoSaveConfiguration } from 'vs/platform/files/common/files'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -80,7 +80,6 @@ export class CodeMenu { private closeFolder: Electron.MenuItem; private closeWorkspace: Electron.MenuItem; - private saveWorkspaceAs: Electron.MenuItem; private nativeTabMenuItems: Electron.MenuItem[]; @@ -255,7 +254,6 @@ export class CodeMenu { this.closeWorkspace.visible = isInWorkspaceContext; this.closeFolder.visible = !isInWorkspaceContext; this.closeFolder.enabled = isInFolderContext; - this.saveWorkspaceAs.enabled = isInFolderContext || isInWorkspaceContext; } private install(): void { @@ -407,7 +405,7 @@ export class CodeMenu { const isMultiRootEnabled = (product.quality !== 'stable'); // TODO@Ben multi root - this.saveWorkspaceAs = this.createMenuItem(nls.localize({ key: 'miSaveWorkspaceAs', comment: ['&& denotes a mnemonic'] }, "&&Save Workspace As..."), 'workbench.action.saveWorkspaceAs'); + const saveWorkspaceAs = this.createMenuItem(nls.localize({ key: 'miSaveWorkspaceAs', comment: ['&& denotes a mnemonic'] }, "&&Save Workspace As..."), 'workbench.action.saveWorkspaceAs'); const addFolder = this.createMenuItem(nls.localize({ key: 'miAddFolderToWorkspace', comment: ['&& denotes a mnemonic'] }, "&&Add Folder to Workspace..."), 'workbench.action.addRootFolder'); const saveFile = this.createMenuItem(nls.localize({ key: 'miSave', comment: ['&& denotes a mnemonic'] }, "&&Save"), 'workbench.action.files.save'); @@ -443,7 +441,7 @@ export class CodeMenu { openRecent, isMultiRootEnabled ? __separator__() : null, isMultiRootEnabled ? addFolder : null, - isMultiRootEnabled ? this.saveWorkspaceAs : null, + isMultiRootEnabled ? saveWorkspaceAs : null, __separator__(), saveFile, saveFileAs, @@ -965,14 +963,14 @@ export class CodeMenu { const keyboardShortcutsUrl = isLinux ? product.keyboardShortcutsUrlLinux : isMacintosh ? product.keyboardShortcutsUrlMac : product.keyboardShortcutsUrlWin; arrays.coalesce([ - new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miWelcome', comment: ['&& denotes a mnemonic'] }, "&&Welcome")), click: () => this.windowsService.sendToFocused('vscode:runAction', 'workbench.action.showWelcomePage'), enabled: (this.windowsService.getWindowCount() > 0) }), - new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miInteractivePlayground', comment: ['&& denotes a mnemonic'] }, "&&Interactive Playground")), click: () => this.windowsService.sendToFocused('vscode:runAction', 'workbench.action.showInteractivePlayground'), enabled: (this.windowsService.getWindowCount() > 0) }), - product.documentationUrl ? new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miDocumentation', comment: ['&& denotes a mnemonic'] }, "&&Documentation")), click: () => this.windowsService.sendToFocused('vscode:runAction', 'workbench.action.openDocumentationUrl'), enabled: (this.windowsService.getWindowCount() > 0) }) : null, - product.releaseNotesUrl ? new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miReleaseNotes', comment: ['&& denotes a mnemonic'] }, "&&Release Notes")), click: () => this.windowsService.sendToFocused('vscode:runAction', 'update.showCurrentReleaseNotes'), enabled: (this.windowsService.getWindowCount() > 0) }) : null, + new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miWelcome', comment: ['&& denotes a mnemonic'] }, "&&Welcome")), click: () => this.runActionInRenderer('workbench.action.showWelcomePage'), enabled: (this.windowsService.getWindowCount() > 0) }), + new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miInteractivePlayground', comment: ['&& denotes a mnemonic'] }, "&&Interactive Playground")), click: () => this.runActionInRenderer('workbench.action.showInteractivePlayground'), enabled: (this.windowsService.getWindowCount() > 0) }), + product.documentationUrl ? new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miDocumentation', comment: ['&& denotes a mnemonic'] }, "&&Documentation")), click: () => this.runActionInRenderer('workbench.action.openDocumentationUrl'), enabled: (this.windowsService.getWindowCount() > 0) }) : null, + product.releaseNotesUrl ? new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miReleaseNotes', comment: ['&& denotes a mnemonic'] }, "&&Release Notes")), click: () => this.runActionInRenderer('update.showCurrentReleaseNotes'), enabled: (this.windowsService.getWindowCount() > 0) }) : null, __separator__(), - keyboardShortcutsUrl ? new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miKeyboardShortcuts', comment: ['&& denotes a mnemonic'] }, "&&Keyboard Shortcuts Reference")), click: () => this.windowsService.sendToFocused('vscode:runAction', 'workbench.action.keybindingsReference'), enabled: (this.windowsService.getWindowCount() > 0) }) : null, - product.introductoryVideosUrl ? new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miIntroductoryVideos', comment: ['&& denotes a mnemonic'] }, "Introductory &&Videos")), click: () => this.windowsService.sendToFocused('vscode:runAction', 'workbench.action.openIntroductoryVideosUrl'), enabled: (this.windowsService.getWindowCount() > 0) }) : null, - product.tipsAndTricksUrl ? new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miTipsAndTricks', comment: ['&& denotes a mnemonic'] }, "&&Tips and Tricks")), click: () => this.windowsService.sendToFocused('vscode:runAction', 'workbench.action.openTipsAndTricksUrl'), enabled: (this.windowsService.getWindowCount() > 0) }) : null, + keyboardShortcutsUrl ? new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miKeyboardShortcuts', comment: ['&& denotes a mnemonic'] }, "&&Keyboard Shortcuts Reference")), click: () => this.runActionInRenderer('workbench.action.keybindingsReference'), enabled: (this.windowsService.getWindowCount() > 0) }) : null, + product.introductoryVideosUrl ? new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miIntroductoryVideos', comment: ['&& denotes a mnemonic'] }, "Introductory &&Videos")), click: () => this.runActionInRenderer('workbench.action.openIntroductoryVideosUrl'), enabled: (this.windowsService.getWindowCount() > 0) }) : null, + product.tipsAndTricksUrl ? new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miTipsAndTricks', comment: ['&& denotes a mnemonic'] }, "&&Tips and Tricks")), click: () => this.runActionInRenderer('workbench.action.openTipsAndTricksUrl'), enabled: (this.windowsService.getWindowCount() > 0) }) : null, (product.introductoryVideosUrl || keyboardShortcutsUrl) ? __separator__() : null, product.twitterUrl ? new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miTwitter', comment: ['&& denotes a mnemonic'] }, "&&Join us on Twitter")), click: () => this.openUrl(product.twitterUrl, 'openTwitterUrl') }) : null, product.requestFeatureUrl ? new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miUserVoice', comment: ['&& denotes a mnemonic'] }, "&&Search Feature Requests")), click: () => this.openUrl(product.requestFeatureUrl, 'openUserVoiceUrl') }) : null, @@ -1023,8 +1021,8 @@ export class CodeMenu { const terminateTask = this.createMenuItem(nls.localize({ key: 'miTerminateTask', comment: ['&& denotes a mnemonic'] }, "&&Terminate Task..."), 'workbench.action.tasks.terminate'); // const testTask = this.createMenuItem(nls.localize({ key: 'miTestTask', comment: ['&& denotes a mnemonic'] }, "Run Test T&&ask..."), 'workbench.action.tasks.test'); // const showTaskLog = this.createMenuItem(nls.localize({ key: 'miShowTaskLog', comment: ['&& denotes a mnemonic'] }, "&&Show Task Log"), 'workbench.action.tasks.showLog'); - const configureTask = this.createMenuItem(nls.localize({ key: 'miConfigureTask', comment: ['&& denotes a mnemonic'] }, "&&Configure Tasks"), 'workbench.action.tasks.configureTaskRunner'); - const configureBuildTask = this.createMenuItem(nls.localize({ key: 'miConfigureBuildTask', comment: ['&& denotes a mnemonic'] }, "Configure De&&fault Build Task"), 'workbench.action.tasks.configureDefaultBuildTask'); + const configureTask = this.createMenuItem(nls.localize({ key: 'miConfigureTask', comment: ['&& denotes a mnemonic'] }, "&&Configure Tasks..."), 'workbench.action.tasks.configureTaskRunner'); + const configureBuildTask = this.createMenuItem(nls.localize({ key: 'miConfigureBuildTask', comment: ['&& denotes a mnemonic'] }, "Configure De&&fault Build Task..."), 'workbench.action.tasks.configureDefaultBuildTask'); // const configureTestTask = this.createMenuItem(nls.localize({ key: 'miConfigureTestTask', comment: ['&& denotes a mnemonic'] }, "Configure Defau&< Test Task"), 'workbench.action.tasks.configureDefaultTestTask'); [ @@ -1113,7 +1111,7 @@ export class CodeMenu { commandId = this.isOptionClick(event) ? arg2[1] : arg2[0]; // support alternative action if we got multiple action Ids and the option key was pressed while invoking } - this.windowsService.sendToFocused('vscode:runAction', commandId); + this.runActionInRenderer(commandId); }; const enabled = typeof arg3 === 'boolean' ? arg3 : this.windowsService.getWindowCount() > 0; const checked = typeof arg4 === 'boolean' ? arg4 : false; @@ -1155,11 +1153,15 @@ export class CodeMenu { } // Finally execute command in Window - this.windowsService.sendToFocused('vscode:runAction', commandId); + this.runActionInRenderer(commandId); } })); } + private runActionInRenderer(id: string): void { + this.windowsService.sendToFocused('vscode:runAction', { id, from: 'menu' } as IRunActionInWindowRequest); + } + private withKeybinding(commandId: string, options: Electron.MenuItemConstructorOptions): Electron.MenuItemConstructorOptions { const binding = this.keybindingsResolver.getKeybinding(commandId); diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index 84ea2f76aeb..3bd87230457 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -19,9 +19,8 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { parseArgs } from 'vs/platform/environment/node/argv'; import product from 'vs/platform/node/product'; import pkg from 'vs/platform/node/package'; -import { IWindowSettings, MenuBarVisibility, IWindowConfiguration, ReadyState } from 'vs/platform/windows/common/windows'; +import { IWindowSettings, MenuBarVisibility, IWindowConfiguration, ReadyState, IRunActionInWindowRequest } from 'vs/platform/windows/common/windows'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { KeyboardLayoutMonitor } from 'vs/code/electron-main/keyboard'; import { isLinux, isMacintosh, isWindows } from 'vs/base/common/platform'; import { ICodeWindow } from 'vs/platform/windows/electron-main/windows'; import { IWorkspaceIdentifier, IWorkspacesMainService } from 'vs/platform/workspaces/common/workspaces'; @@ -66,6 +65,10 @@ interface IWorkbenchEditorConfiguration { }; } +interface ITouchBarSegment extends Electron.SegmentedControlSegment { + id: string; +} + export class CodeWindow implements ICodeWindow { public static themeStorageKey = 'theme'; @@ -94,6 +97,8 @@ export class CodeWindow implements ICodeWindow { private currentConfig: IWindowConfiguration; private pendingLoadConfig: IWindowConfiguration; + private touchBarGroups: Electron.TouchBarSegmentedControl[]; + constructor( config: IWindowCreationOptions, @ILogService private logService: ILogService, @@ -103,6 +108,7 @@ export class CodeWindow implements ICodeWindow { @IWorkspacesMainService private workspaceService: IWorkspacesMainService, @IBackupMainService private backupService: IBackupMainService ) { + this.touchBarGroups = []; this._lastFocusTime = -1; this._readyState = ReadyState.NONE; this.whenReadyCallbacks = []; @@ -114,6 +120,9 @@ export class CodeWindow implements ICodeWindow { // respect configured menu bar visibility this.onConfigurationUpdated(); + // macOS: touch bar support + this.createTouchBar(); + // Eventing this.registerListeners(); } @@ -440,9 +449,9 @@ export class CodeWindow implements ICodeWindow { } if (cmd === back) { - this.send('vscode:runAction', acrossEditors ? 'workbench.action.openPreviousRecentlyUsedEditor' : 'workbench.action.navigateBack'); + this.send('vscode:runAction', { id: acrossEditors ? 'workbench.action.openPreviousRecentlyUsedEditor' : 'workbench.action.navigateBack', from: 'mouse' } as IRunActionInWindowRequest); } else if (cmd === forward) { - this.send('vscode:runAction', acrossEditors ? 'workbench.action.openNextRecentlyUsedEditor' : 'workbench.action.navigateForward'); + this.send('vscode:runAction', { id: acrossEditors ? 'workbench.action.openNextRecentlyUsedEditor' : 'workbench.action.navigateForward', from: 'mouse' } as IRunActionInWindowRequest); } }); } @@ -547,9 +556,6 @@ export class CodeWindow implements ICodeWindow { windowConfiguration.highContrast = isWindows && systemPreferences.isInvertedColorScheme() && (!windowConfig || windowConfig.autoDetectHighContrast); windowConfiguration.accessibilitySupport = app.isAccessibilitySupportEnabled(); - // Set Keyboard Config - windowConfiguration.isISOKeyboard = KeyboardLayoutMonitor.INSTANCE.isISOKeyboard(); - // Theme windowConfiguration.baseTheme = this.getBaseTheme(); windowConfiguration.backgroundColor = this.getBackgroundColor(); @@ -858,49 +864,71 @@ export class CodeWindow implements ICodeWindow { this._win.webContents.send(channel, ...args); } - public updateTouchBar(items: ICommandAction[][]): void { + public updateTouchBar(groups: ICommandAction[][]): void { if (!isMacintosh) { return; // only supported on macOS } - const groups: (Electron.TouchBarGroup | Electron.TouchBarSpacer)[] = []; + // Update segments for all groups. Setting the segments property + // of the group directly prevents ugly flickering from happening + this.touchBarGroups.forEach((touchBarGroup, index) => { + const commands = groups[index]; + touchBarGroup.segments = this.createTouchBarGroupSegments(commands); + }); + } - items.forEach(itemGroup => { - if (itemGroup.length) { + private createTouchBar(): void { + if (!isMacintosh) { + return; // only supported on macOS + } - // Group Segments - const groupSegments = itemGroup.map(item => { - let icon: Electron.NativeImage; - if (item.iconPath) { - icon = nativeImage.createFromPath(item.iconPath); - if (icon.isEmpty()) { - icon = void 0; - } - } + // To avoid flickering, we try to reuse the touch bar group + // as much as possible by creating a large number of groups + // for reusing later. + for (let i = 0; i < 10; i++) { + const groupTouchBar = this.createTouchBarGroup(); + this.touchBarGroups.push(groupTouchBar); + } - return { - label: !icon ? item.title as string : void 0, - icon - }; - }); + this._win.setTouchBar(new TouchBar({ items: this.touchBarGroups })); + } - // Group Touch Bar - const groupTouchBar = new TouchBar.TouchBarSegmentedControl({ - segments: groupSegments, - mode: 'buttons', - segmentStyle: 'automatic', - change: (selectedIndex) => { - this.sendWhenReady('vscode:runAction', itemGroup[selectedIndex].id); - } - }); + private createTouchBarGroup(items: ICommandAction[] = []): Electron.TouchBarSegmentedControl { - // Push and add small space between groups - groups.push(groupTouchBar); - groups.push(new TouchBar.TouchBarSpacer({ size: 'small' })); + // Group Segments + const segments = this.createTouchBarGroupSegments(items); + + // Group Control + const control = new TouchBar.TouchBarSegmentedControl({ + segments, + mode: 'buttons', + segmentStyle: 'automatic', + change: (selectedIndex) => { + this.sendWhenReady('vscode:runAction', { id: (control.segments[selectedIndex] as ITouchBarSegment).id, from: 'touchbar' }); } }); - this._win.setTouchBar(new TouchBar({ items: groups })); + return control; + } + + private createTouchBarGroupSegments(items: ICommandAction[] = []): ITouchBarSegment[] { + const segments: ITouchBarSegment[] = items.map(item => { + let icon: Electron.NativeImage; + if (item.iconPath) { + icon = nativeImage.createFromPath(item.iconPath); + if (icon.isEmpty()) { + icon = void 0; + } + } + + return { + id: item.id, + label: !icon ? item.title as string : void 0, + icon + }; + }); + + return segments; } public dispose(): void { diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index d29017c2752..505bc50be4f 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -32,6 +32,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { IWorkspacesMainService, IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, WORKSPACE_FILTER, isSingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { mnemonicButtonLabel } from 'vs/base/common/labels'; +import { Schemas } from 'vs/base/common/network'; enum WindowError { UNRESPONSIVE, @@ -1813,18 +1814,20 @@ class WorkspacesManager { } private getWorkspaceDialogDefaultPath(workspace?: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier): string { - let defaultPath: string; if (workspace) { if (isSingleFolderWorkspaceIdentifier(workspace)) { - defaultPath = dirname(workspace); - } else { - const resolvedWorkspace = this.workspacesService.resolveWorkspaceSync(workspace.configPath); - if (resolvedWorkspace && resolvedWorkspace.folders.length > 0) { - defaultPath = dirname(resolvedWorkspace.folders[0].path); + return dirname(workspace); + } + + const resolvedWorkspace = this.workspacesService.resolveWorkspaceSync(workspace.configPath); + if (resolvedWorkspace && resolvedWorkspace.folders.length > 0) { + for (const folder of resolvedWorkspace.folders) { + if (folder.uri.scheme === Schemas.file) { + return dirname(folder.uri.fsPath); + } } } } - - return defaultPath; + return void 0; } -} \ No newline at end of file +} diff --git a/src/vs/code/node/cli.ts b/src/vs/code/node/cli.ts index 7476115fcee..f528cfe4ce4 100644 --- a/src/vs/code/node/cli.ts +++ b/src/vs/code/node/cli.ts @@ -17,7 +17,10 @@ import * as os from 'os'; import { whenDeleted } from 'vs/base/node/pfs'; function shouldSpawnCliProcess(argv: ParsedArgs): boolean { - return argv['list-extensions'] || !!argv['install-extension'] || !!argv['uninstall-extension']; + return !!argv['install-source'] + || !!argv['list-extensions'] + || !!argv['install-extension'] + || !!argv['uninstall-extension']; } interface IMainCli { diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index 3ac32af3401..c822b373b3c 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -7,6 +7,7 @@ import { localize } from 'vs/nls'; import product from 'vs/platform/node/product'; import pkg from 'vs/platform/node/package'; import * as path from 'path'; +import * as fs from 'fs'; import { TPromise } from 'vs/base/common/winjs.base'; import { sequence } from 'vs/base/common/async'; @@ -16,7 +17,7 @@ import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment'; -import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; +import { EnvironmentService, getInstallSourcePath } from 'vs/platform/environment/node/environmentService'; import { IExtensionManagementService, IExtensionGalleryService, IExtensionManifest, IGalleryExtension, LocalExtensionType } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService'; @@ -50,6 +51,7 @@ type Task = { (): TPromise }; class Main { constructor( + @IEnvironmentService private environmentService: IEnvironmentService, @IExtensionManagementService private extensionManagementService: IExtensionManagementService, @IExtensionGalleryService private extensionGalleryService: IExtensionGalleryService ) { } @@ -57,7 +59,9 @@ class Main { run(argv: ParsedArgs): TPromise { // TODO@joao - make this contributable - if (argv['list-extensions']) { + if (argv['install-source']) { + return this.setInstallSource(argv['install-source']); + } else if (argv['list-extensions']) { return this.listExtensions(argv['show-versions']); } else if (argv['install-extension']) { const arg = argv['install-extension']; @@ -71,6 +75,13 @@ class Main { return undefined; } + private setInstallSource(installSource: string): TPromise { + return new TPromise((c, e) => { + const path = getInstallSourcePath(this.environmentService.userDataPath); + fs.writeFile(path, installSource.slice(0, 30), 'utf8', err => err ? e(err) : c(null)); + }); + } + private listExtensions(showVersions: boolean): TPromise { return this.extensionManagementService.getInstalled(LocalExtensionType.User).then(extensions => { extensions.forEach(e => console.log(getId(e.manifest, showVersions))); @@ -99,7 +110,7 @@ class Main { return TPromise.as(null); } - return this.extensionGalleryService.query({ names: [id] }) + return this.extensionGalleryService.query({ names: [id], source: 'cli' }) .then>(null, err => { if (err.responseText) { try { @@ -161,7 +172,7 @@ export function main(argv: ParsedArgs): TPromise { const envService = accessor.get(IEnvironmentService); return TPromise.join([envService.appSettingsHome, envService.extensionsPath].map(p => mkdirp(p))).then(() => { - const { appRoot, extensionsPath, extensionDevelopmentPath, isBuilt } = envService; + const { appRoot, extensionsPath, extensionDevelopmentPath, isBuilt, installSource } = envService; const services = new ServiceCollection(); services.set(IConfigurationService, new SyncDescriptor(ConfigurationService)); @@ -183,7 +194,7 @@ export function main(argv: ParsedArgs): TPromise { const config: ITelemetryServiceConfig = { appender: combinedAppender(...appenders), - commonProperties: resolveCommonProperties(product.commit, pkg.version), + commonProperties: resolveCommonProperties(product.commit, pkg.version, installSource), piiPaths: [appRoot, extensionsPath] }; diff --git a/src/vs/code/node/windowsFinder.ts b/src/vs/code/node/windowsFinder.ts index d8b0d1e93d4..f45fa1fcb1c 100644 --- a/src/vs/code/node/windowsFinder.ts +++ b/src/vs/code/node/windowsFinder.ts @@ -11,6 +11,7 @@ import * as platform from 'vs/base/common/platform'; import * as paths from 'vs/base/common/paths'; import { OpenContext } from 'vs/platform/windows/common/windows'; import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, IResolvedWorkspace } from 'vs/platform/workspaces/common/workspaces'; +import { Schemas } from 'vs/base/common/network'; export interface ISimpleWindow { openedWorkspace?: IWorkspaceIdentifier; @@ -62,7 +63,7 @@ function findWindowOnFilePath(windows: W[], filePath: s for (let i = 0; i < workspaceWindows.length; i++) { const window = workspaceWindows[i]; const resolvedWorkspace = workspaceResolver(window.openedWorkspace); - if (resolvedWorkspace && resolvedWorkspace.folders.some(folder => paths.isEqualOrParent(filePath, folder.path, !platform.isLinux /* ignorecase */))) { + if (resolvedWorkspace && resolvedWorkspace.folders.some(folder => folder.uri.scheme === Schemas.file && paths.isEqualOrParent(filePath, folder.uri.fsPath, !platform.isLinux /* ignorecase */))) { return window; } } @@ -164,4 +165,4 @@ export function findWindowOnWorkspaceOrFolderPath(windo return false; })[0]; -} \ No newline at end of file +} diff --git a/src/vs/code/test/node/windowsFinder.test.ts b/src/vs/code/test/node/windowsFinder.test.ts index 0651a98d6b2..2c0b84336ff 100644 --- a/src/vs/code/test/node/windowsFinder.test.ts +++ b/src/vs/code/test/node/windowsFinder.test.ts @@ -9,6 +9,7 @@ import path = require('path'); import { findBestWindowOrFolderForFile, ISimpleWindow, IBestWindowOrFolderOptions } from 'vs/code/node/windowsFinder'; import { OpenContext } from 'vs/platform/windows/common/windows'; import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { toWorkspaceFolders } from 'vs/platform/workspace/common/workspace'; const fixturesFolder = require.toUrl('./fixtures'); @@ -24,7 +25,7 @@ function options(custom?: Partial>): I reuseWindow: false, context: OpenContext.CLI, codeSettingsFolder: '_vscode', - workspaceResolver: workspace => { return workspace === testWorkspace ? { id: testWorkspace.id, configPath: workspace.configPath, folders: [{ path: path.join(fixturesFolder, 'vscode_workspace_1_folder') }, { path: path.join(fixturesFolder, 'vscode_workspace_2_folder') }] } : null; }, + workspaceResolver: workspace => { return workspace === testWorkspace ? { id: testWorkspace.id, configPath: workspace.configPath, folders: toWorkspaceFolders([{ path: path.join(fixturesFolder, 'vscode_workspace_1_folder') }, { path: path.join(fixturesFolder, 'vscode_workspace_2_folder') }]) } : null; }, ...custom }; } diff --git a/src/vs/editor/browser/controller/textAreaHandler.ts b/src/vs/editor/browser/controller/textAreaHandler.ts index 4b80421d71f..2ce35619e78 100644 --- a/src/vs/editor/browser/controller/textAreaHandler.ts +++ b/src/vs/editor/browser/controller/textAreaHandler.ts @@ -7,6 +7,7 @@ import 'vs/css!./textAreaHandler'; import * as platform from 'vs/base/common/platform'; import * as browser from 'vs/base/browser/browser'; +import * as strings from 'vs/base/common/strings'; import { TextAreaInput, ITextAreaInputHost, IPasteData, ICompositionData } from 'vs/editor/browser/controller/textAreaInput'; import { ISimpleModel, ITypeData, TextAreaState, PagedScreenReaderStrategy } from 'vs/editor/browser/controller/textAreaState'; import { Range } from 'vs/editor/common/core/range'; @@ -164,6 +165,20 @@ export class TextAreaHandler extends ViewPart { if (this._accessibilitySupport === platform.AccessibilitySupport.Disabled) { // We know for a fact that a screen reader is not attached + // On OSX, we write the character before the cursor to allow for "long-press" composition + if (platform.isMacintosh) { + const selection = this._selections[0]; + if (selection.isEmpty()) { + const position = selection.getStartPosition(); + if (position.column > 1) { + const lineContent = this._context.model.getLineContent(position.lineNumber); + const charBefore = lineContent.charAt(position.column - 2); + if (!strings.isHighSurrogate(charBefore.charCodeAt(0))) { + return new TextAreaState(charBefore, 1, 1, position, position); + } + } + } + } return TextAreaState.EMPTY; } diff --git a/src/vs/editor/browser/view/viewImpl.ts b/src/vs/editor/browser/view/viewImpl.ts index 79cf5f2c105..1a53e3d9814 100644 --- a/src/vs/editor/browser/view/viewImpl.ts +++ b/src/vs/editor/browser/view/viewImpl.ts @@ -420,6 +420,11 @@ export class View extends ViewEventHandler { this._context.model ); + if (this.contentWidgets.shouldRender()) { + // Give the content widgets a chance to set their max width before a possible synchronous layout + this.contentWidgets.onBeforeRender(viewportData); + } + if (this.viewLines.shouldRender()) { this.viewLines.renderText(viewportData); this.viewLines.onDidRender(); diff --git a/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts b/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts index 15adceca2e1..93934f1131b 100644 --- a/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts +++ b/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts @@ -13,6 +13,7 @@ import { ViewContext } from 'vs/editor/common/view/viewContext'; import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext'; import { Position, IPosition } from 'vs/editor/common/core/position'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; +import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; class Coordinate { _coordinateBrand: void; @@ -73,6 +74,14 @@ export class ViewContentWidgets extends ViewPart { public onFlushed(e: viewEvents.ViewFlushedEvent): boolean { return true; } + public onLineMappingChanged(e: viewEvents.ViewLineMappingChangedEvent): boolean { + let keys = Object.keys(this._widgets); + for (let i = 0, len = keys.length; i < len; i++) { + const widgetId = keys[i]; + this._widgets[widgetId].onLineMappingChanged(e); + } + return true; + } public onLinesChanged(e: viewEvents.ViewLinesChangedEvent): boolean { return true; } @@ -132,6 +141,14 @@ export class ViewContentWidgets extends ViewPart { return false; } + public onBeforeRender(viewportData: ViewportData): void { + let keys = Object.keys(this._widgets); + for (let i = 0, len = keys.length; i < len; i++) { + const widgetId = keys[i]; + this._widgets[widgetId].onBeforeRender(viewportData); + } + } + public prepareRender(ctx: RenderingContext): void { let keys = Object.keys(this._widgets); for (let i = 0, len = keys.length; i < len; i++) { @@ -173,8 +190,13 @@ class Widget { private _lineHeight: number; private _position: IPosition; + private _viewPosition: Position; private _preference: ContentWidgetPositionPreference[]; + private _cachedDomNodeClientWidth: number; + private _cachedDomNodeClientHeight: number; + private _maxWidth: number; private _isVisible: boolean; + private _renderData: Coordinate; constructor(context: ViewContext, viewDomNode: FastDomNode, actual: IContentWidget) { @@ -192,15 +214,18 @@ class Widget { this._contentLeft = this._context.configuration.editor.layoutInfo.contentLeft; this._lineHeight = this._context.configuration.editor.lineHeight; - this._position = null; + this._setPosition(null); this._preference = null; + this._cachedDomNodeClientWidth = -1; + this._cachedDomNodeClientHeight = -1; + this._maxWidth = this._getMaxWidth(); this._isVisible = false; this._renderData = null; this.domNode.setPosition((this._fixedOverflowWidgets && this.allowEditorOverflow) ? 'fixed' : 'absolute'); - this._updateMaxWidth(); this.domNode.setVisibility('hidden'); this.domNode.setAttribute('widgetId', this.id); + this.domNode.setMaxWidth(this._maxWidth); } public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): void { @@ -210,22 +235,40 @@ class Widget { if (e.layoutInfo) { this._contentLeft = this._context.configuration.editor.layoutInfo.contentLeft; this._contentWidth = this._context.configuration.editor.layoutInfo.contentWidth; - - this._updateMaxWidth(); + this._maxWidth = this._getMaxWidth(); } } - private _updateMaxWidth(): void { - const maxWidth = this.allowEditorOverflow - ? window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth - : this._contentWidth; + public onLineMappingChanged(e: viewEvents.ViewLineMappingChangedEvent): void { + this._setPosition(this._position); + } - this.domNode.setMaxWidth(maxWidth); + private _setPosition(position: IPosition): void { + this._position = position; + this._viewPosition = null; + + if (this._position) { + // Do not trust that widgets give a valid position + const validModelPosition = this._context.model.validateModelPosition(this._position); + if (this._context.model.coordinatesConverter.modelPositionIsVisible(validModelPosition)) { + this._viewPosition = this._context.model.coordinatesConverter.convertModelPositionToViewPosition(validModelPosition); + } + } + } + + private _getMaxWidth(): number { + return ( + this.allowEditorOverflow + ? window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth + : this._contentWidth + ); } public setPosition(position: IPosition, preference: ContentWidgetPositionPreference[]): void { - this._position = position; + this._setPosition(position); this._preference = preference; + this._cachedDomNodeClientWidth = -1; + this._cachedDomNodeClientHeight = -1; } private _layoutBoxInViewport(topLeft: Coordinate, width: number, height: number, ctx: RenderingContext): IBoxLayoutResult { @@ -312,50 +355,44 @@ class Widget { return new Coordinate(topLeft.top, topLeft.left + this._contentLeft); } - private _getTopLeft(ctx: RenderingContext, position: Position): Coordinate { - const visibleRange = ctx.visibleRangeForPosition(position); + /** + * Compute `this._topLeft` + */ + private _getTopLeft(ctx: RenderingContext): Coordinate { + if (!this._viewPosition) { + return null; + } + + const visibleRange = ctx.visibleRangeForPosition(this._viewPosition); if (!visibleRange) { return null; } - const top = ctx.getVerticalOffsetForLineNumber(position.lineNumber) - ctx.scrollTop; + const top = ctx.getVerticalOffsetForLineNumber(this._viewPosition.lineNumber) - ctx.scrollTop; return new Coordinate(top, visibleRange.left); } - private _prepareRenderWidget(ctx: RenderingContext): Coordinate { - if (!this._position || !this._preference) { + private _prepareRenderWidget(topLeft: Coordinate, ctx: RenderingContext): Coordinate { + if (!topLeft) { return null; } - // Do not trust that widgets have a valid position - let validModelPosition = this._context.model.validateModelPosition(this._position); - - if (!this._context.model.coordinatesConverter.modelPositionIsVisible(validModelPosition)) { - // this position is hidden by the view model - return null; - } - - let position = this._context.model.coordinatesConverter.convertModelPositionToViewPosition(validModelPosition); - let placement: IBoxLayoutResult = null; let fetchPlacement = (): void => { if (placement) { return; } - const topLeft = this._getTopLeft(ctx, position); - if (!topLeft) { - return; + if (this._cachedDomNodeClientWidth === -1 || this._cachedDomNodeClientHeight === -1) { + const domNode = this.domNode.domNode; + this._cachedDomNodeClientWidth = domNode.clientWidth; + this._cachedDomNodeClientHeight = domNode.clientHeight; } - const domNode = this.domNode.domNode; - const width = domNode.clientWidth; - const height = domNode.clientHeight; - if (this.allowEditorOverflow) { - placement = this._layoutBoxInPage(topLeft, width, height, ctx); + placement = this._layoutBoxInPage(topLeft, this._cachedDomNodeClientWidth, this._cachedDomNodeClientHeight, ctx); } else { - placement = this._layoutBoxInViewport(topLeft, width, height, ctx); + placement = this._layoutBoxInViewport(topLeft, this._cachedDomNodeClientWidth, this._cachedDomNodeClientHeight, ctx); } }; @@ -382,11 +419,6 @@ class Widget { return new Coordinate(placement.belowTop, placement.left); } } else { - const topLeft = this._getTopLeft(ctx, position); - if (!topLeft) { - // Widget outside of viewport - return null; - } if (this.allowEditorOverflow) { return this._prepareRenderWidgetAtExactPositionOverflowing(topLeft); } else { @@ -398,8 +430,25 @@ class Widget { return null; } + /** + * On this first pass, we ensure that the content widget (if it is in the viewport) has the max width set correctly. + */ + public onBeforeRender(viewportData: ViewportData): void { + if (!this._viewPosition || !this._preference) { + return; + } + + if (this._viewPosition.lineNumber < viewportData.startLineNumber || this._viewPosition.lineNumber > viewportData.endLineNumber) { + // Outside of viewport + return; + } + + this.domNode.setMaxWidth(this._maxWidth); + } + public prepareRender(ctx: RenderingContext): void { - this._renderData = this._prepareRenderWidget(ctx); + const topLeft = this._getTopLeft(ctx); + this._renderData = this._prepareRenderWidget(topLeft, ctx); } public render(ctx: RestrictedRenderingContext): void { diff --git a/src/vs/editor/browser/viewParts/indentGuides/indentGuides.ts b/src/vs/editor/browser/viewParts/indentGuides/indentGuides.ts index dfd4f11d014..b50adb24b77 100644 --- a/src/vs/editor/browser/viewParts/indentGuides/indentGuides.ts +++ b/src/vs/editor/browser/viewParts/indentGuides/indentGuides.ts @@ -77,6 +77,9 @@ export class IndentGuidesOverlay extends DynamicViewOverlay { public onZonesChanged(e: viewEvents.ViewZonesChangedEvent): boolean { return true; } + public onLanguageConfigurationChanged(e: viewEvents.ViewLanguageConfigurationEvent): boolean { + return true; + } // --- end event handlers diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts index 71c534741ef..ab984ed37ec 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimap.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts @@ -709,7 +709,7 @@ export class Minimap extends ViewPart { const imageData = this._getBuffer(); // Render untouched lines by using last rendered data. - let needed = Minimap._renderUntouchedLines( + let [_dirtyY1, _dirtyY2, needed] = Minimap._renderUntouchedLines( imageData, startLineNumber, endLineNumber, @@ -744,9 +744,13 @@ export class Minimap extends ViewPart { dy += minimapLineHeight; } + const dirtyY1 = (_dirtyY1 === -1 ? 0 : _dirtyY1); + const dirtyY2 = (_dirtyY2 === -1 ? imageData.height : _dirtyY2); + const dirtyHeight = dirtyY2 - dirtyY1; + // Finally, paint to the canvas const ctx = this._canvas.domNode.getContext('2d'); - ctx.putImageData(imageData, 0, 0); + ctx.putImageData(imageData, 0, 0, 0, dirtyY1, imageData.width, dirtyHeight); // Save rendered data for reuse on next frame if possible return new RenderData( @@ -762,14 +766,14 @@ export class Minimap extends ViewPart { endLineNumber: number, minimapLineHeight: number, lastRenderData: RenderData, - ): boolean[] { + ): [number, number, boolean[]] { let needed: boolean[] = []; if (!lastRenderData) { for (let i = 0, len = endLineNumber - startLineNumber + 1; i < len; i++) { needed[i] = true; } - return needed; + return [-1, -1, needed]; } const _lastData = lastRenderData._get(); @@ -780,6 +784,10 @@ export class Minimap extends ViewPart { const WIDTH = target.width; const targetData = target.data; + const maxDestPixel = (endLineNumber - startLineNumber + 1) * minimapLineHeight * WIDTH * 4; + let dirtyPixel1 = -1; // the pixel offset up to which all the data is equal to the prev frame + let dirtyPixel2 = -1; // the pixel offset after which all the data is equal to the prev frame + let copySourceStart = -1; let copySourceEnd = -1; let copyDestStart = -1; @@ -810,6 +818,12 @@ export class Minimap extends ViewPart { if (copySourceStart !== -1) { // flush existing copy request targetData.set(lastTargetData.subarray(copySourceStart, copySourceEnd), copyDestStart); + if (dirtyPixel1 === -1 && copySourceStart === 0 && copySourceStart === copyDestStart) { + dirtyPixel1 = copySourceEnd; + } + if (dirtyPixel2 === -1 && copySourceEnd === maxDestPixel && copySourceStart === copyDestStart) { + dirtyPixel2 = copySourceStart; + } } copySourceStart = sourceStart; copySourceEnd = sourceEnd; @@ -824,9 +838,18 @@ export class Minimap extends ViewPart { if (copySourceStart !== -1) { // flush existing copy request targetData.set(lastTargetData.subarray(copySourceStart, copySourceEnd), copyDestStart); + if (dirtyPixel1 === -1 && copySourceStart === 0 && copySourceStart === copyDestStart) { + dirtyPixel1 = copySourceEnd; + } + if (dirtyPixel2 === -1 && copySourceEnd === maxDestPixel && copySourceStart === copyDestStart) { + dirtyPixel2 = copySourceStart; + } } - return needed; + const dirtyY1 = (dirtyPixel1 === -1 ? -1 : dirtyPixel1 / (WIDTH * 4)); + const dirtyY2 = (dirtyPixel2 === -1 ? -1 : dirtyPixel2 / (WIDTH * 4)); + + return [dirtyY1, dirtyY2, needed]; } private static _renderLine( diff --git a/src/vs/editor/common/commonCodeEditor.ts b/src/vs/editor/common/commonCodeEditor.ts index 29700d8baf2..56a286d4baf 100644 --- a/src/vs/editor/common/commonCodeEditor.ts +++ b/src/vs/editor/common/commonCodeEditor.ts @@ -23,7 +23,7 @@ import { hash } from 'vs/base/common/hash'; import { EditorModeContext } from 'vs/editor/common/modes/editorModeContext'; import { IModelContentChangedEvent, IModelDecorationsChangedEvent, - IModelLanguageChangedEvent, IModelOptionsChangedEvent, TextModelEventType + IModelLanguageChangedEvent, IModelOptionsChangedEvent, TextModelEventType, IModelLanguageConfigurationChangedEvent } from 'vs/editor/common/model/textModelEvents'; import * as editorOptions from 'vs/editor/common/config/editorOptions'; import { ICursorPositionChangedEvent, ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; @@ -44,6 +44,9 @@ export abstract class CommonCodeEditor extends Disposable implements editorCommo private readonly _onDidChangeModelLanguage: Emitter = this._register(new Emitter()); public readonly onDidChangeModelLanguage: Event = this._onDidChangeModelLanguage.event; + private readonly _onDidChangeModelLanguageConfiguration: Emitter = this._register(new Emitter()); + public readonly onDidChangeModelLanguageConfiguration: Event = this._onDidChangeModelLanguageConfiguration.event; + private readonly _onDidChangeModelOptions: Emitter = this._register(new Emitter()); public readonly onDidChangeModelOptions: Event = this._onDidChangeModelOptions.event; @@ -170,7 +173,7 @@ export abstract class CommonCodeEditor extends Disposable implements editorCommo // editor actions don't need to be disposed this._actions = {}; - + this._removeDecorationTypes(); this._postDetachModelCleanup(this._detachModel()); this._onDidDispose.fire(); @@ -232,10 +235,24 @@ export abstract class CommonCodeEditor extends Disposable implements editorCommo newModelUrl: model ? model.uri : null }; + this._removeDecorationTypes(); this._onDidChangeModel.fire(e); this._postDetachModelCleanup(detachedModel); } + private _removeDecorationTypes(): void { + this._decorationTypeKeysToIds = {}; + if (this._decorationTypeSubtypes) { + for (let decorationType in this._decorationTypeSubtypes) { + let subTypes = this._decorationTypeSubtypes[decorationType]; + for (let subType in subTypes) { + this._removeDecorationType(decorationType + '-' + subType); + } + } + this._decorationTypeSubtypes = {}; + } + } + public getCenteredRangeInViewport(): Range { if (!this.hasView) { return null; @@ -450,7 +467,7 @@ export abstract class CommonCodeEditor extends Disposable implements editorCommo public revealRange(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth, revealVerticalInCenter: boolean = false, revealHorizontal: boolean = true): void { this._revealRange( range, - false ? VerticalRevealType.Center : VerticalRevealType.Simple, + revealVerticalInCenter ? VerticalRevealType.Center : VerticalRevealType.Simple, revealHorizontal, scrollType ); @@ -887,6 +904,10 @@ export abstract class CommonCodeEditor extends Disposable implements editorCommo this._onDidChangeModelLanguage.fire(e); break; + case TextModelEventType.ModelLanguageConfigurationChanged: + this._onDidChangeModelLanguageConfiguration.fire(e); + break; + case TextModelEventType.ModelContentChanged: this._onDidChangeModelContent.fire(e); break; @@ -948,16 +969,6 @@ export abstract class CommonCodeEditor extends Disposable implements editorCommo protected _postDetachModelCleanup(detachedModel: editorCommon.IModel): void { if (detachedModel) { - this._decorationTypeKeysToIds = {}; - if (this._decorationTypeSubtypes) { - for (let decorationType in this._decorationTypeSubtypes) { - let subTypes = this._decorationTypeSubtypes[decorationType]; - for (let subType in subTypes) { - this._removeDecorationType(decorationType + '-' + subType); - } - } - this._decorationTypeSubtypes = {}; - } detachedModel.removeAllDecorationsWithOwnerId(this.id); } } diff --git a/src/vs/editor/common/controller/cursor.ts b/src/vs/editor/common/controller/cursor.ts index 89edd8d344c..65bc4059e5c 100644 --- a/src/vs/editor/common/controller/cursor.ts +++ b/src/vs/editor/common/controller/cursor.ts @@ -13,7 +13,6 @@ import { Range } from 'vs/editor/common/core/range'; import { Selection, SelectionDirection, ISelection } from 'vs/editor/common/core/selection'; import * as editorCommon from 'vs/editor/common/editorCommon'; import { CursorColumns, CursorConfiguration, EditOperationResult, CursorContext, CursorState, RevealTarget, IColumnSelectData, ICursors } from 'vs/editor/common/controller/cursorCommon'; -import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { DeleteOperations } from 'vs/editor/common/controller/cursorDeleteOperations'; import { TypeOperations } from 'vs/editor/common/controller/cursorTypeOperations'; import { TextModelEventType, ModelRawContentChangedEvent, RawContentChangedType } from 'vs/editor/common/model/textModelEvents'; @@ -150,11 +149,10 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors { this.context = new CursorContext(this._configuration, this._model, this._viewModel); this._cursors.updateContext(this.context); }; - this._register(this._model.onDidChangeLanguage((e) => { + this._register(model.onDidChangeLanguage((e) => { updateCursorContext(); })); - this._register(LanguageConfigurationRegistry.onDidChange(() => { - // TODO@Alex: react only if certain supports changed? (and if my model's mode changed) + this._register(model.onDidChangeLanguageConfiguration(() => { updateCursorContext(); })); this._register(model.onDidChangeOptions(() => { diff --git a/src/vs/editor/common/editorCommon.ts b/src/vs/editor/common/editorCommon.ts index a8faeb62a75..ab9db53d0ce 100644 --- a/src/vs/editor/common/editorCommon.ts +++ b/src/vs/editor/common/editorCommon.ts @@ -19,7 +19,7 @@ import { IndentRange } from 'vs/editor/common/model/indentRanges'; import { ITextSource } from 'vs/editor/common/model/textSource'; import { ModelRawContentChangedEvent, IModelContentChangedEvent, IModelDecorationsChangedEvent, - IModelLanguageChangedEvent, IModelOptionsChangedEvent + IModelLanguageChangedEvent, IModelOptionsChangedEvent, IModelLanguageConfigurationChangedEvent } from 'vs/editor/common/model/textModelEvents'; import * as editorOptions from 'vs/editor/common/config/editorOptions'; import { ICursorPositionChangedEvent, ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; @@ -585,26 +585,17 @@ export interface ITextModel { */ getLineContent(lineNumber: number): string; - /** - * @internal - */ - getIndentLevel(lineNumber: number): number; - - /** - * @internal - */ - getIndentRanges(): IndentRange[]; - - /** - * @internal - */ - getLineIndentGuide(lineNumber: number): number; /** * Get the text for all lines. */ getLinesContent(): string[]; + /** + * @internal + */ + getIndentLevel(lineNumber: number): number; + /** * Get the end of line sequence predominantly used in the text buffer. * @return EOL char sequence (e.g.: '\n' or '\r\n'). @@ -912,6 +903,16 @@ export interface ITokenizedModel extends ITextModel { * @internal */ matchBracket(position: IPosition): [Range, Range]; + + /** + * @internal + */ + getIndentRanges(): IndentRange[]; + + /** + * @internal + */ + getLineIndentGuide(lineNumber: number): number; } /** @@ -1149,6 +1150,11 @@ export interface IModel extends IReadOnlyModel, IEditableTextModel, ITextModelWi * @event */ onDidChangeLanguage(listener: (e: IModelLanguageChangedEvent) => void): IDisposable; + /** + * An event emitted when the language configuration associated with the model has changed. + * @event + */ + onDidChangeLanguageConfiguration(listener: (e: IModelLanguageConfigurationChangedEvent) => void): IDisposable; /** * An event emitted right before disposing the model. * @event @@ -1748,6 +1754,11 @@ export interface ICommonCodeEditor extends IEditor { * @event */ onDidChangeModelLanguage(listener: (e: IModelLanguageChangedEvent) => void): IDisposable; + /** + * An event emitted when the language configuration of the current model has changed. + * @event + */ + onDidChangeModelLanguageConfiguration(listener: (e: IModelLanguageConfigurationChangedEvent) => void): IDisposable; /** * An event emitted when the options of the current model has changed. * @event diff --git a/src/vs/editor/common/model/indentRanges.ts b/src/vs/editor/common/model/indentRanges.ts index f220df0cfba..272b011460d 100644 --- a/src/vs/editor/common/model/indentRanges.ts +++ b/src/vs/editor/common/model/indentRanges.ts @@ -12,11 +12,13 @@ export class IndentRange { startLineNumber: number; endLineNumber: number; indent: number; + marker: boolean; - constructor(startLineNumber: number, endLineNumber: number, indent: number) { + constructor(startLineNumber: number, endLineNumber: number, indent: number, marker?: boolean) { this.startLineNumber = startLineNumber; this.endLineNumber = endLineNumber; this.indent = indent; + this.marker = marker; } public static deepCloneArr(indentRanges: IndentRange[]): IndentRange[] { @@ -29,39 +31,80 @@ export class IndentRange { } } -export function computeRanges(model: ITextModel, minimumRangeSize: number = 1): IndentRange[] { +export interface FoldMarkers { + start: string; + end: string; + indent?: number; +} + +interface PreviousRegion { indent: number; line: number; marker: RegExp; }; + +export function computeRanges(model: ITextModel, offSide: boolean, markers?: FoldMarkers, minimumRangeSize: number = 1): IndentRange[] { let result: IndentRange[] = []; - let previousRegions: { indent: number, line: number }[] = []; - previousRegions.push({ indent: -1, line: model.getLineCount() + 1 }); // sentinel, to make sure there's at least one entry + let pattern = void 0; + let patternIndent = -1; + if (markers) { + pattern = new RegExp(`(${markers.start})|(?:${markers.end})`); + patternIndent = typeof markers.indent === 'number' ? markers.indent : -1; + } + + let previousRegions: PreviousRegion[] = []; + previousRegions.push({ indent: -1, line: model.getLineCount() + 1, marker: null }); // sentinel, to make sure there's at least one entry for (let line = model.getLineCount(); line > 0; line--) { let indent = model.getIndentLevel(line); + let previous = previousRegions[previousRegions.length - 1]; if (indent === -1) { + if (offSide) { + // for offSide languages, empty lines are associated to the next block + previous.line = line; + } continue; // only whitespace } + let m; + if (pattern && (patternIndent === -1 || patternIndent === indent) && (m = model.getLineContent(line).match(pattern))) { + // folding pattern match + if (m[1]) { // start pattern match + if (previous.indent >= 0 && !previous.marker) { - let previous = previousRegions[previousRegions.length - 1]; - - if (previous.indent > indent) { - // discard all regions with larger indent - do { - previousRegions.pop(); - previous = previousRegions[previousRegions.length - 1]; - } while (previous.indent > indent); - - // new folding range - let endLineNumber = previous.line - 1; - if (endLineNumber - line >= minimumRangeSize) { - result.push(new IndentRange(line, endLineNumber, indent)); + // discard all regions until the folding pattern + do { + previousRegions.pop(); + previous = previousRegions[previousRegions.length - 1]; + } while (previous.indent >= 0 && !previous.marker); + } + if (previous.marker) { + // new folding range from pattern, includes the end line + result.push(new IndentRange(line, previous.line, indent, true)); + previous.marker = null; + previous.indent = indent; + previous.line = line; + } + } else { // end pattern match + previousRegions.push({ indent: -2, line, marker: pattern }); + } + } else { + if (previous.indent > indent) { + // discard all regions with larger indent + do { + previousRegions.pop(); + previous = previousRegions[previousRegions.length - 1]; + } while (previous.indent > indent); + + // new folding range + let endLineNumber = previous.line - 1; + if (endLineNumber - line >= minimumRangeSize) { + result.push(new IndentRange(line, endLineNumber, indent)); + } + } + if (previous.indent === indent) { + previous.line = line; + } else { // previous.indent < indent + // new region with a bigger indent + previousRegions.push({ indent, line, marker: null }); } - } - if (previous.indent === indent) { - previous.line = line; - } else { // previous.indent < indent - // new region with a bigger indent - previousRegions.push({ indent, line }); } } diff --git a/src/vs/editor/common/model/model.ts b/src/vs/editor/common/model/model.ts index aef76d48016..1228592fe0f 100644 --- a/src/vs/editor/common/model/model.ts +++ b/src/vs/editor/common/model/model.ts @@ -34,7 +34,9 @@ export class Model extends EditableTextModel implements IModel { public onDidChangeLanguage(listener: (e: textModelEvents.IModelLanguageChangedEvent) => void): IDisposable { return this._eventEmitter.addListener(textModelEvents.TextModelEventType.ModelLanguageChanged, listener); } - + public onDidChangeLanguageConfiguration(listener: (e: textModelEvents.IModelLanguageConfigurationChangedEvent) => void): IDisposable { + return this._eventEmitter.addListener(textModelEvents.TextModelEventType.ModelLanguageConfigurationChanged, listener); + } public static createFromString(text: string, options: ITextModelCreationOptions = TextModel.DEFAULT_CREATION_OPTIONS, languageIdentifier: LanguageIdentifier = null, uri: URI = null): Model { return new Model(RawTextSource.fromString(text), options, languageIdentifier, uri); } diff --git a/src/vs/editor/common/model/textModel.ts b/src/vs/editor/common/model/textModel.ts index ce088944f8d..3e55882ed96 100644 --- a/src/vs/editor/common/model/textModel.ts +++ b/src/vs/editor/common/model/textModel.ts @@ -13,7 +13,6 @@ import { ModelLine, IModelLine, MinimalModelLine } from 'vs/editor/common/model/ import { guessIndentation } from 'vs/editor/common/model/indentationGuesser'; import { EDITOR_MODEL_DEFAULTS } from 'vs/editor/common/config/editorOptions'; import { PrefixSumComputer } from 'vs/editor/common/viewModel/prefixSumComputer'; -import { IndentRange, computeRanges } from 'vs/editor/common/model/indentRanges'; import { TextModelSearch, SearchParams } from 'vs/editor/common/model/textModelSearch'; import { TextSource, ITextSource, IRawTextSource, RawTextSource } from 'vs/editor/common/model/textSource'; import { IDisposable } from 'vs/base/common/lifecycle'; @@ -85,7 +84,6 @@ export class TextModel implements editorCommon.ITextModel { protected _isDisposing: boolean; protected _options: editorCommon.TextModelResolvedOptions; protected _lineStarts: PrefixSumComputer; - private _indentRanges: IndentRange[]; private _versionId: number; /** @@ -513,65 +511,6 @@ export class TextModel implements editorCommon.ITextModel { return this._lines[lineNumber - 1].getIndentLevel(); } - protected _resetIndentRanges(): void { - this._indentRanges = null; - } - - private _getIndentRanges(): IndentRange[] { - if (!this._indentRanges) { - this._indentRanges = computeRanges(this); - } - return this._indentRanges; - } - - public getIndentRanges(): IndentRange[] { - this._assertNotDisposed(); - let indentRanges = this._getIndentRanges(); - return IndentRange.deepCloneArr(indentRanges); - } - - private _toValidLineIndentGuide(lineNumber: number, indentGuide: number): number { - let lineIndentLevel = this._lines[lineNumber - 1].getIndentLevel(); - if (lineIndentLevel === -1) { - return indentGuide; - } - let maxIndentGuide = Math.ceil(lineIndentLevel / this._options.tabSize); - return Math.min(maxIndentGuide, indentGuide); - } - - public getLineIndentGuide(lineNumber: number): number { - this._assertNotDisposed(); - if (lineNumber < 1 || lineNumber > this.getLineCount()) { - throw new Error('Illegal value ' + lineNumber + ' for `lineNumber`'); - } - - let indentRanges = this._getIndentRanges(); - - for (let i = indentRanges.length - 1; i >= 0; i--) { - let rng = indentRanges[i]; - - if (rng.startLineNumber === lineNumber) { - return this._toValidLineIndentGuide(lineNumber, Math.ceil(rng.indent / this._options.tabSize)); - } - if (rng.startLineNumber < lineNumber && lineNumber <= rng.endLineNumber) { - return this._toValidLineIndentGuide(lineNumber, 1 + Math.floor(rng.indent / this._options.tabSize)); - } - if (rng.endLineNumber + 1 === lineNumber) { - let bestIndent = rng.indent; - while (i > 0) { - i--; - rng = indentRanges[i]; - if (rng.endLineNumber + 1 === lineNumber) { - bestIndent = rng.indent; - } - } - return this._toValidLineIndentGuide(lineNumber, Math.ceil(bestIndent / this._options.tabSize)); - } - } - - return 0; - } - public getLinesContent(): string[] { this._assertNotDisposed(); var r: string[] = []; @@ -785,7 +724,6 @@ export class TextModel implements editorCommon.ITextModel { this._EOL = textSource.EOL; this._lines = modelLines; this._lineStarts = null; - this._resetIndentRanges(); } private _getEndOfLine(eol: editorCommon.EndOfLinePreference): string { diff --git a/src/vs/editor/common/model/textModelEvents.ts b/src/vs/editor/common/model/textModelEvents.ts index 890dd25df46..2acaf260be2 100644 --- a/src/vs/editor/common/model/textModelEvents.ts +++ b/src/vs/editor/common/model/textModelEvents.ts @@ -18,6 +18,7 @@ export const TextModelEventType = { ModelContentChanged: 'contentChanged', ModelRawContentChanged2: 'rawContentChanged2', ModelDecorationsChanged: 'decorationsChanged', + ModelLanguageConfigurationChanged: 'modelLanguageConfigurationChanged' }; /** @@ -34,6 +35,12 @@ export interface IModelLanguageChangedEvent { readonly newLanguage: string; } +/** + * An event describing that the language configuration associated with a model has changed. + */ +export interface IModelLanguageConfigurationChangedEvent { +} + export interface IModelContentChange { /** * The range that got replaced. diff --git a/src/vs/editor/common/model/textModelWithTokens.ts b/src/vs/editor/common/model/textModelWithTokens.ts index d4f31ffcc58..0006a7da417 100644 --- a/src/vs/editor/common/model/textModelWithTokens.ts +++ b/src/vs/editor/common/model/textModelWithTokens.ts @@ -22,6 +22,7 @@ import { getWordAtText } from 'vs/editor/common/model/wordHelper'; import { TokenizationResult2 } from 'vs/editor/common/core/token'; import { ITextSource, IRawTextSource } from 'vs/editor/common/model/textSource'; import * as textModelEvents from 'vs/editor/common/model/textModelEvents'; +import { IndentRange, computeRanges } from 'vs/editor/common/model/indentRanges'; class ModelTokensChangedEventBuilder { @@ -69,6 +70,9 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke private _invalidLineStartIndex: number; private _lastState: IState; + private _indentRanges: IndentRange[]; + private _languageRegistryListener: IDisposable; + private _revalidateTokensTimeout: number; constructor(rawTextSource: IRawTextSource, creationOptions: editorCommon.ITextModelCreationOptions, languageIdentifier: LanguageIdentifier) { @@ -95,11 +99,20 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke this._revalidateTokensTimeout = -1; + this._languageRegistryListener = LanguageConfigurationRegistry.onDidChange((e) => { + if (e.languageIdentifier.id === this._languageIdentifier.id) { + this._resetIndentRanges(); + this._emitModelLanguageConfigurationEvent({}); + } + }); + this._resetTokenizationState(); + this._resetIndentRanges(); } public dispose(): void { this._tokenizationListener.dispose(); + this._languageRegistryListener.dispose(); this._clearTimers(); this._lastState = null; @@ -114,6 +127,7 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke super._resetValue(newValue); // Cancel tokenization, clear all tokens and begin tokenizing this._resetTokenizationState(); + this._resetIndentRanges(); } protected _resetTokenizationState(): void { @@ -225,6 +239,7 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke // Cancel tokenization, clear all tokens and begin tokenizing this._resetTokenizationState(); + this._resetIndentRanges(); this.emitModelTokensChangedEvent({ ranges: [{ @@ -233,6 +248,7 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke }] }); this._emitModelModeChangedEvent(e); + this._emitModelLanguageConfigurationEvent({}); } public getLanguageIdAtPosition(_lineNumber: number, _column: number): LanguageId { @@ -398,6 +414,12 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke } } + private _emitModelLanguageConfigurationEvent(e: textModelEvents.IModelLanguageConfigurationChangedEvent): void { + if (!this._isDisposing) { + this._eventEmitter.emit(textModelEvents.TextModelEventType.ModelLanguageConfigurationChanged, e); + } + } + private _emitModelModeChangedEvent(e: textModelEvents.IModelLanguageChangedEvent): void { if (!this._isDisposing) { this._eventEmitter.emit(textModelEvents.TextModelEventType.ModelLanguageChanged, e); @@ -814,4 +836,66 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke isOpen: modeBrackets.textIsOpenBracket[text] }; } + + protected _resetIndentRanges(): void { + this._indentRanges = null; + } + + private _getIndentRanges(): IndentRange[] { + if (!this._indentRanges) { + let foldingRules = LanguageConfigurationRegistry.getFoldingRules(this._languageIdentifier.id); + let offSide = foldingRules && foldingRules.offSide; + let markers = foldingRules && foldingRules['markers']; + this._indentRanges = computeRanges(this, offSide, markers); + } + return this._indentRanges; + } + + public getIndentRanges(): IndentRange[] { + this._assertNotDisposed(); + let indentRanges = this._getIndentRanges(); + return IndentRange.deepCloneArr(indentRanges); + } + + public getLineIndentGuide(lineNumber: number): number { + this._assertNotDisposed(); + if (lineNumber < 1 || lineNumber > this.getLineCount()) { + throw new Error('Illegal value ' + lineNumber + ' for `lineNumber`'); + } + + let indentRanges = this._getIndentRanges(); + + for (let i = indentRanges.length - 1; i >= 0; i--) { + let rng = indentRanges[i]; + + if (rng.startLineNumber === lineNumber) { + return this._toValidLineIndentGuide(lineNumber, Math.ceil(rng.indent / this._options.tabSize)); + } + if (rng.startLineNumber < lineNumber && lineNumber <= rng.endLineNumber) { + return this._toValidLineIndentGuide(lineNumber, 1 + Math.floor(rng.indent / this._options.tabSize)); + } + if (rng.endLineNumber + 1 === lineNumber) { + let bestIndent = rng.indent; + while (i > 0) { + i--; + rng = indentRanges[i]; + if (rng.endLineNumber + 1 === lineNumber) { + bestIndent = rng.indent; + } + } + return this._toValidLineIndentGuide(lineNumber, Math.ceil(bestIndent / this._options.tabSize)); + } + } + + return 0; + } + + private _toValidLineIndentGuide(lineNumber: number, indentGuide: number): number { + let lineIndentLevel = this._lines[lineNumber - 1].getIndentLevel(); + if (lineIndentLevel === -1) { + return indentGuide; + } + let maxIndentGuide = Math.ceil(lineIndentLevel / this._options.tabSize); + return Math.min(maxIndentGuide, indentGuide); + } } diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 9c01e6aaafb..9184a639da4 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -247,6 +247,22 @@ export interface ISuggestResult { dispose?(): void; } +/** + * How a suggest provider was triggered. + */ +export enum SuggestTriggerKind { + Invoke = 0, + TriggerCharacter = 1 +} + +/** + * @internal + */ +export interface SuggestContext { + triggerKind: SuggestTriggerKind; + triggerCharacter?: string; +} + /** * @internal */ @@ -254,7 +270,7 @@ export interface ISuggestSupport { triggerCharacters?: string[]; - provideCompletionItems(model: editorCommon.IModel, position: Position, token: CancellationToken): ISuggestResult | Thenable; + provideCompletionItems(model: editorCommon.IModel, position: Position, context: SuggestContext, token: CancellationToken): ISuggestResult | Thenable; resolveCompletionItem?(model: editorCommon.IModel, position: Position, item: ISuggestion, token: CancellationToken): ISuggestion | Thenable; } @@ -728,7 +744,7 @@ export interface DocumentColorProvider { /** * Provide the string representations for a color. */ - provideColorPresentations(colorInfo: IColorInformation, token: CancellationToken): IColorPresentation[] | Thenable; + provideColorPresentations(model: editorCommon.IReadOnlyModel, colorInfo: IColorInformation, token: CancellationToken): IColorPresentation[] | Thenable; } export interface IResourceEdit { diff --git a/src/vs/editor/common/modes/languageConfiguration.ts b/src/vs/editor/common/modes/languageConfiguration.ts index 1fec47f4806..093db6b102a 100644 --- a/src/vs/editor/common/modes/languageConfiguration.ts +++ b/src/vs/editor/common/modes/languageConfiguration.ts @@ -61,6 +61,12 @@ export interface LanguageConfiguration { * settings will be used. */ surroundingPairs?: IAutoClosingPair[]; + + /** + * The language's folding rules. + */ + folding?: FoldingRules; + /** * **Deprecated** Do not use. * @@ -89,6 +95,20 @@ export interface IndentationRule { * If a line matches this pattern, then its indentation should not be changed and it should not be evaluated against the other rules. */ unIndentedLinePattern?: RegExp; + +} + +/** + * Describes folding rules for a language. + */ +export interface FoldingRules { + /** + * Used by the indentation based strategy to decide wheter empty lines belong to the previous or the next block. + * A language adheres to the off-side rule if blocks in that language are expressed by their indentation. + * See [wikipedia](https://en.wikipedia.org/wiki/Off-side_rule) for more information. + * If not set, `false` is used and empty lines belong to the previous block. + */ + offSide?: boolean; } /** diff --git a/src/vs/editor/common/modes/languageConfigurationRegistry.ts b/src/vs/editor/common/modes/languageConfigurationRegistry.ts index dd3e1ec8acc..f069921add2 100644 --- a/src/vs/editor/common/modes/languageConfigurationRegistry.ts +++ b/src/vs/editor/common/modes/languageConfigurationRegistry.ts @@ -18,7 +18,7 @@ import { DEFAULT_WORD_REGEXP, ensureValidWordDefinition } from 'vs/editor/common import { createScopedLineTokens } from 'vs/editor/common/modes/supports'; import { LineTokens } from 'vs/editor/common/core/lineTokens'; import { Range } from 'vs/editor/common/core/range'; -import { IndentAction, EnterAction, IAutoClosingPair, LanguageConfiguration, IndentationRule } from 'vs/editor/common/modes/languageConfiguration'; +import { IndentAction, EnterAction, IAutoClosingPair, LanguageConfiguration, IndentationRule, FoldingRules } from 'vs/editor/common/modes/languageConfiguration'; import { LanguageIdentifier, LanguageId } from 'vs/editor/common/modes'; /** @@ -55,6 +55,7 @@ export class RichEditSupport { public readonly indentRulesSupport: IndentRulesSupport; public readonly brackets: RichEditBrackets; public readonly indentationRules: IndentationRule; + public readonly foldingRules: FoldingRules; constructor(languageIdentifier: LanguageIdentifier, previous: RichEditSupport, rawConf: LanguageConfiguration) { @@ -82,6 +83,8 @@ export class RichEditSupport { if (this._conf.indentationRules) { this.indentRulesSupport = new IndentRulesSupport(this._conf.indentationRules); } + + this.foldingRules = this._conf.folding || {}; } private static _mergeConf(prev: LanguageConfiguration, current: LanguageConfiguration): LanguageConfiguration { @@ -93,6 +96,7 @@ export class RichEditSupport { onEnterRules: (prev ? current.onEnterRules || prev.onEnterRules : current.onEnterRules), autoClosingPairs: (prev ? current.autoClosingPairs || prev.autoClosingPairs : current.autoClosingPairs), surroundingPairs: (prev ? current.surroundingPairs || prev.surroundingPairs : current.surroundingPairs), + folding: (prev ? current.folding || prev.folding : current.folding), __electricCharacterSupport: (prev ? current.__electricCharacterSupport || prev.__electricCharacterSupport : current.__electricCharacterSupport), }; } @@ -143,12 +147,16 @@ export class RichEditSupport { } } +export class LanguageConfigurationChangeEvent { + languageIdentifier: LanguageIdentifier; +} + export class LanguageConfigurationRegistryImpl { private _entries: RichEditSupport[]; - private _onDidChange: Emitter = new Emitter(); - public onDidChange: Event = this._onDidChange.event; + private _onDidChange: Emitter = new Emitter(); + public onDidChange: Event = this._onDidChange.event; constructor() { this._entries = []; @@ -158,12 +166,12 @@ export class LanguageConfigurationRegistryImpl { let previous = this._getRichEditSupport(languageIdentifier.id); let current = new RichEditSupport(languageIdentifier, previous, configuration); this._entries[languageIdentifier.id] = current; - this._onDidChange.fire(void 0); + this._onDidChange.fire({ languageIdentifier }); return { dispose: () => { if (this._entries[languageIdentifier.id] === current) { this._entries[languageIdentifier.id] = previous; - this._onDidChange.fire(void 0); + this._onDidChange.fire({ languageIdentifier }); } } }; @@ -268,9 +276,15 @@ export class LanguageConfigurationRegistryImpl { return ensureValidWordDefinition(value.wordDefinition || null); } + public getFoldingRules(languageId: LanguageId): FoldingRules { + let value = this._getRichEditSupport(languageId); + if (!value) { + return {}; + } + return value.foldingRules; + } - - // beigin Indent Rules + // begin Indent Rules public getIndentRulesSupport(languageId: LanguageId): IndentRulesSupport { let value = this._getRichEditSupport(languageId); diff --git a/src/vs/editor/common/modes/languageSelector.ts b/src/vs/editor/common/modes/languageSelector.ts index 335e08e8b2e..356f454ecf4 100644 --- a/src/vs/editor/common/modes/languageSelector.ts +++ b/src/vs/editor/common/modes/languageSelector.ts @@ -6,12 +6,12 @@ 'use strict'; import URI from 'vs/base/common/uri'; -import { match as matchGlobPattern } from 'vs/base/common/glob'; // TODO@Alex +import { match as matchGlobPattern, IRelativePattern } from 'vs/base/common/glob'; // TODO@Alex export interface LanguageFilter { language?: string; scheme?: string; - pattern?: string; + pattern?: string | IRelativePattern; } export type LanguageSelector = string | LanguageFilter | (string | LanguageFilter)[]; diff --git a/src/vs/editor/common/view/viewEvents.ts b/src/vs/editor/common/view/viewEvents.ts index 4034473fa40..46244956a30 100644 --- a/src/vs/editor/common/view/viewEvents.ts +++ b/src/vs/editor/common/view/viewEvents.ts @@ -27,7 +27,8 @@ export const enum ViewEventType { ViewTokensChanged = 12, ViewTokensColorsChanged = 13, ViewZonesChanged = 14, - ViewThemeChanged = 15 + ViewThemeChanged = 15, + ViewLanguageConfigurationChanged = 16 } export class ViewConfigurationChangedEvent { @@ -282,6 +283,14 @@ export class ViewZonesChangedEvent { } } +export class ViewLanguageConfigurationEvent { + + public readonly type = ViewEventType.ViewLanguageConfigurationChanged; + + constructor() { + } +} + export type ViewEvent = ( ViewConfigurationChangedEvent | ViewCursorStateChangedEvent @@ -298,6 +307,7 @@ export type ViewEvent = ( | ViewTokensColorsChangedEvent | ViewZonesChangedEvent | ViewThemeChangedEvent + | ViewLanguageConfigurationEvent ); export interface IViewEventListener { diff --git a/src/vs/editor/common/viewModel/viewEventHandler.ts b/src/vs/editor/common/viewModel/viewEventHandler.ts index a3e03e9005d..874b206787e 100644 --- a/src/vs/editor/common/viewModel/viewEventHandler.ts +++ b/src/vs/editor/common/viewModel/viewEventHandler.ts @@ -49,6 +49,9 @@ export class ViewEventHandler extends Disposable { public onFocusChanged(e: viewEvents.ViewFocusChangedEvent): boolean { return false; } + public onLanguageConfigurationChanged(e: viewEvents.ViewLanguageConfigurationEvent): boolean { + return false; + } public onLineMappingChanged(e: viewEvents.ViewLineMappingChangedEvent): boolean { return false; } @@ -121,6 +124,12 @@ export class ViewEventHandler extends Disposable { } break; + case viewEvents.ViewEventType.ViewLanguageConfigurationChanged: + if (this.onLanguageConfigurationChanged(e)) { + shouldRender = true; + } + break; + case viewEvents.ViewEventType.ViewLineMappingChanged: if (this.onLineMappingChanged(e)) { shouldRender = true; @@ -175,7 +184,6 @@ export class ViewEventHandler extends Disposable { } break; - case viewEvents.ViewEventType.ViewThemeChanged: if (this.onThemeChanged(e)) { shouldRender = true; diff --git a/src/vs/editor/common/viewModel/viewModelImpl.ts b/src/vs/editor/common/viewModel/viewModelImpl.ts index 20b51a429b4..f1a7fcbf961 100644 --- a/src/vs/editor/common/viewModel/viewModelImpl.ts +++ b/src/vs/editor/common/viewModel/viewModelImpl.ts @@ -261,6 +261,10 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel // That's ok, a model tokens changed event will follow shortly break; } + case textModelEvents.TextModelEventType.ModelLanguageConfigurationChanged: { + eventsCollector.emit(new viewEvents.ViewLanguageConfigurationEvent()); + break; + } case textModelEvents.TextModelEventType.ModelContentChanged: { // Ignore break; @@ -459,29 +463,37 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel } public getPlainTextToCopy(ranges: Range[], emptySelectionClipboard: boolean): string { - let newLineCharacter = this.model.getEOL(); + const newLineCharacter = this.model.getEOL(); - if (ranges.length === 1) { - let range: Range = ranges[0]; - if (range.isEmpty()) { - if (emptySelectionClipboard) { - let modelLineNumber = this.coordinatesConverter.convertViewPositionToModelPosition(new Position(range.startLineNumber, 1)).lineNumber; - return this.model.getLineContent(modelLineNumber) + newLineCharacter; - } else { - return ''; + ranges = ranges.slice(0); + ranges.sort(Range.compareRangesUsingStarts); + const nonEmptyRanges = ranges.filter((r) => !r.isEmpty()); + + if (nonEmptyRanges.length === 0) { + if (!emptySelectionClipboard) { + return ''; + } + + const modelLineNumbers = ranges.map((r) => { + const viewLineStart = new Position(r.startLineNumber, 1); + return this.coordinatesConverter.convertViewPositionToModelPosition(viewLineStart).lineNumber; + }); + + let result = ''; + for (let i = 0; i < modelLineNumbers.length; i++) { + if (i > 0 && modelLineNumbers[i - 1] === modelLineNumbers[i]) { + continue; } + result += this.model.getLineContent(modelLineNumbers[i]) + newLineCharacter; } - - return this.getValueInRange(range, editorCommon.EndOfLinePreference.TextDefined); - } else { - ranges = ranges.slice(0).sort(Range.compareRangesUsingStarts); - let result: string[] = []; - for (let i = 0; i < ranges.length; i++) { - result.push(this.getValueInRange(ranges[i], editorCommon.EndOfLinePreference.TextDefined)); - } - - return result.join(newLineCharacter); + return result; } + + let result: string[] = []; + for (let i = 0; i < nonEmptyRanges.length; i++) { + result.push(this.getValueInRange(nonEmptyRanges[i], editorCommon.EndOfLinePreference.TextDefined)); + } + return result.join(newLineCharacter); } public getHTMLToCopy(viewRanges: Range[], emptySelectionClipboard: boolean): string { diff --git a/src/vs/editor/contrib/bracketMatching/common/bracketMatching.ts b/src/vs/editor/contrib/bracketMatching/common/bracketMatching.ts index 87cfca45bef..ac3d45aee32 100644 --- a/src/vs/editor/contrib/bracketMatching/common/bracketMatching.ts +++ b/src/vs/editor/contrib/bracketMatching/common/bracketMatching.ts @@ -94,6 +94,10 @@ export class BracketMatchingController extends Disposable implements editorCommo this._updateBracketsSoon.schedule(); })); this._register(editor.onDidChangeModel((e) => { this._decorations = []; this._updateBracketsSoon.schedule(); })); + this._register(editor.onDidChangeModelLanguageConfiguration((e) => { + this._lastBracketsData = []; + this._updateBracketsSoon.schedule(); + })); this._register(editor.onDidChangeConfiguration((e) => { this._matchBrackets = this._editor.getConfiguration().contribInfo.matchBrackets; if (!this._matchBrackets && this._decorations.length > 0) { diff --git a/src/vs/editor/contrib/colorPicker/common/color.ts b/src/vs/editor/contrib/colorPicker/common/color.ts index d85a0375ba3..14309923337 100644 --- a/src/vs/editor/contrib/colorPicker/common/color.ts +++ b/src/vs/editor/contrib/colorPicker/common/color.ts @@ -27,6 +27,6 @@ export function getColors(model: IReadOnlyModel): TPromise { return TPromise.join(promises).then(() => colors); } -export function getColorPresentations(colorInfo: IColorInformation, provider: DocumentColorProvider): TPromise { - return asWinJsPromise(token => provider.provideColorPresentations(colorInfo, token)); +export function getColorPresentations(model: IReadOnlyModel, colorInfo: IColorInformation, provider: DocumentColorProvider): TPromise { + return asWinJsPromise(token => provider.provideColorPresentations(model, colorInfo, token)); } diff --git a/src/vs/editor/contrib/comment/common/blockCommentCommand.ts b/src/vs/editor/contrib/comment/common/blockCommentCommand.ts index ffefd02ed8c..933dff3f46b 100644 --- a/src/vs/editor/contrib/comment/common/blockCommentCommand.ts +++ b/src/vs/editor/contrib/comment/common/blockCommentCommand.ts @@ -26,12 +26,12 @@ export class BlockCommentCommand implements editorCommon.ICommand { if (offset < 0) { return false; } - var needleLength = needle.length; - var haystackLength = haystack.length; + const needleLength = needle.length; + const haystackLength = haystack.length; if (offset + needleLength > haystackLength) { return false; } - for (var i = 0; i < needleLength; i++) { + for (let i = 0; i < needleLength; i++) { if (haystack.charCodeAt(offset + i) !== needle.charCodeAt(i)) { return false; } @@ -40,51 +40,75 @@ export class BlockCommentCommand implements editorCommon.ICommand { } private _createOperationsForBlockComment(selection: Range, config: ICommentsConfiguration, model: editorCommon.ITokenizedModel, builder: editorCommon.IEditOperationBuilder): void { - var startLineNumber = selection.startLineNumber; - var startColumn = selection.startColumn; - var endLineNumber = selection.endLineNumber; - var endColumn = selection.endColumn; + const startLineNumber = selection.startLineNumber; + const startColumn = selection.startColumn; + const endLineNumber = selection.endLineNumber; + const endColumn = selection.endColumn; - var startToken = config.blockCommentStartToken; - var endToken = config.blockCommentEndToken; + const startLineText = model.getLineContent(startLineNumber); + const endLineText = model.getLineContent(endLineNumber); - var startTokenIndex = model.getLineContent(startLineNumber).lastIndexOf(startToken, startColumn - 1 + startToken.length); - var endTokenIndex = model.getLineContent(endLineNumber).indexOf(endToken, endColumn - 1 - endToken.length); + let startToken = config.blockCommentStartToken; + let endToken = config.blockCommentEndToken; - var ops: editorCommon.IIdentifiedSingleEditOperation[]; + let startTokenIndex = startLineText.lastIndexOf(startToken, startColumn - 1 + startToken.length); + let endTokenIndex = endLineText.indexOf(endToken, endColumn - 1 - endToken.length); if (startTokenIndex !== -1 && endTokenIndex !== -1) { - var endTokenBeforeCursorIndex = model.getLineContent(startLineNumber).lastIndexOf(endToken, startColumn - 1 + endToken.length); - if (endTokenBeforeCursorIndex > startTokenIndex + startToken.length - 1) { - ops = BlockCommentCommand._createAddBlockCommentOperations(selection, startToken, endToken); - this._usedEndToken = ops.length === 1 ? endToken : null; - } else { - // We have to adjust to possible inner white space - // For Space after startToken, add Space to startToken - range math will work out - if (model.getLineContent(startLineNumber).charCodeAt(startTokenIndex + startToken.length) === CharCode.Space) { - startToken += ' '; + + if (startLineNumber === endLineNumber) { + const lineBetweenTokens = startLineText.substring(startTokenIndex + startToken.length, endTokenIndex); + + if (lineBetweenTokens.indexOf(endToken) >= 0) { + // force to add a block comment + startTokenIndex = -1; + endTokenIndex = -1; } - // For Space before endToken, add Space before endToken and shift index one left - if (model.getLineContent(endLineNumber).charCodeAt(endTokenIndex - 1) === CharCode.Space) { + } else { + const startLineAfterStartToken = startLineText.substring(startTokenIndex + startToken.length); + const endLineBeforeEndToken = endLineText.substring(0, endTokenIndex); + + if (startLineAfterStartToken.indexOf(endToken) >= 0 || endLineBeforeEndToken.indexOf(endToken) >= 0) { + // force to add a block comment + startTokenIndex = -1; + endTokenIndex = -1; + } + } + } + + let ops: editorCommon.IIdentifiedSingleEditOperation[]; + + if (startTokenIndex !== -1 && endTokenIndex !== -1) { + // Consider spaces as part of the comment tokens + if (startTokenIndex + startToken.length < startLineText.length) { + if (startLineText.charCodeAt(startTokenIndex + startToken.length) === CharCode.Space) { + // Pretend the start token contains a trailing space + startToken = startToken + ' '; + } + } + + if (endTokenIndex > 0) { + if (endLineText.charCodeAt(endTokenIndex - 1) === CharCode.Space) { + // Pretend the end token contains a leading space endToken = ' ' + endToken; endTokenIndex -= 1; } - ops = BlockCommentCommand._createRemoveBlockCommentOperations( - new Range(startLineNumber, startTokenIndex + 1 + startToken.length, endLineNumber, endTokenIndex + 1), startToken, endToken - ); } + ops = BlockCommentCommand._createRemoveBlockCommentOperations( + new Range(startLineNumber, startTokenIndex + startToken.length + 1, endLineNumber, endTokenIndex + 1), startToken, endToken + ); } else { ops = BlockCommentCommand._createAddBlockCommentOperations(selection, startToken, endToken); this._usedEndToken = ops.length === 1 ? endToken : null; } - for (var i = 0; i < ops.length; i++) { + for (let i = 0; i < ops.length; i++) { builder.addTrackedEditOperation(ops[i].range, ops[i].text); } } public static _createRemoveBlockCommentOperations(r: Range, startToken: string, endToken: string): editorCommon.IIdentifiedSingleEditOperation[] { - var res: editorCommon.IIdentifiedSingleEditOperation[] = []; + let res: editorCommon.IIdentifiedSingleEditOperation[] = []; if (!Range.isEmpty(r)) { // Remove block comment start @@ -110,7 +134,7 @@ export class BlockCommentCommand implements editorCommon.ICommand { } public static _createAddBlockCommentOperations(r: Range, startToken: string, endToken: string): editorCommon.IIdentifiedSingleEditOperation[] { - var res: editorCommon.IIdentifiedSingleEditOperation[] = []; + let res: editorCommon.IIdentifiedSingleEditOperation[] = []; if (!Range.isEmpty(r)) { // Insert block comment start @@ -130,29 +154,27 @@ export class BlockCommentCommand implements editorCommon.ICommand { } public getEditOperations(model: editorCommon.ITokenizedModel, builder: editorCommon.IEditOperationBuilder): void { - var startLineNumber = this._selection.startLineNumber; - var startColumn = this._selection.startColumn; - var endLineNumber = this._selection.endLineNumber; - var endColumn = this._selection.endColumn; + const startLineNumber = this._selection.startLineNumber; + const startColumn = this._selection.startColumn; model.tokenizeIfCheap(startLineNumber); - let languageId = model.getLanguageIdAtPosition(startLineNumber, startColumn); - let config = LanguageConfigurationRegistry.getComments(languageId); + const languageId = model.getLanguageIdAtPosition(startLineNumber, startColumn); + const config = LanguageConfigurationRegistry.getComments(languageId); if (!config || !config.blockCommentStartToken || !config.blockCommentEndToken) { // Mode does not support block comments return; } this._createOperationsForBlockComment( - new Range(startLineNumber, startColumn, endLineNumber, endColumn), config, model, builder + this._selection, config, model, builder ); } public computeCursorState(model: editorCommon.ITokenizedModel, helper: editorCommon.ICursorStateComputerData): Selection { - var inverseEditOperations = helper.getInverseEditOperations(); + const inverseEditOperations = helper.getInverseEditOperations(); if (inverseEditOperations.length === 2) { - var startTokenEditOperation = inverseEditOperations[0]; - var endTokenEditOperation = inverseEditOperations[1]; + const startTokenEditOperation = inverseEditOperations[0]; + const endTokenEditOperation = inverseEditOperations[1]; return new Selection( startTokenEditOperation.range.endLineNumber, @@ -161,8 +183,8 @@ export class BlockCommentCommand implements editorCommon.ICommand { endTokenEditOperation.range.startColumn ); } else { - var srcRange = inverseEditOperations[0].range; - var deltaColumn = this._usedEndToken ? -this._usedEndToken.length - 1 : 0; // minus 1 space before endToken + const srcRange = inverseEditOperations[0].range; + const deltaColumn = this._usedEndToken ? -this._usedEndToken.length - 1 : 0; // minus 1 space before endToken return new Selection( srcRange.endLineNumber, srcRange.endColumn + deltaColumn, diff --git a/src/vs/editor/contrib/comment/test/common/blockCommentCommand.test.ts b/src/vs/editor/contrib/comment/test/common/blockCommentCommand.test.ts index e204aa44255..4b5ea60286d 100644 --- a/src/vs/editor/contrib/comment/test/common/blockCommentCommand.test.ts +++ b/src/vs/editor/contrib/comment/test/common/blockCommentCommand.test.ts @@ -457,4 +457,17 @@ suite('Editor Contrib - Block Comment Command', () => { new Selection(1, 16, 1, 22) ); }); + + test('issue #34618', function () { + testBlockCommentCommand( + [ + '<0 0> middle end', + ], + new Selection(1, 4, 1, 4), + [ + ' middle end' + ], + new Selection(1, 1, 1, 1) + ); + }); }); diff --git a/src/vs/editor/contrib/folding/browser/folding.ts b/src/vs/editor/contrib/folding/browser/folding.ts index bbe359dd428..d6eba543ba4 100644 --- a/src/vs/editor/contrib/folding/browser/folding.ts +++ b/src/vs/editor/contrib/folding/browser/folding.ts @@ -237,6 +237,9 @@ export class FoldingController implements IFoldingController { this.localToDispose.push(this.contentChangedScheduler); this.localToDispose.push(this.cursorChangedScheduler); + this.localToDispose.push(model.onDidChangeLanguageConfiguration(e => { + this.contentChangedScheduler.schedule(); + })); this.localToDispose.push(this.editor.onDidChangeModelContent(e => this.contentChangedScheduler.schedule())); this.localToDispose.push(this.editor.onDidChangeCursorPosition((e) => { diff --git a/src/vs/editor/contrib/hover/browser/modesContentHover.ts b/src/vs/editor/contrib/hover/browser/modesContentHover.ts index 0e0ea6df661..bb0601afb80 100644 --- a/src/vs/editor/contrib/hover/browser/modesContentHover.ts +++ b/src/vs/editor/contrib/hover/browser/modesContentHover.ts @@ -325,7 +325,7 @@ export class ModesContentHoverWidget extends ContentHoverWidget { const model = new ColorPickerModel(color, [], 0); const widget = new ColorPickerWidget(fragment, model, this._editor.getConfiguration().pixelRatio); - getColorPresentations(colorInfo, msg.provider).then(colorPresentations => { + getColorPresentations(editorModel, colorInfo, msg.provider).then(colorPresentations => { model.colorPresentations = colorPresentations; const originalText = this._editor.getModel().getValueInRange(msg.range); model.guessColorPresentation(color, originalText); @@ -335,7 +335,6 @@ export class ModesContentHoverWidget extends ContentHoverWidget { let newRange; if (model.presentation.textEdit) { textEdits = [model.presentation.textEdit]; - console.log('insert text'); newRange = new Range( model.presentation.textEdit.range.startLineNumber, model.presentation.textEdit.range.startColumn, @@ -360,7 +359,7 @@ export class ModesContentHoverWidget extends ContentHoverWidget { }; const updateColorPresentations = (color: Color) => { - return getColorPresentations({ + return getColorPresentations(editorModel, { range: range, color: { red: color.rgba.r / 255, diff --git a/src/vs/editor/contrib/suggest/browser/suggest.ts b/src/vs/editor/contrib/suggest/browser/suggest.ts index 3831064a10c..aea81522ddd 100644 --- a/src/vs/editor/contrib/suggest/browser/suggest.ts +++ b/src/vs/editor/contrib/suggest/browser/suggest.ts @@ -12,7 +12,7 @@ import { onUnexpectedExternalError } from 'vs/base/common/errors'; import { TPromise } from 'vs/base/common/winjs.base'; import { IModel, IEditorContribution, ICommonCodeEditor } from 'vs/editor/common/editorCommon'; import { CommonEditorRegistry } from 'vs/editor/common/editorCommonExtensions'; -import { ISuggestResult, ISuggestSupport, ISuggestion, SuggestRegistry } from 'vs/editor/common/modes'; +import { ISuggestResult, ISuggestSupport, ISuggestion, SuggestRegistry, SuggestContext, SuggestTriggerKind } from 'vs/editor/common/modes'; import { Position, IPosition } from 'vs/editor/common/core/position'; import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; @@ -42,7 +42,7 @@ export function setSnippetSuggestSupport(support: ISuggestSupport): ISuggestSupp return old; } -export function provideSuggestionItems(model: IModel, position: Position, snippetConfig: SnippetConfig = 'bottom', onlyFrom?: ISuggestSupport[]): TPromise { +export function provideSuggestionItems(model: IModel, position: Position, snippetConfig: SnippetConfig = 'bottom', onlyFrom?: ISuggestSupport[], context?: SuggestContext): TPromise { const allSuggestions: ISuggestionItem[] = []; const acceptSuggestion = createSuggesionFilter(snippetConfig); @@ -57,6 +57,8 @@ export function provideSuggestionItems(model: IModel, position: Position, snippe supports.unshift([_snippetSuggestSupport]); } + const suggestConext = context || { triggerKind: SuggestTriggerKind.Invoke }; + // add suggestions from contributed providers - providers are ordered in groups of // equal score and once a group produces a result the process stops let hasResult = false; @@ -73,7 +75,7 @@ export function provideSuggestionItems(model: IModel, position: Position, snippe return undefined; } - return asWinJsPromise(token => support.provideCompletionItems(model, position, token)).then(container => { + return asWinJsPromise(token => support.provideCompletionItems(model, position, suggestConext, token)).then(container => { const len = allSuggestions.length; diff --git a/src/vs/editor/contrib/suggest/browser/suggestController.ts b/src/vs/editor/contrib/suggest/browser/suggestController.ts index 37a5ad50ff3..6f7fb7e9add 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestController.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestController.ts @@ -209,7 +209,7 @@ export class SuggestController implements IEditorContribution { } triggerSuggest(onlyFrom?: ISuggestSupport[]): void { - this._model.trigger(false, false, onlyFrom); + this._model.trigger({ auto: false }, false, onlyFrom); this._editor.revealLine(this._editor.getPosition().lineNumber, ScrollType.Smooth); this._editor.focus(); } diff --git a/src/vs/editor/contrib/suggest/browser/suggestModel.ts b/src/vs/editor/contrib/suggest/browser/suggestModel.ts index 69b20642956..f74fda9bbed 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestModel.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestModel.ts @@ -11,7 +11,7 @@ import Event, { Emitter } from 'vs/base/common/event'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { TPromise } from 'vs/base/common/winjs.base'; import { ICommonCodeEditor, IModel, IWordAtPosition } from 'vs/editor/common/editorCommon'; -import { ISuggestSupport, SuggestRegistry, StandardTokenType } from 'vs/editor/common/modes'; +import { ISuggestSupport, SuggestRegistry, StandardTokenType, SuggestTriggerKind } from 'vs/editor/common/modes'; import { Position } from 'vs/editor/common/core/position'; import { provideSuggestionItems, getSuggestionComparator, ISuggestionItem } from './suggest'; import { CompletionModel } from './completionModel'; @@ -31,6 +31,11 @@ export interface ISuggestEvent { auto: boolean; } +export interface SuggestTriggerContext { + auto: boolean; + triggerCharacter?: string; +} + export class LineContext { static shouldAutoTrigger(editor: ICommonCodeEditor): boolean { @@ -201,7 +206,7 @@ export class SuggestModel implements IDisposable { } } } - this.trigger(true, Boolean(this.completionModel), supports, items); + this.trigger({ auto: true, triggerCharacter: lastChar }, Boolean(this.completionModel), supports, items); } }); } @@ -237,7 +242,7 @@ export class SuggestModel implements IDisposable { if (!SuggestRegistry.has(this.editor.getModel())) { this.cancel(); } else { - this.trigger(this._state === State.Auto, true); + this.trigger({ auto: this._state === State.Auto }, true); } } } @@ -296,11 +301,11 @@ export class SuggestModel implements IDisposable { } else if (quickSuggestions === true) { // all good } else { + // Check the type of the token that triggered this model.tokenizeIfCheap(pos.lineNumber); const { tokenType } = model .getLineTokens(pos.lineNumber) - .findTokenAtOffset(pos.column - 1); - + .findTokenAtOffset(Math.max(pos.column - 1 - 1, 0)); const inValidScope = quickSuggestions.other && tokenType === StandardTokenType.Other || quickSuggestions.comments && tokenType === StandardTokenType.Comment || quickSuggestions.strings && tokenType === StandardTokenType.String; @@ -311,7 +316,7 @@ export class SuggestModel implements IDisposable { } this.triggerAutoSuggestPromise = null; - this.trigger(true); + this.trigger({ auto: true }); }); } } @@ -326,14 +331,14 @@ export class SuggestModel implements IDisposable { } } - public trigger(auto: boolean, retrigger: boolean = false, onlyFrom?: ISuggestSupport[], existingItems?: ISuggestionItem[]): void { + public trigger(context: SuggestTriggerContext, retrigger: boolean = false, onlyFrom?: ISuggestSupport[], existingItems?: ISuggestionItem[]): void { const model = this.editor.getModel(); if (!model) { return; } - + const auto = context.auto; const ctx = new LineContext(model, this.editor.getPosition(), auto); if (!LineContext.isInEditableRange(this.editor)) { @@ -350,7 +355,11 @@ export class SuggestModel implements IDisposable { this.requestPromise = provideSuggestionItems(model, this.editor.getPosition(), this.editor.getConfiguration().contribInfo.snippetSuggestions, - onlyFrom + onlyFrom, + { + triggerCharacter: context.triggerCharacter, + triggerKind: context.triggerCharacter ? SuggestTriggerKind.TriggerCharacter : SuggestTriggerKind.Invoke + } ).then(items => { this.requestPromise = null; @@ -394,7 +403,7 @@ export class SuggestModel implements IDisposable { if (ctx.column < this.context.column) { // typed -> moved cursor LEFT -> retrigger if still on a word if (ctx.leadingWord.word) { - this.trigger(this.context.auto, true); + this.trigger({ auto: this.context.auto }, true); } else { this.cancel(); } @@ -409,7 +418,7 @@ export class SuggestModel implements IDisposable { if (ctx.column > this.context.column && this.completionModel.incomplete && ctx.leadingWord.word.length !== 0) { // typed -> moved cursor RIGHT & incomple model & still on a word -> retrigger const { complete, incomplete } = this.completionModel.resolveIncompleteInfo(); - this.trigger(this._state === State.Auto, true, incomplete, complete); + this.trigger({ auto: this._state === State.Auto }, true, incomplete, complete); } else { // typed -> moved cursor RIGHT -> update UI @@ -425,7 +434,7 @@ export class SuggestModel implements IDisposable { if (LineContext.shouldAutoTrigger(this.editor) && this.context.leadingWord.endColumn < ctx.leadingWord.startColumn) { // retrigger when heading into a new word - this.trigger(this.context.auto, true); + this.trigger({ auto: this.context.auto }, true); return; } diff --git a/src/vs/editor/contrib/suggest/test/browser/suggestModel.test.ts b/src/vs/editor/contrib/suggest/test/browser/suggestModel.test.ts index 6bba3555808..0964ab132cc 100644 --- a/src/vs/editor/contrib/suggest/test/browser/suggestModel.test.ts +++ b/src/vs/editor/contrib/suggest/test/browser/suggestModel.test.ts @@ -11,7 +11,7 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { TPromise } from 'vs/base/common/winjs.base'; import { Model } from 'vs/editor/common/model/model'; import { ICommonCodeEditor, Handler } from 'vs/editor/common/editorCommon'; -import { ISuggestSupport, ISuggestResult, SuggestRegistry } from 'vs/editor/common/modes'; +import { ISuggestSupport, ISuggestResult, SuggestRegistry, SuggestTriggerKind } from 'vs/editor/common/modes'; import { SuggestModel, LineContext } from 'vs/editor/contrib/suggest/browser/suggestModel'; import { MockCodeEditor, MockScopeLocation } from 'vs/editor/test/common/mocks/mockCodeEditor'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; @@ -149,25 +149,25 @@ suite('SuggestModel - TriggerAndCancelOracle', function () { // cancel on trigger assertEvent(model.onDidCancel, function () { - model.trigger(false); + model.trigger({ auto: false }); }, function (event) { assert.equal(event.retrigger, false); }), assertEvent(model.onDidCancel, function () { - model.trigger(false, true); + model.trigger({ auto: false }, true); }, function (event) { assert.equal(event.retrigger, true); }), assertEvent(model.onDidTrigger, function () { - model.trigger(true); + model.trigger({ auto: true }); }, function (event) { assert.equal(event.auto, true); }), assertEvent(model.onDidTrigger, function () { - model.trigger(false); + model.trigger({ auto: false }); }, function (event) { assert.equal(event.auto, false); }) @@ -183,12 +183,12 @@ suite('SuggestModel - TriggerAndCancelOracle', function () { return withOracle(model => { return TPromise.join([ assertEvent(model.onDidCancel, function () { - model.trigger(true); + model.trigger({ auto: true }); }, function (event) { assert.equal(event.retrigger, false); }), assertEvent(model.onDidSuggest, function () { - model.trigger(false); + model.trigger({ auto: false }); }, function (event) { assert.equal(event.auto, false); assert.equal(event.isFrozen, false); @@ -239,7 +239,7 @@ suite('SuggestModel - TriggerAndCancelOracle', function () { return assertEvent(model.onDidSuggest, () => { // make sure completionModel starts here! - model.trigger(true); + model.trigger({ auto: true }); }, event => { return assertEvent(model.onDidSuggest, () => { @@ -338,7 +338,7 @@ suite('SuggestModel - TriggerAndCancelOracle', function () { editor.setPosition({ lineNumber: 1, column: 3 }); return assertEvent(model.onDidSuggest, () => { - model.trigger(false); + model.trigger({ auto: false }); }, event => { assert.equal(event.auto, false); assert.equal(event.isFrozen, false); @@ -363,7 +363,7 @@ suite('SuggestModel - TriggerAndCancelOracle', function () { editor.setPosition({ lineNumber: 1, column: 3 }); return assertEvent(model.onDidSuggest, () => { - model.trigger(false); + model.trigger({ auto: false }); }, event => { assert.equal(event.auto, false); assert.equal(event.isFrozen, false); @@ -400,7 +400,7 @@ suite('SuggestModel - TriggerAndCancelOracle', function () { editor.setPosition({ lineNumber: 1, column: 4 }); return assertEvent(model.onDidSuggest, () => { - model.trigger(false); + model.trigger({ auto: false }); }, event => { assert.equal(event.auto, false); assert.equal(event.completionModel.incomplete, true); @@ -437,7 +437,7 @@ suite('SuggestModel - TriggerAndCancelOracle', function () { editor.setPosition({ lineNumber: 1, column: 4 }); return assertEvent(model.onDidSuggest, () => { - model.trigger(false); + model.trigger({ auto: false }); }, event => { assert.equal(event.auto, false); assert.equal(event.completionModel.incomplete, true); @@ -457,4 +457,39 @@ suite('SuggestModel - TriggerAndCancelOracle', function () { }); }); }); + + test('Trigger character is provided in suggest context', function () { + let triggerCharacter = ''; + disposables.push(SuggestRegistry.register({ scheme: 'test' }, { + triggerCharacters: ['.'], + provideCompletionItems(doc, pos, context) { + assert.equal(context.triggerKind, SuggestTriggerKind.TriggerCharacter); + triggerCharacter = context.triggerCharacter; + return { + currentWord: '', + incomplete: false, + suggestions: [ + { + label: 'foo.bar', + type: 'property', + insertText: 'foo.bar', + overwriteBefore: pos.column - 1 + } + ] + }; + } + })); + + model.setValue(''); + + return withOracle((model, editor) => { + + return assertEvent(model.onDidSuggest, () => { + editor.setPosition({ lineNumber: 1, column: 1 }); + editor.trigger('keyboard', Handler.Type, { text: 'foo.' }); + }, event => { + assert.equal(triggerCharacter, '.'); + }); + }); + }); }); diff --git a/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.ts b/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.ts index 2cd8cab8f8c..573f8f08a9e 100644 --- a/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.ts +++ b/src/vs/editor/contrib/wordHighlighter/common/wordHighlighter.ts @@ -116,6 +116,17 @@ class WordHighlighter { this.renderDecorationsTimer = -1; } + public hasDecorations(): boolean { + return (this._decorationIds.length > 0); + } + + public restore(): void { + if (!this.occurrencesHighlight) { + return; + } + this._run(); + } + private _removeDecorations(): void { if (this._decorationIds.length > 0) { // remove decorations @@ -162,6 +173,10 @@ class WordHighlighter { return; } + this._run(); + } + + private _run(): void { // no providers for this model if (!DocumentHighlightProviderRegistry.has(this.model)) { this._stopAll(); @@ -344,6 +359,19 @@ class WordHighlighterContribution implements editorCommon.IEditorContribution { return WordHighlighterContribution.ID; } + public saveViewState(): boolean { + if (this.wordHighligher.hasDecorations()) { + return true; + } + return false; + } + + public restoreViewState(state: boolean | undefined): void { + if (state) { + this.wordHighligher.restore(); + } + } + public dispose(): void { this.wordHighligher.dispose(); } diff --git a/src/vs/editor/standalone/browser/simpleServices.ts b/src/vs/editor/standalone/browser/simpleServices.ts index 3eea14eb51a..bad64ca55ae 100644 --- a/src/vs/editor/standalone/browser/simpleServices.ts +++ b/src/vs/editor/standalone/browser/simpleServices.ts @@ -18,7 +18,7 @@ import { KeybindingResolver } from 'vs/platform/keybinding/common/keybindingReso import { IKeybindingEvent, KeybindingSource, IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding'; import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IConfirmation, IMessageService } from 'vs/platform/message/common/message'; -import { IWorkspaceContextService, IWorkspace, WorkbenchState, WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, IWorkspace, WorkbenchState, IWorkspaceFolder, IWorkspaceFoldersChangeEvent, WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import * as editorCommon from 'vs/editor/common/editorCommon'; import { ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser'; import { Selection } from 'vs/editor/common/core/selection'; @@ -525,8 +525,8 @@ export class SimpleWorkspaceContextService implements IWorkspaceContextService { private readonly _onDidChangeWorkspaceName: Emitter = new Emitter(); public readonly onDidChangeWorkspaceName: Event = this._onDidChangeWorkspaceName.event; - private readonly _onDidChangeWorkspaceFolders: Emitter = new Emitter(); - public readonly onDidChangeWorkspaceFolders: Event = this._onDidChangeWorkspaceFolders.event; + private readonly _onDidChangeWorkspaceFolders: Emitter = new Emitter(); + public readonly onDidChangeWorkspaceFolders: Event = this._onDidChangeWorkspaceFolders.event; private readonly _onDidChangeWorkbenchState: Emitter = new Emitter(); public readonly onDidChangeWorkbenchState: Event = this._onDidChangeWorkbenchState.event; @@ -535,7 +535,7 @@ export class SimpleWorkspaceContextService implements IWorkspaceContextService { constructor() { const resource = URI.from({ scheme: SimpleWorkspaceContextService.SCHEME, authority: 'model', path: '/' }); - this.workspace = { id: '4064f6ec-cb38-4ad0-af64-ee6467e63c82', folders: [{ uri: resource, raw: { path: resource.toString() }, name: '', index: 0, }], name: resource.fsPath }; + this.workspace = { id: '4064f6ec-cb38-4ad0-af64-ee6467e63c82', folders: [new WorkspaceFolder({ uri: resource, name: '', index: 0 })], name: resource.fsPath }; } public getWorkspace(): IWorkspace { @@ -552,7 +552,7 @@ export class SimpleWorkspaceContextService implements IWorkspaceContextService { return WorkbenchState.EMPTY; } - public getWorkspaceFolder(resource: URI): WorkspaceFolder { + public getWorkspaceFolder(resource: URI): IWorkspaceFolder { return resource && resource.scheme === SimpleWorkspaceContextService.SCHEME ? this.workspace.folders[0] : void 0; } @@ -560,7 +560,7 @@ export class SimpleWorkspaceContextService implements IWorkspaceContextService { return resource && resource.scheme === SimpleWorkspaceContextService.SCHEME; } - public toResource(workspaceRelativePath: string, workspaceFolder: WorkspaceFolder): URI { + public toResource(workspaceRelativePath: string, workspaceFolder: IWorkspaceFolder): URI { return URI.file(workspaceRelativePath); } diff --git a/src/vs/editor/standalone/browser/standaloneLanguages.ts b/src/vs/editor/standalone/browser/standaloneLanguages.ts index d97e850f41c..78fe0aa3d37 100644 --- a/src/vs/editor/standalone/browser/standaloneLanguages.ts +++ b/src/vs/editor/standalone/browser/standaloneLanguages.ts @@ -373,8 +373,8 @@ export function registerCompletionItemProvider(languageId: string, provider: Com let adapter = new SuggestAdapter(provider); return modes.SuggestRegistry.register(languageId, { triggerCharacters: provider.triggerCharacters, - provideCompletionItems: (model: editorCommon.IReadOnlyModel, position: Position, token: CancellationToken): Thenable => { - return adapter.provideCompletionItems(model, position, token); + provideCompletionItems: (model: editorCommon.IReadOnlyModel, position: Position, context: modes.SuggestContext, token: CancellationToken): Thenable => { + return adapter.provideCompletionItems(model, position, context, token); }, resolveCompletionItem: (model: editorCommon.IReadOnlyModel, position: Position, suggestion: modes.ISuggestion, token: CancellationToken): Thenable => { return adapter.resolveCompletionItem(model, position, suggestion, token); @@ -537,6 +537,25 @@ export interface CompletionList { */ items: CompletionItem[]; } + +/** + * Contains additional information about the context in which + * [completion provider](#CompletionItemProvider.provideCompletionItems) is triggered. + */ +export interface CompletionContext { + /** + * How the completion was triggered. + */ + triggerKind: modes.SuggestTriggerKind; + + /** + * Character that triggered the completion item provider. + * + * `undefined` if provider was not triggered by a character. + */ + triggerCharacter?: string; +} + /** * The completion item provider interface defines the contract between extensions and * the [IntelliSense](https://code.visualstudio.com/docs/editor/intellisense). @@ -553,7 +572,8 @@ export interface CompletionItemProvider { /** * Provide completion items for the given position and document. */ - provideCompletionItems(model: editorCommon.IReadOnlyModel, position: Position, token: CancellationToken): CompletionItem[] | Thenable | CompletionList | Thenable; + provideCompletionItems(document: editorCommon.IReadOnlyModel, position: Position, token: CancellationToken, context: CompletionContext): CompletionItem[] | Thenable | CompletionList | Thenable; + /** * Given a completion item fill in more data, like [doc-comment](#CompletionItem.documentation) * or [details](#CompletionItem.detail). @@ -590,6 +610,7 @@ function convertKind(kind: CompletionItemKind): modes.SuggestionType { } return 'property'; } + class SuggestAdapter { private _provider: CompletionItemProvider; @@ -639,9 +660,9 @@ class SuggestAdapter { return suggestion; } - provideCompletionItems(model: editorCommon.IReadOnlyModel, position: Position, token: CancellationToken): Thenable { - - return toThenable(this._provider.provideCompletionItems(model, position, token)).then(value => { + provideCompletionItems(model: editorCommon.IReadOnlyModel, position: Position, context: modes.SuggestContext, token: CancellationToken): Thenable { + const result = this._provider.provideCompletionItems(model, position, token, context); + return toThenable(result).then(value => { const result: modes.ISuggestResult = { suggestions: [] }; @@ -738,6 +759,7 @@ export function createMonacoLanguagesAPI(): typeof monaco.languages { DocumentHighlightKind: modes.DocumentHighlightKind, CompletionItemKind: CompletionItemKind, SymbolKind: modes.SymbolKind, - IndentAction: IndentAction + IndentAction: IndentAction, + SuggestTriggerKind: modes.SuggestTriggerKind }; } diff --git a/src/vs/editor/test/common/model/indentRanges.test.ts b/src/vs/editor/test/common/model/indentRanges.test.ts index 55b5e1ad6dd..0a022e43d02 100644 --- a/src/vs/editor/test/common/model/indentRanges.test.ts +++ b/src/vs/editor/test/common/model/indentRanges.test.ts @@ -7,116 +7,266 @@ import * as assert from 'assert'; import { Model } from 'vs/editor/common/model/model'; -import { computeRanges } from 'vs/editor/common/model/indentRanges'; +import { computeRanges, FoldMarkers } from 'vs/editor/common/model/indentRanges'; export interface IndentRange { startLineNumber: number; endLineNumber: number; indent: number; + marker: boolean; } -suite('Indentation Folding', () => { - function assertRanges(lines: string[], expected: IndentRange[]): void { - let model = Model.createFromString(lines.join('\n')); - let actual = computeRanges(model); - actual.sort((r1, r2) => r1.startLineNumber - r2.startLineNumber); - assert.deepEqual(actual, expected); - model.dispose(); - } +function assertRanges(lines: string[], expected: IndentRange[], offside: boolean, markers?: FoldMarkers): void { + let model = Model.createFromString(lines.join('\n')); + let actual = computeRanges(model, offside, markers); + actual.sort((r1, r2) => r1.startLineNumber - r2.startLineNumber); + assert.deepEqual(actual, expected); + model.dispose(); +} - function r(startLineNumber: number, endLineNumber: number, indent: number): IndentRange { - return { startLineNumber, endLineNumber, indent }; - } +function r(startLineNumber: number, endLineNumber: number, indent: number, marker?: boolean): IndentRange { + return { startLineNumber, endLineNumber, indent, marker }; +} - test('Fold one level', () => { - assertRanges([ - 'A', - ' A', - ' A', - ' A' - ], [r(1, 4, 0)]); - }); +// suite('Indentation Folding', () => { - test('Fold two levels', () => { - assertRanges([ - 'A', - ' A', - ' A', - ' A', - ' A' - ], [r(1, 5, 0), r(3, 5, 2)]); - }); +// test('Fold one level', () => { +// let range = [ +// 'A', +// ' A', +// ' A', +// ' A' +// ]; +// assertRanges(range, [r(1, 4, 0)], true); +// assertRanges(range, [r(1, 4, 0)], false); +// }); - test('Fold three levels', () => { - assertRanges([ - 'A', - ' A', - ' A', - ' A', - 'A' - ], [r(1, 4, 0), r(2, 4, 2), r(3, 4, 4)]); - }); +// test('Fold two levels', () => { +// let range = [ +// 'A', +// ' A', +// ' A', +// ' A', +// ' A' +// ]; +// assertRanges(range, [r(1, 5, 0), r(3, 5, 2)], true); +// assertRanges(range, [r(1, 5, 0), r(3, 5, 2)], false); +// }); - test('Fold decreasing indent', () => { - assertRanges([ - ' A', - ' A', - 'A' - ], []); - }); +// test('Fold three levels', () => { +// let range = [ +// 'A', +// ' A', +// ' A', +// ' A', +// 'A' +// ]; +// assertRanges(range, [r(1, 4, 0), r(2, 4, 2), r(3, 4, 4)], true); +// assertRanges(range, [r(1, 4, 0), r(2, 4, 2), r(3, 4, 4)], false); +// }); - test('Fold Java', () => { +// test('Fold decreasing indent', () => { +// let range = [ +// ' A', +// ' A', +// 'A' +// ]; +// assertRanges(range, [], true); +// assertRanges(range, [], false); +// }); + +// test('Fold Java', () => { +// assertRanges([ +// /* 1*/ 'class A {', +// /* 2*/ ' void foo() {', +// /* 3*/ ' console.log();', +// /* 4*/ ' console.log();', +// /* 5*/ ' }', +// /* 6*/ '', +// /* 7*/ ' void bar() {', +// /* 8*/ ' console.log();', +// /* 9*/ ' }', +// /*10*/ '}', +// /*11*/ 'interface B {', +// /*12*/ ' void bar();', +// /*13*/ '}', +// ], [r(1, 9, 0), r(2, 4, 2), r(7, 8, 2), r(11, 12, 0)], false); +// }); + +// test('Fold Javadoc', () => { +// assertRanges([ +// /* 1*/ '/**', +// /* 2*/ ' * Comment', +// /* 3*/ ' */', +// /* 4*/ 'class A {', +// /* 5*/ ' void foo() {', +// /* 6*/ ' }', +// /* 7*/ '}', +// ], [r(1, 3, 0), r(4, 6, 0)], false); +// }); +// test('Fold Whitespace Java', () => { +// assertRanges([ +// /* 1*/ 'class A {', +// /* 2*/ '', +// /* 3*/ ' void foo() {', +// /* 4*/ ' ', +// /* 5*/ ' return 0;', +// /* 6*/ ' }', +// /* 7*/ ' ', +// /* 8*/ '}', +// ], [r(1, 7, 0), r(3, 5, 2)], false); +// }); + +// test('Fold Whitespace Python', () => { +// assertRanges([ +// /* 1*/ 'def a:', +// /* 2*/ ' pass', +// /* 3*/ ' ', +// /* 4*/ ' def b:', +// /* 5*/ ' pass', +// /* 6*/ ' ', +// /* 7*/ ' ', +// /* 8*/ 'def c: # since there was a deintent here' +// ], [r(1, 5, 0), r(4, 5, 2)], true); +// }); + +// test('Fold Tabs', () => { +// assertRanges([ +// /* 1*/ 'class A {', +// /* 2*/ '\t\t', +// /* 3*/ '\tvoid foo() {', +// /* 4*/ '\t \t//hello', +// /* 5*/ '\t return 0;', +// /* 6*/ ' \t}', +// /* 7*/ ' ', +// /* 8*/ '}', +// ], [r(1, 7, 0), r(3, 5, 4)], false); +// }); +// }); + +let foldPattern: FoldMarkers = { + start: '^\\s*#region', + end: '^\\s*#endregion' +}; + +suite('Folding with regions', () => { + test('Inside region, indented', () => { assertRanges([ /* 1*/ 'class A {', - /* 2*/ ' void foo() {', - /* 3*/ ' console.log();', - /* 4*/ ' console.log();', - /* 5*/ ' }', - /* 6*/ '', - /* 7*/ ' void bar() {', - /* 8*/ ' console.log();', - /* 9*/ ' }', - /*10*/ '}', - /*11*/ 'interface B {', - /*12*/ ' void bar();', - /*13*/ '}', - ], [r(1, 9, 0), r(2, 4, 2), r(7, 8, 2), r(11, 12, 0)]); - }); - - test('Fold Javadoc', () => { - assertRanges([ - /* 1*/ '/**', - /* 2*/ ' * Comment', - /* 3*/ ' */', - /* 4*/ 'class A {', - /* 5*/ ' void foo() {', - /* 6*/ ' }', - /* 7*/ '}', - ], [r(1, 3, 0), r(4, 6, 0)]); - }); - test('Fold Whitespace', () => { - assertRanges([ - /* 1*/ 'class A {', - /* 2*/ '', + /* 2*/ ' #region', /* 3*/ ' void foo() {', /* 4*/ ' ', /* 5*/ ' return 0;', /* 6*/ ' }', - /* 7*/ ' ', + /* 7*/ ' #endregion', /* 8*/ '}', - ], [r(1, 7, 0), r(3, 5, 2)]); + ], [r(1, 7, 0), r(2, 7, 2, true), r(3, 5, 2)], false, foldPattern); }); - - test('Fold Tabs', () => { + test('Inside region, not indented', () => { + assertRanges([ + /* 1*/ 'var x;', + /* 2*/ '#region', + /* 3*/ 'void foo() {', + /* 4*/ ' ', + /* 5*/ ' return 0;', + /* 6*/ ' }', + /* 7*/ '#endregion', + /* 8*/ '', + ], [r(2, 7, 0, true), r(3, 6, 0)], false, foldPattern); + }); + test('Empty Regions', () => { + assertRanges([ + /* 1*/ 'var x;', + /* 2*/ '#region', + /* 3*/ '#endregion', + /* 4*/ '#region', + /* 5*/ '', + /* 6*/ '#endregion', + /* 7*/ 'var y;', + ], [r(2, 3, 0, true), r(4, 6, 0, true)], false, foldPattern); + }); + test('Nested Regions', () => { + assertRanges([ + /* 1*/ 'var x;', + /* 2*/ '#region', + /* 3*/ '#region', + /* 4*/ '', + /* 5*/ '#endregion', + /* 6*/ '#endregion', + /* 7*/ 'var y;', + ], [r(2, 6, 0, true), r(3, 5, 0, true)], false, foldPattern); + }); + test('Nested Regions 2', () => { assertRanges([ /* 1*/ 'class A {', - /* 2*/ '\t\t', - /* 3*/ '\tvoid foo() {', - /* 4*/ '\t \t//hello', - /* 5*/ '\t return 0;', - /* 6*/ ' \t}', - /* 7*/ ' ', - /* 8*/ '}', - ], [r(1, 7, 0), r(3, 5, 4)]); + /* 2*/ ' #region', + /* 3*/ '', + /* 4*/ ' #region', + /* 5*/ '', + /* 6*/ ' #endregion', + /* 7*/ ' // comment', + /* 8*/ ' #endregion', + /* 9*/ '}', + ], [r(1, 8, 0), r(2, 8, 2, true), r(4, 6, 2, true)], false, foldPattern); }); -}); + test('Incomplete Regions', () => { + assertRanges([ + /* 1*/ 'class A {', + /* 2*/ '#region', + /* 3*/ ' // comment', + /* 4*/ '}', + ], [], false, foldPattern); + }); + test('Incomplete Regions', () => { + assertRanges([ + /* 1*/ '', + /* 2*/ '#region', + /* 3*/ '#region', + /* 4*/ '#region', + /* 5*/ ' // comment', + /* 6*/ '#endregion', + /* 7*/ '#endregion', + /* 8*/ ' // hello', + ], [r(3, 7, 0, true), r(4, 6, 0, true)], false, foldPattern); + }); + test('Indented region before', () => { + assertRanges([ + /* 1*/ 'if (x)', + /* 2*/ ' return;', + /* 3*/ '', + /* 4*/ '#region', + /* 5*/ ' // comment', + /* 6*/ '#endregion', + ], [r(1, 3, 0), r(4, 6, 0, true)], false, foldPattern); + }); + test('Indented region before 2', () => { + assertRanges([ + /* 1*/ 'if (x)', + /* 2*/ ' log();', + /* 3*/ '', + /* 4*/ ' #region', + /* 5*/ ' // comment', + /* 6*/ ' #endregion', + ], [r(1, 6, 0), r(2, 6, 2), r(4, 6, 4, true)], false, foldPattern); + }); + test('Indented region in-between', () => { + assertRanges([ + /* 1*/ '#region', + /* 2*/ ' // comment', + /* 3*/ ' if (x)', + /* 4*/ ' return;', + /* 5*/ '', + /* 6*/ '#endregion', + ], [r(1, 6, 0, true), r(3, 5, 2)], false, foldPattern); + }); + test('Indented region after', () => { + assertRanges([ + /* 1*/ '#region', + /* 2*/ ' // comment', + /* 3*/ '', + /* 4*/ '#endregion', + /* 5*/ ' if (x)', + /* 6*/ ' return;', + ], [r(1, 4, 0, true), r(5, 6, 2)], false, foldPattern); + }); +}); \ No newline at end of file diff --git a/src/vs/editor/test/common/model/textModel.test.ts b/src/vs/editor/test/common/model/textModel.test.ts index cba2a056eba..e36e11308e5 100644 --- a/src/vs/editor/test/common/model/textModel.test.ts +++ b/src/vs/editor/test/common/model/textModel.test.ts @@ -820,178 +820,4 @@ suite('TextModel.mightContainRTL', () => { assert.equal(model.mightContainRTL(), false); }); -}); - -suite('TextModel.getLineIndentGuide', () => { - function assertIndentGuides(lines: [number, string][]): void { - let text = lines.map(l => l[1]).join('\n'); - let model = TextModel.createFromString(text); - - let actual: [number, string][] = []; - for (let line = 1; line <= model.getLineCount(); line++) { - actual[line - 1] = [model.getLineIndentGuide(line), model.getLineContent(line)]; - } - - // let expected = lines.map(l => l[0]); - - assert.deepEqual(actual, lines); - - model.dispose(); - } - - test('getLineIndentGuide one level', () => { - assertIndentGuides([ - [0, 'A'], - [1, ' A'], - [1, ' A'], - [1, ' A'], - ]); - }); - - test('getLineIndentGuide two levels', () => { - assertIndentGuides([ - [0, 'A'], - [1, ' A'], - [1, ' A'], - [1, ' A'], - [1, ' A'], - ]); - }); - - test('getLineIndentGuide three levels', () => { - assertIndentGuides([ - [0, 'A'], - [1, ' A'], - [1, ' A'], - [2, ' A'], - [0, 'A'], - ]); - }); - - test('getLineIndentGuide decreasing indent', () => { - assertIndentGuides([ - [0, ' A'], - [0, ' A'], - [0, 'A'], - ]); - }); - - test('getLineIndentGuide Java', () => { - assertIndentGuides([ - /* 1*/[0, 'class A {'], - /* 2*/[1, ' void foo() {'], - /* 3*/[1, ' console.log(1);'], - /* 4*/[1, ' console.log(2);'], - /* 5*/[1, ' }'], - /* 6*/[1, ''], - /* 7*/[1, ' void bar() {'], - /* 8*/[1, ' console.log(3);'], - /* 9*/[1, ' }'], - /*10*/[0, '}'], - /*11*/[0, 'interface B {'], - /*12*/[1, ' void bar();'], - /*13*/[0, '}'], - ]); - }); - - test('getLineIndentGuide Javadoc', () => { - assertIndentGuides([ - [0, '/**'], - [1, ' * Comment'], - [1, ' */'], - [0, 'class A {'], - [1, ' void foo() {'], - [1, ' }'], - [0, '}'], - ]); - }); - - test('getLineIndentGuide Whitespace', () => { - assertIndentGuides([ - [0, 'class A {'], - [1, ''], - [1, ' void foo() {'], - [1, ' '], - [1, ' return 1;'], - [1, ' }'], - [1, ' '], - [0, '}'], - ]); - }); - - test('getLineIndentGuide Tabs', () => { - assertIndentGuides([ - [0, 'class A {'], - [1, '\t\t'], - [1, '\tvoid foo() {'], - [2, '\t \t//hello'], - [2, '\t return 2;'], - [1, ' \t}'], - [1, ' '], - [0, '}'], - ]); - }); - - test('getLineIndentGuide checker.ts', () => { - assertIndentGuides([ - /* 1*/[0, '/// '], - /* 2*/[0, ''], - /* 3*/[0, '/* @internal */'], - /* 4*/[0, 'namespace ts {'], - /* 5*/[1, ' let nextSymbolId = 1;'], - /* 6*/[1, ' let nextNodeId = 1;'], - /* 7*/[1, ' let nextMergeId = 1;'], - /* 8*/[1, ' let nextFlowId = 1;'], - /* 9*/[1, ''], - /*10*/[1, ' export function getNodeId(node: Node): number {'], - /*11*/[2, ' if (!node.id) {'], - /*12*/[3, ' node.id = nextNodeId;'], - /*13*/[3, ' nextNodeId++;'], - /*14*/[2, ' }'], - /*15*/[2, ' return node.id;'], - /*16*/[1, ' }'], - /*17*/[0, '}'], - ]); - }); - - test('issue #8425 - Missing indentation lines for first level indentation', () => { - assertIndentGuides([ - [1, '\tindent1'], - [2, '\t\tindent2'], - [2, '\t\tindent2'], - [1, '\tindent1'], - ]); - }); - - test('issue #8952 - Indentation guide lines going through text on .yml file', () => { - assertIndentGuides([ - [0, 'properties:'], - [1, ' emailAddress:'], - [2, ' - bla'], - [2, ' - length:'], - [3, ' max: 255'], - [0, 'getters:'], - ]); - }); - - test('issue #11892 - Indent guides look funny', () => { - assertIndentGuides([ - [0, 'function test(base) {'], - [1, '\tswitch (base) {'], - [2, '\t\tcase 1:'], - [3, '\t\t\treturn 1;'], - [2, '\t\tcase 2:'], - [3, '\t\t\treturn 2;'], - [1, '\t}'], - [0, '}'], - ]); - }); - - test('issue #12398 - Problem in indent guidelines', () => { - assertIndentGuides([ - [2, '\t\t.bla'], - [3, '\t\t\tlabel(for)'], - [0, 'include script'], - ]); - }); -}); +}); \ No newline at end of file diff --git a/src/vs/editor/test/common/model/textModelWithTokens.test.ts b/src/vs/editor/test/common/model/textModelWithTokens.test.ts index 6ac512096dd..24c66e08947 100644 --- a/src/vs/editor/test/common/model/textModelWithTokens.test.ts +++ b/src/vs/editor/test/common/model/textModelWithTokens.test.ts @@ -359,3 +359,177 @@ suite('TextModelWithTokens regression tests', () => { registration.dispose(); }); }); + +suite('TextModel.getLineIndentGuide', () => { + function assertIndentGuides(lines: [number, string][]): void { + let text = lines.map(l => l[1]).join('\n'); + let model = Model.createFromString(text); + + let actual: [number, string][] = []; + for (let line = 1; line <= model.getLineCount(); line++) { + actual[line - 1] = [model.getLineIndentGuide(line), model.getLineContent(line)]; + } + + // let expected = lines.map(l => l[0]); + + assert.deepEqual(actual, lines); + + model.dispose(); + } + + test('getLineIndentGuide one level', () => { + assertIndentGuides([ + [0, 'A'], + [1, ' A'], + [1, ' A'], + [1, ' A'], + ]); + }); + + test('getLineIndentGuide two levels', () => { + assertIndentGuides([ + [0, 'A'], + [1, ' A'], + [1, ' A'], + [1, ' A'], + [1, ' A'], + ]); + }); + + test('getLineIndentGuide three levels', () => { + assertIndentGuides([ + [0, 'A'], + [1, ' A'], + [1, ' A'], + [2, ' A'], + [0, 'A'], + ]); + }); + + test('getLineIndentGuide decreasing indent', () => { + assertIndentGuides([ + [0, ' A'], + [0, ' A'], + [0, 'A'], + ]); + }); + + test('getLineIndentGuide Java', () => { + assertIndentGuides([ + /* 1*/[0, 'class A {'], + /* 2*/[1, ' void foo() {'], + /* 3*/[1, ' console.log(1);'], + /* 4*/[1, ' console.log(2);'], + /* 5*/[1, ' }'], + /* 6*/[1, ''], + /* 7*/[1, ' void bar() {'], + /* 8*/[1, ' console.log(3);'], + /* 9*/[1, ' }'], + /*10*/[0, '}'], + /*11*/[0, 'interface B {'], + /*12*/[1, ' void bar();'], + /*13*/[0, '}'], + ]); + }); + + test('getLineIndentGuide Javadoc', () => { + assertIndentGuides([ + [0, '/**'], + [1, ' * Comment'], + [1, ' */'], + [0, 'class A {'], + [1, ' void foo() {'], + [1, ' }'], + [0, '}'], + ]); + }); + + test('getLineIndentGuide Whitespace', () => { + assertIndentGuides([ + [0, 'class A {'], + [1, ''], + [1, ' void foo() {'], + [1, ' '], + [1, ' return 1;'], + [1, ' }'], + [1, ' '], + [0, '}'], + ]); + }); + + test('getLineIndentGuide Tabs', () => { + assertIndentGuides([ + [0, 'class A {'], + [1, '\t\t'], + [1, '\tvoid foo() {'], + [2, '\t \t//hello'], + [2, '\t return 2;'], + [1, ' \t}'], + [1, ' '], + [0, '}'], + ]); + }); + + test('getLineIndentGuide checker.ts', () => { + assertIndentGuides([ + /* 1*/[0, '/// '], + /* 2*/[0, ''], + /* 3*/[0, '/* @internal */'], + /* 4*/[0, 'namespace ts {'], + /* 5*/[1, ' let nextSymbolId = 1;'], + /* 6*/[1, ' let nextNodeId = 1;'], + /* 7*/[1, ' let nextMergeId = 1;'], + /* 8*/[1, ' let nextFlowId = 1;'], + /* 9*/[1, ''], + /*10*/[1, ' export function getNodeId(node: Node): number {'], + /*11*/[2, ' if (!node.id) {'], + /*12*/[3, ' node.id = nextNodeId;'], + /*13*/[3, ' nextNodeId++;'], + /*14*/[2, ' }'], + /*15*/[2, ' return node.id;'], + /*16*/[1, ' }'], + /*17*/[0, '}'], + ]); + }); + + test('issue #8425 - Missing indentation lines for first level indentation', () => { + assertIndentGuides([ + [1, '\tindent1'], + [2, '\t\tindent2'], + [2, '\t\tindent2'], + [1, '\tindent1'], + ]); + }); + + test('issue #8952 - Indentation guide lines going through text on .yml file', () => { + assertIndentGuides([ + [0, 'properties:'], + [1, ' emailAddress:'], + [2, ' - bla'], + [2, ' - length:'], + [3, ' max: 255'], + [0, 'getters:'], + ]); + }); + + test('issue #11892 - Indent guides look funny', () => { + assertIndentGuides([ + [0, 'function test(base) {'], + [1, '\tswitch (base) {'], + [2, '\t\tcase 1:'], + [3, '\t\t\treturn 1;'], + [2, '\t\tcase 2:'], + [3, '\t\t\treturn 2;'], + [1, '\t}'], + [0, '}'], + ]); + }); + + test('issue #12398 - Problem in indent guidelines', () => { + assertIndentGuides([ + [2, '\t\t.bla'], + [3, '\t\t\tlabel(for)'], + [0, 'include script'], + ]); + }); +}); diff --git a/src/vs/editor/test/common/viewModel/viewModelImpl.test.ts b/src/vs/editor/test/common/viewModel/viewModelImpl.test.ts index 40c071085cd..665f07f72ef 100644 --- a/src/vs/editor/test/common/viewModel/viewModelImpl.test.ts +++ b/src/vs/editor/test/common/viewModel/viewModelImpl.test.ts @@ -41,4 +41,148 @@ suite('ViewModel', () => { assert.equal(viewModel.getLineCount(), 10); }); }); + + function assertGetPlainTextToCopy(text: string[], ranges: Range[], emptySelectionClipboard: boolean, expected: string): void { + testViewModel(text, {}, (viewModel, model) => { + let actual = viewModel.getPlainTextToCopy(ranges, emptySelectionClipboard); + assert.equal(actual, expected); + }); + } + + const USUAL_TEXT = [ + '', + 'line2', + 'line3', + 'line4', + '' + ]; + + test('getPlainTextToCopy 0/1', () => { + assertGetPlainTextToCopy( + USUAL_TEXT, + [ + new Range(2, 2, 2, 2) + ], + false, + '' + ); + }); + + test('getPlainTextToCopy 0/1 - emptySelectionClipboard', () => { + assertGetPlainTextToCopy( + USUAL_TEXT, + [ + new Range(2, 2, 2, 2) + ], + true, + 'line2\n' + ); + }); + + test('getPlainTextToCopy 1/1', () => { + assertGetPlainTextToCopy( + USUAL_TEXT, + [ + new Range(2, 2, 2, 6) + ], + false, + 'ine2' + ); + }); + + test('getPlainTextToCopy 1/1 - emptySelectionClipboard', () => { + assertGetPlainTextToCopy( + USUAL_TEXT, + [ + new Range(2, 2, 2, 6) + ], + true, + 'ine2' + ); + }); + + test('getPlainTextToCopy 0/2', () => { + assertGetPlainTextToCopy( + USUAL_TEXT, + [ + new Range(2, 2, 2, 2), + new Range(3, 2, 3, 2), + ], + false, + '' + ); + }); + + test('getPlainTextToCopy 0/2 - emptySelectionClipboard', () => { + assertGetPlainTextToCopy( + USUAL_TEXT, + [ + new Range(2, 2, 2, 2), + new Range(3, 2, 3, 2), + ], + true, + 'line2\nline3\n' + ); + }); + + test('getPlainTextToCopy 1/2', () => { + assertGetPlainTextToCopy( + USUAL_TEXT, + [ + new Range(2, 2, 2, 6), + new Range(3, 2, 3, 2), + ], + false, + 'ine2' + ); + }); + + test('getPlainTextToCopy 1/2 - emptySelectionClipboard', () => { + assertGetPlainTextToCopy( + USUAL_TEXT, + [ + new Range(2, 2, 2, 6), + new Range(3, 2, 3, 2), + ], + true, + 'ine2' + ); + }); + + test('getPlainTextToCopy 2/2', () => { + assertGetPlainTextToCopy( + USUAL_TEXT, + [ + new Range(2, 2, 2, 6), + new Range(3, 2, 3, 6), + ], + false, + 'ine2\nine3' + ); + }); + + test('getPlainTextToCopy 2/2 reversed', () => { + assertGetPlainTextToCopy( + USUAL_TEXT, + [ + new Range(3, 2, 3, 6), + new Range(2, 2, 2, 6), + ], + false, + 'ine2\nine3' + ); + }); + + test('getPlainTextToCopy 0/3 - emptySelectionClipboard', () => { + assertGetPlainTextToCopy( + USUAL_TEXT, + [ + new Range(2, 2, 2, 2), + new Range(2, 3, 2, 3), + new Range(3, 2, 3, 2), + ], + true, + 'line2\nline3\n' + ); + }); }); diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index e1b0f728c19..bff19722704 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -1787,6 +1787,11 @@ declare module monaco.editor { * @event */ onDidChangeLanguage(listener: (e: IModelLanguageChangedEvent) => void): IDisposable; + /** + * An event emitted when the language configuration associated with the model has changed. + * @event + */ + onDidChangeLanguageConfiguration(listener: (e: IModelLanguageConfigurationChangedEvent) => void): IDisposable; /** * An event emitted right before disposing the model. * @event @@ -2185,6 +2190,11 @@ declare module monaco.editor { * @event */ onDidChangeModelLanguage(listener: (e: IModelLanguageChangedEvent) => void): IDisposable; + /** + * An event emitted when the language configuration of the current model has changed. + * @event + */ + onDidChangeModelLanguageConfiguration(listener: (e: IModelLanguageConfigurationChangedEvent) => void): IDisposable; /** * An event emitted when the options of the current model has changed. * @event @@ -2419,6 +2429,12 @@ declare module monaco.editor { readonly newLanguage: string; } + /** + * An event describing that the language configuration associated with a model has changed. + */ + export interface IModelLanguageConfigurationChangedEvent { + } + export interface IModelContentChange { /** * The range that got replaced. @@ -4213,6 +4229,23 @@ declare module monaco.languages { items: CompletionItem[]; } + /** + * Contains additional information about the context in which + * [completion provider](#CompletionItemProvider.provideCompletionItems) is triggered. + */ + export interface CompletionContext { + /** + * How the completion was triggered. + */ + triggerKind: SuggestTriggerKind; + /** + * Character that triggered the completion item provider. + * + * `undefined` if provider was not triggered by a character. + */ + triggerCharacter?: string; + } + /** * The completion item provider interface defines the contract between extensions and * the [IntelliSense](https://code.visualstudio.com/docs/editor/intellisense). @@ -4229,7 +4262,7 @@ declare module monaco.languages { /** * Provide completion items for the given position and document. */ - provideCompletionItems(model: editor.IReadOnlyModel, position: Position, token: CancellationToken): CompletionItem[] | Thenable | CompletionList | Thenable; + provideCompletionItems(document: editor.IReadOnlyModel, position: Position, token: CancellationToken, context: CompletionContext): CompletionItem[] | Thenable | CompletionList | Thenable; /** * Given a completion item fill in more data, like [doc-comment](#CompletionItem.documentation) * or [details](#CompletionItem.detail). @@ -4294,6 +4327,10 @@ declare module monaco.languages { * settings will be used. */ surroundingPairs?: IAutoClosingPair[]; + /** + * The language's folding rules. + */ + folding?: FoldingRules; /** * **Deprecated** Do not use. * @@ -4324,6 +4361,19 @@ declare module monaco.languages { unIndentedLinePattern?: RegExp; } + /** + * Describes folding rules for a language. + */ + export interface FoldingRules { + /** + * Used by the indentation based strategy to decide wheter empty lines belong to the previous or the next block. + * A language adheres to the off-side rule if blocks in that language are expressed by their indentation. + * See [wikipedia](https://en.wikipedia.org/wiki/Off-side_rule) for more information. + * If not set, `false` is used and empty lines belong to the previous block. + */ + offSide?: boolean; + } + /** * Describes a rule to be evaluated when pressing Enter. */ @@ -4461,6 +4511,14 @@ declare module monaco.languages { provideHover(model: editor.IReadOnlyModel, position: Position, token: CancellationToken): Hover | Thenable; } + /** + * How a suggest provider was triggered. + */ + export enum SuggestTriggerKind { + Invoke = 0, + TriggerCharacter = 1, + } + /** * Represents a parameter of a callable-signature. A parameter can * have a label and a doc-comment. @@ -4869,7 +4927,7 @@ declare module monaco.languages { /** * Provide the string representations for a color. */ - provideColorPresentations(colorInfo: IColorInformation, token: CancellationToken): IColorPresentation[] | Thenable; + provideColorPresentations(model: editor.IReadOnlyModel, colorInfo: IColorInformation, token: CancellationToken): IColorPresentation[] | Thenable; } export interface IResourceEdit { diff --git a/src/vs/platform/configuration/common/configurationRegistry.ts b/src/vs/platform/configuration/common/configurationRegistry.ts index d2495cce311..0d3276bf69d 100644 --- a/src/vs/platform/configuration/common/configurationRegistry.ts +++ b/src/vs/platform/configuration/common/configurationRegistry.ts @@ -11,6 +11,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import types = require('vs/base/common/types'); import * as strings from 'vs/base/common/strings'; import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; +import { clone } from 'vs/base/common/objects'; export const Extensions = { Configuration: 'base.contributions.configuration' @@ -61,6 +62,7 @@ export interface IConfigurationPropertySchema extends IJSONSchema { overridable?: boolean; isExecutable?: boolean; scope?: ConfigurationScope; + isFromExtensions?: boolean; } export interface IConfigurationNode { @@ -81,33 +83,28 @@ export interface IDefaultConfigurationExtension { defaults: { [key: string]: {} }; } -export const schemaId = 'vscode://schemas/settings'; +export const settingsSchema: IJSONSchema = { properties: {}, patternProperties: {}, additionalProperties: false, errorMessage: 'Unknown configuration setting' }; +export const resourceSettingsSchema: IJSONSchema = { properties: {}, patternProperties: {}, additionalProperties: false, errorMessage: 'Unknown configuration setting' }; + export const editorConfigurationSchemaId = 'vscode://schemas/settings/editor'; -export const resourceConfigurationSchemaId = 'vscode://schemas/settings/resource'; const contributionRegistry = Registry.as(JSONExtensions.JSONContribution); class ConfigurationRegistry implements IConfigurationRegistry { private configurationContributors: IConfigurationNode[]; private configurationProperties: { [qualifiedKey: string]: IJSONSchema }; - private configurationSchema: IJSONSchema; private editorConfigurationSchema: IJSONSchema; - private resourceConfigurationSchema: IJSONSchema; private _onDidRegisterConfiguration: Emitter; private overrideIdentifiers: string[] = []; private overridePropertyPattern: string; constructor() { this.configurationContributors = []; - this.configurationSchema = { properties: {}, patternProperties: {}, additionalProperties: false, errorMessage: 'Unknown configuration setting' }; this.editorConfigurationSchema = { properties: {}, patternProperties: {}, additionalProperties: false, errorMessage: 'Unknown editor configuration setting' }; - this.resourceConfigurationSchema = { properties: {}, patternProperties: {}, additionalProperties: false, errorMessage: 'Not a resource configuration setting' }; this._onDidRegisterConfiguration = new Emitter(); this.configurationProperties = {}; this.computeOverridePropertyPattern(); - contributionRegistry.registerSchema(schemaId, this.configurationSchema); contributionRegistry.registerSchema(editorConfigurationSchemaId, this.editorConfigurationSchema); - contributionRegistry.registerSchema(resourceConfigurationSchemaId, this.resourceConfigurationSchema); } public get onDidRegisterConfiguration() { @@ -208,12 +205,15 @@ class ConfigurationRegistry implements IConfigurationRegistry { } private registerJSONConfiguration(configuration: IConfigurationNode) { - let configurationSchema = this.configurationSchema; function register(configuration: IConfigurationNode) { let properties = configuration.properties; if (properties) { for (let key in properties) { - configurationSchema.properties[key] = properties[key]; + settingsSchema.properties[key] = properties[key]; + resourceSettingsSchema.properties[key] = clone(properties[key]); + if (properties[key].scope !== ConfigurationScope.RESOURCE) { + resourceSettingsSchema.properties[key].doNotSuggest = true; + } } } let subNodes = configuration.allOf; @@ -222,19 +222,17 @@ class ConfigurationRegistry implements IConfigurationRegistry { } }; register(configuration); - contributionRegistry.registerSchema(schemaId, configurationSchema); } private updateSchemaForOverrideSettingsConfiguration(configuration: IConfigurationNode): void { if (configuration.id !== SETTINGS_OVERRRIDE_NODE_ID) { this.update(configuration); contributionRegistry.registerSchema(editorConfigurationSchemaId, this.editorConfigurationSchema); - contributionRegistry.registerSchema(resourceConfigurationSchemaId, this.resourceConfigurationSchema); } } private updateOverridePropertyPatternKey(): void { - let patternProperties: IJSONSchema = this.configurationSchema.patternProperties[this.overridePropertyPattern]; + let patternProperties: IJSONSchema = settingsSchema.patternProperties[this.overridePropertyPattern]; if (!patternProperties) { patternProperties = { type: 'object', @@ -243,10 +241,11 @@ class ConfigurationRegistry implements IConfigurationRegistry { $ref: editorConfigurationSchemaId }; } - delete this.configurationSchema.patternProperties[this.overridePropertyPattern]; + delete settingsSchema.patternProperties[this.overridePropertyPattern]; this.computeOverridePropertyPattern(); - this.configurationSchema.patternProperties[this.overridePropertyPattern] = patternProperties; - contributionRegistry.registerSchema(schemaId, this.configurationSchema); + + settingsSchema.patternProperties[this.overridePropertyPattern] = patternProperties; + resourceSettingsSchema.patternProperties[this.overridePropertyPattern] = patternProperties; } private update(configuration: IConfigurationNode): void { @@ -256,11 +255,6 @@ class ConfigurationRegistry implements IConfigurationRegistry { if (properties[key].overridable) { this.editorConfigurationSchema.properties[key] = this.getConfigurationProperties()[key]; } - switch (properties[key].scope) { - case ConfigurationScope.RESOURCE: - this.resourceConfigurationSchema.properties[key] = this.getConfigurationProperties()[key]; - break; - } } } let subNodes = configuration.allOf; diff --git a/src/vs/platform/configuration/test/common/testConfigurationService.ts b/src/vs/platform/configuration/test/common/testConfigurationService.ts index e7f948d824c..976cb5840ad 100644 --- a/src/vs/platform/configuration/test/common/testConfigurationService.ts +++ b/src/vs/platform/configuration/test/common/testConfigurationService.ts @@ -5,7 +5,7 @@ 'use strict'; -import { TrieMap } from 'vs/base/common/map'; +import { StringTrieMap } from 'vs/base/common/map'; import URI from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; import { EventEmitter } from 'vs/base/common/eventEmitter'; @@ -17,7 +17,7 @@ export class TestConfigurationService extends EventEmitter implements IConfigura private configuration = Object.create(null); - private configurationByRoot: TrieMap = new TrieMap(); + private configurationByRoot: StringTrieMap = new StringTrieMap(); public reloadConfiguration(section?: string): TPromise { return TPromise.as(this.getConfiguration()); diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts index 0741cac9892..e236455b8d0 100644 --- a/src/vs/platform/environment/common/environment.ts +++ b/src/vs/platform/environment/common/environment.ts @@ -42,6 +42,7 @@ export interface ParsedArgs { 'skip-getting-started'?: boolean; 'sticky-quickopen'?: boolean; 'export-default-configuration'?: string; + 'install-source'?: string; } export const IEnvironmentService = createDecorator('environmentService'); @@ -102,4 +103,6 @@ export interface IEnvironmentService { sharedIPCHandle: string; nodeCachedDataDir: string; + + installSource: string; } diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index 1bcf77bddbe..7b2b9c660c1 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -27,7 +27,8 @@ const options: minimist.Opts = { 'debugBrkSearch', 'open-url', 'enable-proposed-api', - 'export-default-configuration' + 'export-default-configuration', + 'install-source' ], boolean: [ 'help', diff --git a/src/vs/platform/environment/node/environmentService.ts b/src/vs/platform/environment/node/environmentService.ts index 01222188e6a..f073e0fce56 100644 --- a/src/vs/platform/environment/node/environmentService.ts +++ b/src/vs/platform/environment/node/environmentService.ts @@ -52,6 +52,10 @@ function getIPCHandle(userDataPath: string, type: string): string { } } +export function getInstallSourcePath(userDataPath: string): string { + return path.join(userDataPath, 'installSource'); +} + export class EnvironmentService implements IEnvironmentService { _serviceBrand: any; @@ -139,10 +143,12 @@ export class EnvironmentService implements IEnvironmentService { get sharedIPCHandle(): string { return getIPCHandle(this.userDataPath, 'shared'); } @memoize - get nodeCachedDataDir(): string { return this.isBuilt ? path.join(this.userDataPath, 'CachedData', product.commit) : undefined; } + get nodeCachedDataDir(): string { return this.isBuilt ? path.join(this.userDataPath, 'CachedData', product.commit || new Array(41).join('0')) : undefined; } readonly machineUUID: string; + readonly installSource: string; + constructor(private _args: ParsedArgs, private _execPath: string) { const machineIdPath = path.join(this.userDataPath, 'machineid'); @@ -161,6 +167,12 @@ export class EnvironmentService implements IEnvironmentService { console.warn('Could not store machine ID'); } } + + try { + this.installSource = fs.readFileSync(getInstallSourcePath(this.userDataPath), 'utf8').slice(0, 30); + } catch (err) { + this.installSource = ''; + } } } diff --git a/src/vs/platform/extensionManagement/common/extensionManagement.ts b/src/vs/platform/extensionManagement/common/extensionManagement.ts index 24d82c47bb5..df42ecd966c 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagement.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagement.ts @@ -228,17 +228,23 @@ export interface InstallExtensionEvent { gallery?: IGalleryExtension; } +export enum ErrorCode { + OBSOLETE = 1, + GALLERY, + LOCAL +} + export interface DidInstallExtensionEvent { id: string; zipPath?: string; gallery?: IGalleryExtension; local?: ILocalExtension; - error?: Error; + error?: ErrorCode; } export interface DidUninstallExtensionEvent { id: string; - error?: Error; + error?: ErrorCode; } export interface IExtensionManagementService { diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index 24f88da7d2a..ae321626480 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -18,7 +18,8 @@ import { IExtensionManagementService, IExtensionGalleryService, ILocalExtension, IGalleryExtension, IExtensionManifest, IGalleryMetadata, InstallExtensionEvent, DidInstallExtensionEvent, DidUninstallExtensionEvent, LocalExtensionType, - StatisticType + StatisticType, + ErrorCode } from 'vs/platform/extensionManagement/common/extensionManagement'; import { getLocalExtensionIdFromGallery, getLocalExtensionIdFromManifest, getGalleryExtensionIdFromLocal, getIdAndVersionFromLocalExtensionId, adoptToGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { localizeManifest } from '../common/extensionNls'; @@ -68,6 +69,12 @@ function readManifest(extensionPath: string): TPromise<{ manifest: IExtensionMan }); } +interface InstallableExtension { + zipPath: string; + id: string; + metadata: IGalleryMetadata; +} + export class ExtensionManagementService implements IExtensionManagementService { _serviceBrand: any; @@ -107,12 +114,12 @@ export class ExtensionManagementService implements IExtensionManagementService { return this.isObsolete(id).then(isObsolete => { if (isObsolete) { - return TPromise.wrapError(new Error(nls.localize('restartCode', "Please restart Code before reinstalling {0}.", manifest.displayName || manifest.name))); + return TPromise.wrapError(new Error(nls.localize('restartCodeLocal', "Please restart Code before reinstalling {0}.", manifest.displayName || manifest.name))); } this._onInstallExtension.fire({ id, zipPath }); - return this.installExtension(zipPath, id) + return this.installExtension({ zipPath, id, metadata: null }) .then( local => this._onDidInstallExtension.fire({ id, zipPath, local }), error => { this._onDidInstallExtension.fire({ id, zipPath, error }); return TPromise.wrapError(error); } @@ -122,91 +129,83 @@ export class ExtensionManagementService implements IExtensionManagementService { } installFromGallery(extension: IGalleryExtension): TPromise { - const id = getLocalExtensionIdFromGallery(extension, extension.version); + return this.prepareAndCollectExtensionsToInstall(extension) + .then(extensionsToInstall => this.downloadAndInstallExtensions(extensionsToInstall) + .then(local => this.onDidInstallExtensions(extensionsToInstall, local))); + } - return this.isObsolete(id).then(isObsolete => { - if (isObsolete) { - return TPromise.wrapError(new Error(nls.localize('restartCode', "Please restart Code before reinstalling {0}.", extension.displayName || extension.name))); - } - this._onInstallExtension.fire({ id, gallery: extension }); - return this.installCompatibleVersion(extension, true) + private prepareAndCollectExtensionsToInstall(extension: IGalleryExtension): TPromise { + this.onInstallExtensions([extension]); + return this.collectExtensionsToInstall(extension) + .then( + extensionsToInstall => this.checkForObsolete(extensionsToInstall) .then( - local => this._onDidInstallExtension.fire({ id, local, gallery: extension }), - error => { - this._onDidInstallExtension.fire({ id, gallery: extension, error }); - return TPromise.wrapError(error); - } - ); - }); - } - - private installCompatibleVersion(extension: IGalleryExtension, installDependencies: boolean): TPromise { - return this.galleryService.loadCompatibleVersion(extension) - .then(compatibleVersion => this.getDependenciesToInstall(extension, installDependencies) - .then(dependencies => dependencies.length ? this.installWithDependencies(compatibleVersion) : this.downloadAndInstall(compatibleVersion))); - } - - private getDependenciesToInstall(extension: IGalleryExtension, checkDependecies: boolean): TPromise { - if (!checkDependecies) { - return TPromise.wrap([]); - } - // Filter out self - const dependencies = extension.properties.dependencies ? extension.properties.dependencies.filter(id => id !== extension.id) : []; - if (!dependencies.length) { - return TPromise.wrap([]); - } - // Filter out installed dependencies - return this.getInstalled().then(installed => { - return dependencies.filter(dep => installed.every(i => `${i.manifest.publisher}.${i.manifest.name}` !== dep)); - }); - } - - private installWithDependencies(extension: IGalleryExtension): TPromise { - return this.galleryService.getAllDependencies(extension) - .then(allDependencies => this.filterDependenciesToInstall(extension, allDependencies)) - .then(toInstall => this.filterObsolete(...toInstall.map(i => getLocalExtensionIdFromGallery(i, i.version))) - .then((obsolete) => { - if (obsolete.length) { - return TPromise.wrapError(new Error(nls.localize('restartCode', "Please restart Code before reinstalling {0}.", extension.displayName || extension.name))); + extensionsToInstall => { + if (extensionsToInstall.length > 1) { + this.onInstallExtensions(extensionsToInstall.slice(1)); } - return this.bulkInstallWithDependencies(extension, toInstall); - }) + return extensionsToInstall; + }, + error => this.onDidInstallExtensions([extension], null, ErrorCode.OBSOLETE, error) + ), + error => this.onDidInstallExtensions([extension], null, ErrorCode.GALLERY, error) ); } - private bulkInstallWithDependencies(extension: IGalleryExtension, dependecies: IGalleryExtension[]): TPromise { - for (const dependency of dependecies) { - const id = getLocalExtensionIdFromGallery(dependency, dependency.version); - this._onInstallExtension.fire({ id, gallery: dependency }); - } - return this.downloadAndInstall(extension) - .then(localExtension => { - return TPromise.join(dependecies.map((dep) => this.installCompatibleVersion(dep, false))) - .then(installedLocalExtensions => { - for (const installedLocalExtension of installedLocalExtensions) { - const gallery = this.getGalleryExtensionForLocalExtension(dependecies, installedLocalExtension); - this._onDidInstallExtension.fire({ id: installedLocalExtension.id, local: installedLocalExtension, gallery }); - } - return localExtension; - }, error => { - return this.rollback(localExtension, dependecies).then(() => { - return TPromise.wrapError(Array.isArray(error) ? error[error.length - 1] : error); - }); - }); - }) - .then(localExtension => localExtension, error => { - for (const dependency of dependecies) { - this._onDidInstallExtension.fire({ id: getLocalExtensionIdFromGallery(dependency, dependency.version), gallery: dependency, error }); - } - return TPromise.wrapError(error); - }); + private downloadAndInstallExtensions(extensions: IGalleryExtension[]): TPromise { + return TPromise.join(extensions.map(extensionToInstall => this.downloadInstallableExtension(extensionToInstall))) + .then( + installableExtensions => TPromise.join(installableExtensions.map(installableExtension => this.installExtension(installableExtension))) + .then(null, error => this.rollback(extensions).then(() => this.onDidInstallExtensions(extensions, null, ErrorCode.LOCAL, error))), + error => this.onDidInstallExtensions(extensions, null, ErrorCode.GALLERY, error)); } - private rollback(localExtension: ILocalExtension, dependecies: IGalleryExtension[]): TPromise { - return this.doUninstall(localExtension) - .then(() => this.filterOutUninstalled(dependecies)) - .then(installed => TPromise.join(installed.map((i) => this.doUninstall(i)))) - .then(() => null); + private collectExtensionsToInstall(extension: IGalleryExtension): TPromise { + return this.galleryService.loadCompatibleVersion(extension) + .then(extensionToInstall => this.galleryService.getAllDependencies(extension) + .then(allDependencies => this.filterDependenciesToInstall(extension, allDependencies)) + .then(dependenciesToInstall => [extensionToInstall, ...dependenciesToInstall])); + } + + private checkForObsolete(extensionsToInstall: IGalleryExtension[]): TPromise { + return this.filterObsolete(...extensionsToInstall.map(i => getLocalExtensionIdFromGallery(i, i.version))) + .then(obsolete => obsolete.length ? TPromise.wrapError(new Error(nls.localize('restartCodeGallery', "Please restart Code before reinstalling."))) : extensionsToInstall); + } + + private downloadInstallableExtension(extension: IGalleryExtension): TPromise { + const id = getLocalExtensionIdFromGallery(extension, extension.version); + const metadata = { + id: extension.uuid, + publisherId: extension.publisherId, + publisherDisplayName: extension.publisherDisplayName, + }; + return this.galleryService.download(extension) + .then(zipPath => validate(zipPath).then(() => ({ zipPath, id, metadata }))); + } + + private rollback(extensions: IGalleryExtension[]): TPromise { + return this.filterOutUninstalled(extensions) + .then(installed => TPromise.join(installed.map(local => this.uninstallExtension(local.id)))) + .then(() => null, () => null); + } + + private onInstallExtensions(extensions: IGalleryExtension[]): void { + for (const extension of extensions) { + const id = getLocalExtensionIdFromGallery(extension, extension.version); + this._onInstallExtension.fire({ id, gallery: extension }); + } + } + + private onDidInstallExtensions(extensions: IGalleryExtension[], local: ILocalExtension[], errorCode?: ErrorCode, error?: any): TPromise { + extensions.forEach((gallery, index) => { + const id = getLocalExtensionIdFromGallery(gallery, gallery.version); + if (errorCode) { + this._onDidInstallExtension.fire({ id, gallery, error: errorCode }); + } else { + this._onDidInstallExtension.fire({ id, gallery, local: local[index] }); + } + }); + return error ? TPromise.wrapError(Array.isArray(error) ? this.joinErrors(error) : error) : TPromise.as(null); } private filterDependenciesToInstall(extension: IGalleryExtension, dependencies: IGalleryExtension[]): TPromise { @@ -232,20 +231,7 @@ export class ExtensionManagementService implements IExtensionManagementService { return filtered.length ? filtered[0] : null; } - private downloadAndInstall(extension: IGalleryExtension): TPromise { - const id = getLocalExtensionIdFromGallery(extension, extension.version); - const metadata = { - id: extension.uuid, - publisherId: extension.publisherId, - publisherDisplayName: extension.publisherDisplayName, - }; - - return this.galleryService.download(extension) - .then(zipPath => validate(zipPath).then(() => zipPath)) - .then(zipPath => this.installExtension(zipPath, id, metadata)); - } - - private installExtension(zipPath: string, id: string, metadata: IGalleryMetadata = null): TPromise { + private installExtension({ zipPath, id, metadata }: InstallableExtension): TPromise { const extensionPath = path.join(this.extensionsPath, id); return pfs.rimraf(extensionPath).then(() => { @@ -291,7 +277,7 @@ export class ExtensionManagementService implements IExtensionManagementService { } return errors.reduce((previousValue: Error, currentValue: Error | string) => { - return new Error(`${previousValue.message}\n${currentValue instanceof Error ? currentValue.message : currentValue}`); + return new Error(`${previousValue.message}${previousValue.message ? ',' : ''}${currentValue instanceof Error ? currentValue.message : currentValue}`); }, new Error('')); } @@ -300,7 +286,7 @@ export class ExtensionManagementService implements IExtensionManagementService { .then(() => this.hasDependencies(extension, installed) ? this.promptForDependenciesAndUninstall(extension, installed, force) : this.promptAndUninstall(extension, installed, force)) .then(() => this.postUninstallExtension(extension), error => { - this.postUninstallExtension(extension, error); + this.postUninstallExtension(extension, ErrorCode.LOCAL); return TPromise.wrapError(error); }); } @@ -416,7 +402,7 @@ export class ExtensionManagementService implements IExtensionManagementService { .then(() => this.uninstallExtension(extension.id)) .then(() => this.postUninstallExtension(extension), error => { - this.postUninstallExtension(extension, error); + this.postUninstallExtension(extension, ErrorCode.LOCAL); return TPromise.wrapError(error); }); } @@ -435,7 +421,7 @@ export class ExtensionManagementService implements IExtensionManagementService { .then(() => this.unsetObsolete(id)); } - private async postUninstallExtension(extension: ILocalExtension, error?: any): TPromise { + private async postUninstallExtension(extension: ILocalExtension, error?: ErrorCode): TPromise { if (!error) { await this.galleryService.reportStatistic(extension.manifest.publisher, extension.manifest.name, extension.manifest.version, StatisticType.Uninstall); } diff --git a/src/vs/platform/extensions/common/extensionHost.ts b/src/vs/platform/extensions/common/extensionHost.ts index 3466201a199..dd2d2ca92d2 100644 --- a/src/vs/platform/extensions/common/extensionHost.ts +++ b/src/vs/platform/extensions/common/extensionHost.ts @@ -11,10 +11,4 @@ export const EXTENSION_LOG_BROADCAST_CHANNEL = 'vscode:extensionLog'; export const EXTENSION_ATTACH_BROADCAST_CHANNEL = 'vscode:extensionAttach'; export const EXTENSION_TERMINATE_BROADCAST_CHANNEL = 'vscode:extensionTerminate'; export const EXTENSION_RELOAD_BROADCAST_CHANNEL = 'vscode:extensionReload'; -export const EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL = 'vscode:extensionCloseExtensionHost'; - -export interface ILogEntry { - type: string; - severity: string; - arguments: any; -} \ No newline at end of file +export const EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL = 'vscode:extensionCloseExtensionHost'; \ No newline at end of file diff --git a/src/vs/platform/files/common/files.ts b/src/vs/platform/files/common/files.ts index c89cc6399bf..4456aae8707 100644 --- a/src/vs/platform/files/common/files.ts +++ b/src/vs/platform/files/common/files.ts @@ -13,6 +13,8 @@ import { isLinux } from 'vs/base/common/platform'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import Event from 'vs/base/common/event'; import { beginsWithIgnoreCase } from 'vs/base/common/strings'; +import { IProgress } from 'vs/platform/progress/common/progress'; +import { IDisposable } from 'vs/base/common/lifecycle'; export const IFileService = createDecorator('fileService'); @@ -30,6 +32,16 @@ export interface IFileService { */ onAfterOperation: Event; + /** + * Registeres a file system provider for a certain scheme. + */ + registerProvider?(scheme: string, provider: IFileSystemProvider): IDisposable; + + /** + * Checks if this file service can handle the given resource. + */ + canHandleResource?(resource: URI): boolean; + /** * Resolve the properties of a file identified by the resource. * @@ -150,6 +162,37 @@ export interface IFileService { dispose(): void; } + +export enum FileType { + File = 0, + Dir = 1, + Symlink = 2 +} +export interface IStat { + id: number | string; + mtime: number; + size: number; + type: FileType; +} + +export interface IFileSystemProvider { + + onDidChange?: Event; + + // more... + // + utimes(resource: URI, mtime: number, atime: number): TPromise; + stat(resource: URI): TPromise; + read(resource: URI, offset: number, count: number, progress: IProgress): TPromise; + write(resource: URI, content: Uint8Array): TPromise; + move(from: URI, to: URI): TPromise; + mkdir(resource: URI): TPromise; + readdir(resource: URI): TPromise<[URI, IStat][]>; + rmdir(resource: URI): TPromise; + unlink(resource: URI): TPromise; +} + + export enum FileOperation { CREATE, DELETE, diff --git a/src/vs/platform/jsonschemas/common/jsonContributionRegistry.ts b/src/vs/platform/jsonschemas/common/jsonContributionRegistry.ts index 5d4f91c0dff..b9c921d4505 100644 --- a/src/vs/platform/jsonschemas/common/jsonContributionRegistry.ts +++ b/src/vs/platform/jsonschemas/common/jsonContributionRegistry.ts @@ -5,9 +5,8 @@ 'use strict'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; -import platform = require('vs/platform/registry/common/platform'); -import { EventEmitter } from 'vs/base/common/eventEmitter'; -import { IDisposable } from 'vs/base/common/lifecycle'; +import * as platform from 'vs/platform/registry/common/platform'; +import Event, { Emitter } from 'vs/base/common/event'; export const Extensions = { JSONContribution: 'base.contributions.json' @@ -19,6 +18,8 @@ export interface ISchemaContributions { export interface IJSONContributionRegistry { + readonly onDidChangeSchema: Event; + /** * Register a schema to the registry. */ @@ -28,12 +29,6 @@ export interface IJSONContributionRegistry { * Get all schemas */ getSchemaContributions(): ISchemaContributions; - - /** - * Adds a change listener - */ - addRegistryChangedListener(callback: (e: IJSONContributionRegistryEvent) => void): IDisposable; - } export interface IJSONContributionRegistryEvent { @@ -50,21 +45,19 @@ function normalizeId(id: string) { class JSONContributionRegistry implements IJSONContributionRegistry { + private schemasById: { [id: string]: IJSONSchema }; - private eventEmitter: EventEmitter; + + private _onDidChangeSchema: Emitter = new Emitter(); + readonly onDidChangeSchema: Event = this._onDidChangeSchema.event; constructor() { this.schemasById = {}; - this.eventEmitter = new EventEmitter(); - } - - public addRegistryChangedListener(callback: (e: IJSONContributionRegistryEvent) => void): IDisposable { - return this.eventEmitter.addListener('registryChanged', callback); } public registerSchema(uri: string, unresolvedSchemaContent: IJSONSchema): void { this.schemasById[normalizeId(uri)] = unresolvedSchemaContent; - this.eventEmitter.emit('registryChanged', {}); + this._onDidChangeSchema.fire(uri); } public getSchemaContributions(): ISchemaContributions { diff --git a/src/vs/platform/node/product.ts b/src/vs/platform/node/product.ts index 29c55f1e439..a986f877135 100644 --- a/src/vs/platform/node/product.ts +++ b/src/vs/platform/node/product.ts @@ -18,7 +18,7 @@ export interface IProductConfiguration { downloadUrl: string; updateUrl?: string; quality?: string; - commit: string; + commit?: string; date: string; extensionsGallery: { serviceUrl: string; @@ -26,7 +26,7 @@ export interface IProductConfiguration { }; extensionTips: { [id: string]: string; }; extensionImportantTips: { [id: string]: { name: string; pattern: string; }; }; - exeBasedExtensionTips: { [id: string]: string; }; + exeBasedExtensionTips: { [id: string]: any; }; extensionKeywords: { [extension: string]: string[]; }; extensionAllowedBadgeProviders: string[]; keymapExtensionTips: string[]; @@ -86,4 +86,4 @@ if (process.env['VSCODE_DEV']) { product.dataFolderName += '-dev'; } -export default product; \ No newline at end of file +export default product; diff --git a/src/vs/platform/opener/browser/openerService.ts b/src/vs/platform/opener/browser/openerService.ts index f6d6e7a0287..d567a6f5779 100644 --- a/src/vs/platform/opener/browser/openerService.ts +++ b/src/vs/platform/opener/browser/openerService.ts @@ -73,7 +73,7 @@ export class OpenerService implements IOpenerService { return TPromise.as(undefined); } else if (resource.scheme === Schemas.file) { - resource = URI.file(normalize(resource.fsPath)); // workaround for non-normalized paths (https://github.com/Microsoft/vscode/issues/12954) + resource = resource.with({ path: normalize(resource.path) }); // workaround for non-normalized paths (https://github.com/Microsoft/vscode/issues/12954) } promise = this._editorService.openEditor({ resource, options: { selection, } }, options && options.openToSide); } diff --git a/src/vs/platform/quickOpen/common/quickOpen.ts b/src/vs/platform/quickOpen/common/quickOpen.ts index 82277c2212e..c0611d3dde4 100644 --- a/src/vs/platform/quickOpen/common/quickOpen.ts +++ b/src/vs/platform/quickOpen/common/quickOpen.ts @@ -35,6 +35,7 @@ export interface IPickOpenEntry { } export interface IPickOpenItem { + index: number; remove: () => void; getId: () => string; getResource: () => uri; diff --git a/src/vs/platform/telemetry/node/commonProperties.ts b/src/vs/platform/telemetry/node/commonProperties.ts index 5e6611497c3..ea27f5c5b6c 100644 --- a/src/vs/platform/telemetry/node/commonProperties.ts +++ b/src/vs/platform/telemetry/node/commonProperties.ts @@ -11,7 +11,7 @@ import * as uuid from 'vs/base/common/uuid'; export const machineIdStorageKey = 'telemetry.machineId'; export const machineIdIpcChannel = 'vscode:machineId'; -export function resolveCommonProperties(commit: string, version: string): TPromise<{ [name: string]: string; }> { +export function resolveCommonProperties(commit: string, version: string, source: string): TPromise<{ [name: string]: string; }> { const result: { [name: string]: string; } = Object.create(null); result['sessionID'] = uuid.generateUuid() + Date.now(); @@ -21,6 +21,7 @@ export function resolveCommonProperties(commit: string, version: string): TPromi result['common.platform'] = Platform.Platform[Platform.platform]; result['common.nodePlatform'] = process.platform; result['common.nodeArch'] = process.arch; + result['common.source'] = source; // dynamic properties which value differs on each call let seq = 0; diff --git a/src/vs/platform/telemetry/node/workbenchCommonProperties.ts b/src/vs/platform/telemetry/node/workbenchCommonProperties.ts index 58443e27c9b..c6a5772af49 100644 --- a/src/vs/platform/telemetry/node/workbenchCommonProperties.ts +++ b/src/vs/platform/telemetry/node/workbenchCommonProperties.ts @@ -14,8 +14,8 @@ import { resolveCommonProperties, machineIdStorageKey } from '../node/commonProp const SQM_KEY: string = '\\Software\\Microsoft\\SQMClient'; -export function resolveWorkbenchCommonProperties(storageService: IStorageService, commit: string, version: string): TPromise<{ [name: string]: string }> { - return resolveCommonProperties(commit, version).then(result => { +export function resolveWorkbenchCommonProperties(storageService: IStorageService, commit: string, version: string, source: string): TPromise<{ [name: string]: string }> { + return resolveCommonProperties(commit, version, source).then(result => { result['common.version.shell'] = process.versions && (process).versions['electron']; result['common.version.renderer'] = process.versions && (process).versions['chrome']; result['common.osVersion'] = os.release(); diff --git a/src/vs/platform/telemetry/test/electron-browser/commonProperties.test.ts b/src/vs/platform/telemetry/test/electron-browser/commonProperties.test.ts index 810f50728c5..d57e87fbec5 100644 --- a/src/vs/platform/telemetry/test/electron-browser/commonProperties.test.ts +++ b/src/vs/platform/telemetry/test/electron-browser/commonProperties.test.ts @@ -14,6 +14,7 @@ suite('Telemetry - common properties', function () { const commit = void 0; const version = void 0; + const source = void 0; let storageService; setup(() => { @@ -22,7 +23,7 @@ suite('Telemetry - common properties', function () { test('default', function () { - return resolveWorkbenchCommonProperties(storageService, commit, version).then(props => { + return resolveWorkbenchCommonProperties(storageService, commit, version, source).then(props => { assert.ok('commitHash' in props); assert.ok('sessionID' in props); @@ -37,6 +38,7 @@ suite('Telemetry - common properties', function () { // assert.ok('common.version.renderer' in first.data); assert.ok('common.osVersion' in props, 'osVersion'); assert.ok('version' in props); + assert.ok('common.source' in props); assert.ok('common.firstSessionDate' in props, 'firstSessionDate'); assert.ok('common.lastSessionDate' in props, 'lastSessionDate'); // conditional, see below, 'lastSessionDate'ow @@ -57,7 +59,7 @@ suite('Telemetry - common properties', function () { storageService.store('telemetry.lastSessionDate', new Date().toUTCString()); - return resolveWorkbenchCommonProperties(storageService, commit, version).then(props => { + return resolveWorkbenchCommonProperties(storageService, commit, version, source).then(props => { assert.ok('common.lastSessionDate' in props); // conditional, see below assert.ok('common.isNewSession' in props); @@ -66,7 +68,7 @@ suite('Telemetry - common properties', function () { }); test('values chance on ask', function () { - return resolveWorkbenchCommonProperties(storageService, commit, version).then(props => { + return resolveWorkbenchCommonProperties(storageService, commit, version, source).then(props => { let value1 = props['common.sequence']; let value2 = props['common.sequence']; assert.ok(value1 !== value2, 'seq'); diff --git a/src/vs/platform/theme/common/styler.ts b/src/vs/platform/theme/common/styler.ts index d5d4698e424..03ae2a5dc63 100644 --- a/src/vs/platform/theme/common/styler.ts +++ b/src/vs/platform/theme/common/styler.ts @@ -11,6 +11,10 @@ import { IDisposable } from 'vs/base/common/lifecycle'; export type styleFn = (colors: { [name: string]: ColorIdentifier }) => void; +export interface IStyleOverrides { + [color: string]: ColorIdentifier; +} + export interface IThemable { style: styleFn; } @@ -43,36 +47,43 @@ export function attachStyler(themeService: IThemeServic return themeService.onThemeChange(applyStyles); } -export function attachCheckboxStyler(widget: IThemable, themeService: IThemeService, style?: { inputActiveOptionBorderColor?: ColorIdentifier }): IDisposable { - return attachStyler(themeService, { - inputActiveOptionBorder: (style && style.inputActiveOptionBorderColor) || inputActiveOptionBorder - }, widget); +export interface ICheckboxStyleOverrides extends IStyleOverrides { + inputActiveOptionBorderColor?: ColorIdentifier; } -export function attachBadgeStyler(widget: IThemable, themeService: IThemeService, style?: - { - badgeBackground?: ColorIdentifier, - badgeForeground?: ColorIdentifier - }): IDisposable { +export function attachCheckboxStyler(widget: IThemable, themeService: IThemeService, style?: ICheckboxStyleOverrides): IDisposable { + return attachStyler(themeService, { + inputActiveOptionBorder: (style && style.inputActiveOptionBorderColor) || inputActiveOptionBorder + } as ICheckboxStyleOverrides, widget); +} + +export interface IBadgeStyleOverrides extends IStyleOverrides { + badgeBackground?: ColorIdentifier; + badgeForeground?: ColorIdentifier; +} + +export function attachBadgeStyler(widget: IThemable, themeService: IThemeService, style?: IBadgeStyleOverrides): IDisposable { return attachStyler(themeService, { badgeBackground: (style && style.badgeBackground) || badgeBackground, badgeForeground: (style && style.badgeForeground) || badgeForeground, badgeBorder: contrastBorder - }, widget); + } as IBadgeStyleOverrides, widget); } -export function attachInputBoxStyler(widget: IThemable, themeService: IThemeService, style?: - { - inputBackground?: ColorIdentifier, - inputForeground?: ColorIdentifier, - inputBorder?: ColorIdentifier, - inputValidationInfoBorder?: ColorIdentifier, - inputValidationInfoBackground?: ColorIdentifier, - inputValidationWarningBorder?: ColorIdentifier, - inputValidationWarningBackground?: ColorIdentifier, - inputValidationErrorBorder?: ColorIdentifier, - inputValidationErrorBackground?: ColorIdentifier - }): IDisposable { +export interface IInputBoxStyleOverrides extends IStyleOverrides { + inputBackground?: ColorIdentifier; + inputForeground?: ColorIdentifier; + inputBorder?: ColorIdentifier; + inputActiveOptionBorder?: ColorIdentifier; + inputValidationInfoBorder?: ColorIdentifier; + inputValidationInfoBackground?: ColorIdentifier; + inputValidationWarningBorder?: ColorIdentifier; + inputValidationWarningBackground?: ColorIdentifier; + inputValidationErrorBorder?: ColorIdentifier; + inputValidationErrorBackground?: ColorIdentifier; +} + +export function attachInputBoxStyler(widget: IThemable, themeService: IThemeService, style?: IInputBoxStyleOverrides): IDisposable { return attachStyler(themeService, { inputBackground: (style && style.inputBackground) || inputBackground, inputForeground: (style && style.inputForeground) || inputForeground, @@ -83,30 +94,24 @@ export function attachInputBoxStyler(widget: IThemable, themeService: IThemeServ inputValidationWarningBackground: (style && style.inputValidationWarningBackground) || inputValidationWarningBackground, inputValidationErrorBorder: (style && style.inputValidationErrorBorder) || inputValidationErrorBorder, inputValidationErrorBackground: (style && style.inputValidationErrorBackground) || inputValidationErrorBackground - }, widget); + } as IInputBoxStyleOverrides, widget); } -export function attachSelectBoxStyler(widget: IThemable, themeService: IThemeService, style?: { selectBackground?: ColorIdentifier, selectForeground?: ColorIdentifier, selectBorder?: ColorIdentifier }): IDisposable { +export interface ISelectBoxStyleOverrides extends IStyleOverrides { + selectBackground?: ColorIdentifier; + selectForeground?: ColorIdentifier; + selectBorder?: ColorIdentifier; +} + +export function attachSelectBoxStyler(widget: IThemable, themeService: IThemeService, style?: ISelectBoxStyleOverrides): IDisposable { return attachStyler(themeService, { selectBackground: (style && style.selectBackground) || selectBackground, selectForeground: (style && style.selectForeground) || selectForeground, selectBorder: (style && style.selectBorder) || selectBorder - }, widget); + } as ISelectBoxStyleOverrides, widget); } -export function attachFindInputBoxStyler(widget: IThemable, themeService: IThemeService, style?: - { - inputBackground?: ColorIdentifier, - inputForeground?: ColorIdentifier, - inputBorder?: ColorIdentifier, - inputActiveOptionBorder?: ColorIdentifier, - inputValidationInfoBorder?: ColorIdentifier, - inputValidationInfoBackground?: ColorIdentifier, - inputValidationWarningBorder?: ColorIdentifier, - inputValidationWarningBackground?: ColorIdentifier, - inputValidationErrorBorder?: ColorIdentifier, - inputValidationErrorBackground?: ColorIdentifier - }): IDisposable { +export function attachFindInputBoxStyler(widget: IThemable, themeService: IThemeService, style?: IInputBoxStyleOverrides): IDisposable { return attachStyler(themeService, { inputBackground: (style && style.inputBackground) || inputBackground, inputForeground: (style && style.inputForeground) || inputForeground, @@ -118,43 +123,19 @@ export function attachFindInputBoxStyler(widget: IThemable, themeService: ITheme inputValidationWarningBackground: (style && style.inputValidationWarningBackground) || inputValidationWarningBackground, inputValidationErrorBorder: (style && style.inputValidationErrorBorder) || inputValidationErrorBorder, inputValidationErrorBackground: (style && style.inputValidationErrorBackground) || inputValidationErrorBackground - }, widget); + } as IInputBoxStyleOverrides, widget); } -export function attachQuickOpenStyler(widget: IThemable, themeService: IThemeService, style?: { - foreground?: ColorIdentifier, - background?: ColorIdentifier, - borderColor?: ColorIdentifier, - widgetShadow?: ColorIdentifier, - progressBarBackground?: ColorIdentifier, - inputBackground?: ColorIdentifier, - inputForeground?: ColorIdentifier, - inputBorder?: ColorIdentifier, - inputValidationInfoBorder?: ColorIdentifier, - inputValidationInfoBackground?: ColorIdentifier, - inputValidationWarningBorder?: ColorIdentifier, - inputValidationWarningBackground?: ColorIdentifier, - inputValidationErrorBorder?: ColorIdentifier, - inputValidationErrorBackground?: ColorIdentifier - pickerGroupForeground?: ColorIdentifier, - pickerGroupBorder?: ColorIdentifier, - listFocusBackground?: ColorIdentifier, - listFocusForeground?: ColorIdentifier, - listActiveSelectionBackground?: ColorIdentifier, - listActiveSelectionForeground?: ColorIdentifier, - listFocusAndSelectionBackground?: ColorIdentifier, - listFocusAndSelectionForeground?: ColorIdentifier, - listInactiveSelectionBackground?: ColorIdentifier, - listInactiveSelectionForeground?: ColorIdentifier, - listInactiveFocusBackground?: ColorIdentifier, - listInactiveFocusForeground?: ColorIdentifier, - listHoverBackground?: ColorIdentifier, - listHoverForeground?: ColorIdentifier, - listDropBackground?: ColorIdentifier, - listFocusOutline?: ColorIdentifier, - listSelectionOutline?: ColorIdentifier, - listHoverOutline?: ColorIdentifier -}): IDisposable { +export interface IQuickOpenStyleOverrides extends IListStyleOverrides, IInputBoxStyleOverrides, IProgressBarStyleOverrides { + foreground?: ColorIdentifier; + background?: ColorIdentifier; + borderColor?: ColorIdentifier; + widgetShadow?: ColorIdentifier; + pickerGroupForeground?: ColorIdentifier; + pickerGroupBorder?: ColorIdentifier; +}; + +export function attachQuickOpenStyler(widget: IThemable, themeService: IThemeService, style?: IQuickOpenStyleOverrides): IDisposable { return attachStyler(themeService, { foreground: (style && style.foreground) || foreground, background: (style && style.background) || editorBackground, @@ -188,28 +169,30 @@ export function attachQuickOpenStyler(widget: IThemable, themeService: IThemeSer listFocusOutline: (style && style.listFocusOutline) || activeContrastBorder, listSelectionOutline: (style && style.listSelectionOutline) || activeContrastBorder, listHoverOutline: (style && style.listHoverOutline) || activeContrastBorder - }, widget); + } as IQuickOpenStyleOverrides, widget); } -export function attachListStyler(widget: IThemable, themeService: IThemeService, style?: { - listFocusBackground?: ColorIdentifier, - listFocusForeground?: ColorIdentifier, - listActiveSelectionBackground?: ColorIdentifier, - listActiveSelectionForeground?: ColorIdentifier, - listFocusAndSelectionBackground?: ColorIdentifier, - listFocusAndSelectionForeground?: ColorIdentifier, - listInactiveSelectionBackground?: ColorIdentifier, - listInactiveSelectionForeground?: ColorIdentifier, - listInactiveFocusBackground?: ColorIdentifier, - listInactiveFocusForeground?: ColorIdentifier, - listHoverBackground?: ColorIdentifier, - listHoverForeground?: ColorIdentifier, - listDropBackground?: ColorIdentifier, - listFocusOutline?: ColorIdentifier, - listInactiveFocusOutline?: ColorIdentifier, - listSelectionOutline?: ColorIdentifier, - listHoverOutline?: ColorIdentifier, -}): IDisposable { +export interface IListStyleOverrides extends IStyleOverrides { + listFocusBackground?: ColorIdentifier; + listFocusForeground?: ColorIdentifier; + listActiveSelectionBackground?: ColorIdentifier; + listActiveSelectionForeground?: ColorIdentifier; + listFocusAndSelectionBackground?: ColorIdentifier; + listFocusAndSelectionForeground?: ColorIdentifier; + listInactiveSelectionBackground?: ColorIdentifier; + listInactiveSelectionForeground?: ColorIdentifier; + listInactiveFocusBackground?: ColorIdentifier; + listInactiveFocusForeground?: ColorIdentifier; + listHoverBackground?: ColorIdentifier; + listHoverForeground?: ColorIdentifier; + listDropBackground?: ColorIdentifier; + listFocusOutline?: ColorIdentifier; + listInactiveFocusOutline?: ColorIdentifier; + listSelectionOutline?: ColorIdentifier; + listHoverOutline?: ColorIdentifier; +} + +export function attachListStyler(widget: IThemable, themeService: IThemeService, style?: IListStyleOverrides): IDisposable { return attachStyler(themeService, { listFocusBackground: (style && style.listFocusBackground) || listFocusBackground, listFocusForeground: (style && style.listFocusForeground) || listFocusForeground, @@ -228,22 +211,32 @@ export function attachListStyler(widget: IThemable, themeService: IThemeService, listSelectionOutline: (style && style.listSelectionOutline) || activeContrastBorder, listHoverOutline: (style && style.listHoverOutline) || activeContrastBorder, listInactiveFocusOutline: style && style.listInactiveFocusOutline // not defined by default, only opt-in - }, widget); + } as IListStyleOverrides, widget); } -export function attachButtonStyler(widget: IThemable, themeService: IThemeService, style?: { buttonForeground?: ColorIdentifier, buttonBackground?: ColorIdentifier, buttonHoverBackground?: ColorIdentifier }): IDisposable { +export interface IButtonStyleOverrides extends IStyleOverrides { + buttonForeground?: ColorIdentifier; + buttonBackground?: ColorIdentifier; + buttonHoverBackground?: ColorIdentifier; +} + +export function attachButtonStyler(widget: IThemable, themeService: IThemeService, style?: IButtonStyleOverrides): IDisposable { return attachStyler(themeService, { buttonForeground: (style && style.buttonForeground) || buttonForeground, buttonBackground: (style && style.buttonBackground) || buttonBackground, buttonHoverBackground: (style && style.buttonHoverBackground) || buttonHoverBackground, buttonBorder: contrastBorder - }, widget); + } as IButtonStyleOverrides, widget); } -export function attachProgressBarStyler(widget: IThemable, themeService: IThemeService, style?: { progressBarBackground?: ColorIdentifier }): IDisposable { +export interface IProgressBarStyleOverrides extends IStyleOverrides { + progressBarBackground?: ColorIdentifier; +} + +export function attachProgressBarStyler(widget: IThemable, themeService: IThemeService, style?: IProgressBarStyleOverrides): IDisposable { return attachStyler(themeService, { progressBarBackground: (style && style.progressBarBackground) || progressBarBackground - }, widget); + } as IProgressBarStyleOverrides, widget); } export function attachStylerCallback(themeService: IThemeService, colors: { [name: string]: ColorIdentifier }, callback: styleFn): IDisposable { diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index ee9e0250027..6be7162905c 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -242,7 +242,6 @@ export interface IWindowConfiguration extends ParsedArgs, IOpenFileRequest { workspace?: IWorkspaceIdentifier; folderPath?: string; - isISOKeyboard?: boolean; zoomLevel?: number; fullscreen?: boolean; highContrast?: boolean; @@ -253,4 +252,9 @@ export interface IWindowConfiguration extends ParsedArgs, IOpenFileRequest { perfStartTime?: number; perfAppReady?: number; perfWindowLoadTime?: number; +} + +export interface IRunActionInWindowRequest { + id: string; + from: 'menu' | 'touchbar' | 'mouse'; } \ No newline at end of file diff --git a/src/vs/platform/workspace/common/workspace.ts b/src/vs/platform/workspace/common/workspace.ts index 0e307a99689..3c05b4dd124 100644 --- a/src/vs/platform/workspace/common/workspace.ts +++ b/src/vs/platform/workspace/common/workspace.ts @@ -6,10 +6,11 @@ import URI from 'vs/base/common/uri'; import * as paths from 'vs/base/common/paths'; +import * as resources from 'vs/base/common/resources'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { TrieMap } from 'vs/base/common/map'; +import { StringTrieMap } from 'vs/base/common/map'; import Event from 'vs/base/common/event'; -import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier, IStoredWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces'; +import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier, IStoredWorkspaceFolder, isRawFileWorkspaceFolder, isRawUriWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces'; import { coalesce, distinct } from 'vs/base/common/arrays'; import { isLinux } from 'vs/base/common/platform'; @@ -21,6 +22,12 @@ export enum WorkbenchState { WORKSPACE } +export interface IWorkspaceFoldersChangeEvent { + added: IWorkspaceFolder[]; + removed: IWorkspaceFolder[]; + changed: IWorkspaceFolder[]; +} + export interface IWorkspaceContextService { _serviceBrand: any; @@ -52,13 +59,13 @@ export interface IWorkspaceContextService { /** * An event which fires on workspace folders change. */ - onDidChangeWorkspaceFolders: Event; + onDidChangeWorkspaceFolders: Event; /** * Returns the folder for the given resource from the workspace. * Can be null if there is no workspace or the resource is not inside the workspace. */ - getWorkspaceFolder(resource: URI): WorkspaceFolder; + getWorkspaceFolder(resource: URI): IWorkspaceFolder; /** * Return `true` if the current workspace has the given identifier otherwise `false`. @@ -69,11 +76,6 @@ export interface IWorkspaceContextService { * Returns if the provided resource is inside the workspace or not. */ isInsideWorkspace(resource: URI): boolean; - - /** - * Given a workspace relative path and workspace folder, returns the resource with the absolute path. - */ - toResource: (workspaceRelativePath: string, workspaceFolder: WorkspaceFolder) => URI; } export interface IWorkspace { @@ -91,7 +93,7 @@ export interface IWorkspace { /** * Folders in the workspace. */ - readonly folders: WorkspaceFolder[]; + readonly folders: IWorkspaceFolder[]; /** * the location of the workspace configuration @@ -99,8 +101,7 @@ export interface IWorkspace { readonly configuration?: URI; } -export interface WorkspaceFolder { - +export interface IWorkspaceFolderData { /** * The associated URI for this workspace folder. */ @@ -116,16 +117,19 @@ export interface WorkspaceFolder { * The ordinal number of this workspace folder. */ readonly index: number; +} + +export interface IWorkspaceFolder extends IWorkspaceFolderData { /** - * The raw path of this workspace folder + * Given workspace folder relative path, returns the resource with the absolute path. */ - readonly raw: IStoredWorkspaceFolder; + toResource: (relativePath: string) => URI; } export class Workspace implements IWorkspace { - private _foldersMap: TrieMap = new TrieMap(); + private _foldersMap: StringTrieMap = new StringTrieMap(); private _folders: WorkspaceFolder[]; constructor( @@ -179,18 +183,18 @@ export class Workspace implements IWorkspace { this._configuration = configuration; } - public getFolder(resource: URI): WorkspaceFolder { + public getFolder(resource: URI): IWorkspaceFolder { if (!resource) { return null; } - return this._foldersMap.findSubstr(resource.fsPath); + return this._foldersMap.findSubstr(resource.toString()); } private updateFoldersMap(): void { - this._foldersMap = new TrieMap(); + this._foldersMap = new StringTrieMap(); for (const folder of this.folders) { - this._foldersMap.insert(folder.uri.fsPath, folder); + this._foldersMap.insert(folder.uri.toString(), folder); } } @@ -199,19 +203,51 @@ export class Workspace implements IWorkspace { } } +export class WorkspaceFolder implements IWorkspaceFolder { + + readonly uri: URI; + readonly name: string; + readonly index: number; + + constructor(data: IWorkspaceFolderData, + readonly raw?: IStoredWorkspaceFolder) { + this.uri = data.uri; + this.index = data.index; + this.name = data.name; + } + + toResource(relativePath: string): URI { + return this.uri.with({ path: paths.join(this.uri.path, relativePath) }); + } + + toJSON(): IWorkspaceFolderData { + return { uri: this.uri, name: this.name, index: this.index }; + } +} + export function toWorkspaceFolders(configuredFolders: IStoredWorkspaceFolder[], relativeTo?: URI): WorkspaceFolder[] { let workspaceFolders = parseWorkspaceFolders(configuredFolders, relativeTo); return ensureUnique(coalesce(workspaceFolders)) - .map(({ uri, raw, name }, index) => ({ uri, raw, name: name || paths.basename(uri.fsPath), index })); + .map(({ uri, raw, name }, index) => new WorkspaceFolder({ uri, name: name || resources.basenameOrAuthority(uri), index }, raw)); } function parseWorkspaceFolders(configuredFolders: IStoredWorkspaceFolder[], relativeTo: URI): WorkspaceFolder[] { return configuredFolders.map((configuredFolder, index) => { - const uri = toUri(configuredFolder.path, relativeTo); + let uri: URI; + if (isRawFileWorkspaceFolder(configuredFolder)) { + uri = toUri(configuredFolder.path, relativeTo); + } else if (isRawUriWorkspaceFolder(configuredFolder)) { + try { + uri = URI.parse(configuredFolder.uri); + } catch (e) { + console.warn(e); + // ignore + } + } if (!uri) { return void 0; } - return { uri, raw: configuredFolder, index, name: configuredFolder.name }; + return new WorkspaceFolder({ uri, name: configuredFolder.name, index }, configuredFolder); }); } @@ -221,12 +257,12 @@ function toUri(path: string, relativeTo: URI): URI { return URI.file(path); } if (relativeTo) { - return URI.file(paths.join(relativeTo.fsPath, path)); + return relativeTo.with({ path: paths.join(relativeTo.path, path) }); } } return null; } function ensureUnique(folders: WorkspaceFolder[]): WorkspaceFolder[] { - return distinct(folders, folder => isLinux ? folder.uri.fsPath : folder.uri.fsPath.toLowerCase()); + return distinct(folders, folder => isLinux ? folder.uri.toString() : folder.uri.toString().toLowerCase()); } diff --git a/src/vs/platform/workspace/test/common/workspace.test.ts b/src/vs/platform/workspace/test/common/workspace.test.ts index f81c9186eb9..ac6f0caf17f 100644 --- a/src/vs/platform/workspace/test/common/workspace.test.ts +++ b/src/vs/platform/workspace/test/common/workspace.test.ts @@ -6,14 +6,15 @@ 'use strict'; import * as assert from 'assert'; -import { Workspace, WorkspaceFolder, toWorkspaceFolders } from 'vs/platform/workspace/common/workspace'; +import { Workspace, toWorkspaceFolders, WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import URI from 'vs/base/common/uri'; +import { IRawFileWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces'; suite('Workspace', () => { test('getFolder returns the folder with given uri', () => { - const expected = aWorkspaceFolder(URI.file('/src/test')); - let testObject = new Workspace('', '', [aWorkspaceFolder(URI.file('/src/main')), expected, aWorkspaceFolder(URI.file('/src/code'))]); + const expected = new WorkspaceFolder({ uri: URI.file('/src/test'), name: '', index: 2 }); + let testObject = new Workspace('', '', [new WorkspaceFolder({ uri: URI.file('/src/main'), name: '', index: 0 }), expected, new WorkspaceFolder({ uri: URI.file('/src/code'), name: '', index: 2 })]); const actual = testObject.getFolder(expected.uri); @@ -21,8 +22,8 @@ suite('Workspace', () => { }); test('getFolder returns the folder if the uri is sub', () => { - const expected = aWorkspaceFolder(URI.file('/src/test')); - let testObject = new Workspace('', '', [expected, aWorkspaceFolder(URI.file('/src/main')), aWorkspaceFolder(URI.file('/src/code'))]); + const expected = new WorkspaceFolder({ uri: URI.file('/src/test'), name: '', index: 0 }); + let testObject = new Workspace('', '', [expected, new WorkspaceFolder({ uri: URI.file('/src/main'), name: '', index: 1 }), new WorkspaceFolder({ uri: URI.file('/src/code'), name: '', index: 2 })]); const actual = testObject.getFolder(URI.file('/src/test/a')); @@ -30,8 +31,8 @@ suite('Workspace', () => { }); test('getFolder returns the closest folder if the uri is sub', () => { - const expected = aWorkspaceFolder(URI.file('/src/test')); - let testObject = new Workspace('', '', [aWorkspaceFolder(URI.file('/src/code')), aWorkspaceFolder(URI.file('/src')), expected]); + const expected = new WorkspaceFolder({ uri: URI.file('/src/test'), name: '', index: 2 }); + let testObject = new Workspace('', '', [new WorkspaceFolder({ uri: URI.file('/src/main'), name: '', index: 0 }), new WorkspaceFolder({ uri: URI.file('/src/code'), name: '', index: 1 }), expected]); const actual = testObject.getFolder(URI.file('/src/test/a')); @@ -39,7 +40,7 @@ suite('Workspace', () => { }); test('getFolder returns null if the uri is not sub', () => { - let testObject = new Workspace('', '', [aWorkspaceFolder(URI.file('/src/code')), aWorkspaceFolder(URI.file('/src/test'))]); + let testObject = new Workspace('', '', [new WorkspaceFolder({ uri: URI.file('/src/test'), name: '', index: 0 }), new WorkspaceFolder({ uri: URI.file('/src/code'), name: '', index: 1 })]); const actual = testObject.getFolder(URI.file('/src/main/a')); @@ -51,7 +52,7 @@ suite('Workspace', () => { assert.equal(actual.length, 1); assert.equal(actual[0].uri.fsPath, URI.file('/src/test').fsPath); - assert.equal(actual[0].raw.path, '/src/test'); + assert.equal((actual[0].raw).path, '/src/test'); assert.equal(actual[0].index, 0); assert.equal(actual[0].name, 'test'); }); @@ -61,7 +62,7 @@ suite('Workspace', () => { assert.equal(actual.length, 1); assert.equal(actual[0].uri.fsPath, URI.file('/src/test').fsPath); - assert.equal(actual[0].raw.path, './test'); + assert.equal((actual[0].raw).path, './test'); assert.equal(actual[0].index, 0); assert.equal(actual[0].name, 'test'); }); @@ -70,8 +71,9 @@ suite('Workspace', () => { const actual = toWorkspaceFolders([{ path: '/src/test', name: 'hello' }]); assert.equal(actual.length, 1); + assert.equal(actual[0].uri.fsPath, URI.file('/src/test').fsPath); - assert.equal(actual[0].raw.path, '/src/test'); + assert.equal((actual[0].raw).path, '/src/test'); assert.equal(actual[0].index, 0); assert.equal(actual[0].name, 'hello'); }); @@ -81,17 +83,17 @@ suite('Workspace', () => { assert.equal(actual.length, 3); assert.equal(actual[0].uri.fsPath, URI.file('/src/test2').fsPath); - assert.equal(actual[0].raw.path, '/src/test2'); + assert.equal((actual[0].raw).path, '/src/test2'); assert.equal(actual[0].index, 0); assert.equal(actual[0].name, 'test2'); assert.equal(actual[1].uri.fsPath, URI.file('/src/test3').fsPath); - assert.equal(actual[1].raw.path, '/src/test3'); + assert.equal((actual[1].raw).path, '/src/test3'); assert.equal(actual[1].index, 1); assert.equal(actual[1].name, 'test3'); assert.equal(actual[2].uri.fsPath, URI.file('/src/test1').fsPath); - assert.equal(actual[2].raw.path, '/src/test1'); + assert.equal((actual[2].raw).path, '/src/test1'); assert.equal(actual[2].index, 2); assert.equal(actual[2].name, 'test1'); }); @@ -101,17 +103,17 @@ suite('Workspace', () => { assert.equal(actual.length, 3); assert.equal(actual[0].uri.fsPath, URI.file('/src/test2').fsPath); - assert.equal(actual[0].raw.path, '/src/test2'); + assert.equal((actual[0].raw).path, '/src/test2'); assert.equal(actual[0].index, 0); assert.equal(actual[0].name, 'test2'); assert.equal(actual[1].uri.fsPath, URI.file('/src/test3').fsPath); - assert.equal(actual[1].raw.path, '/src/test3'); + assert.equal((actual[1].raw).path, '/src/test3'); assert.equal(actual[1].index, 1); assert.equal(actual[1].name, 'noName'); assert.equal(actual[2].uri.fsPath, URI.file('/src/test1').fsPath); - assert.equal(actual[2].raw.path, '/src/test1'); + assert.equal((actual[2].raw).path, '/src/test1'); assert.equal(actual[2].index, 2); assert.equal(actual[2].name, 'test1'); }); @@ -121,17 +123,17 @@ suite('Workspace', () => { assert.equal(actual.length, 3); assert.equal(actual[0].uri.fsPath, URI.file('/src/test2').fsPath); - assert.equal(actual[0].raw.path, '/src/test2'); + assert.equal((actual[0].raw).path, '/src/test2'); assert.equal(actual[0].index, 0); assert.equal(actual[0].name, 'test2'); assert.equal(actual[1].uri.fsPath, URI.file('/abc/test3').fsPath); - assert.equal(actual[1].raw.path, '/abc/test3'); + assert.equal((actual[1].raw).path, '/abc/test3'); assert.equal(actual[1].index, 1); assert.equal(actual[1].name, 'noName'); assert.equal(actual[2].uri.fsPath, URI.file('/src/test1').fsPath); - assert.equal(actual[2].raw.path, './test1'); + assert.equal((actual[2].raw).path, './test1'); assert.equal(actual[2].index, 2); assert.equal(actual[2].name, 'test1'); }); @@ -141,12 +143,12 @@ suite('Workspace', () => { assert.equal(actual.length, 2); assert.equal(actual[0].uri.fsPath, URI.file('/src/test2').fsPath); - assert.equal(actual[0].raw.path, '/src/test2'); + assert.equal((actual[0].raw).path, '/src/test2'); assert.equal(actual[0].index, 0); assert.equal(actual[0].name, 'test2'); assert.equal(actual[1].uri.fsPath, URI.file('/src/test1').fsPath); - assert.equal(actual[1].raw.path, '/src/test1'); + assert.equal((actual[1].raw).path, '/src/test1'); assert.equal(actual[1].index, 1); assert.equal(actual[1].name, 'test1'); }); @@ -156,17 +158,17 @@ suite('Workspace', () => { assert.equal(actual.length, 3); assert.equal(actual[0].uri.fsPath, URI.file('/src/test2').fsPath); - assert.equal(actual[0].raw.path, '/src/test2'); + assert.equal((actual[0].raw).path, '/src/test2'); assert.equal(actual[0].index, 0); assert.equal(actual[0].name, 'test2'); assert.equal(actual[1].uri.fsPath, URI.file('/src/test3').fsPath); - assert.equal(actual[1].raw.path, '/src/test3'); + assert.equal((actual[1].raw).path, '/src/test3'); assert.equal(actual[1].index, 1); assert.equal(actual[1].name, 'noName'); assert.equal(actual[2].uri.fsPath, URI.file('/abc/test1').fsPath); - assert.equal(actual[2].raw.path, '/abc/test1'); + assert.equal((actual[2].raw).path, '/abc/test1'); assert.equal(actual[2].index, 2); assert.equal(actual[2].name, 'test1'); }); @@ -176,25 +178,18 @@ suite('Workspace', () => { assert.equal(actual.length, 3); assert.equal(actual[0].uri.fsPath, URI.file('/src/test2').fsPath); - assert.equal(actual[0].raw.path, '/src/test2'); + assert.equal((actual[0].raw).path, '/src/test2'); assert.equal(actual[0].index, 0); assert.equal(actual[0].name, 'test2'); assert.equal(actual[1].uri.fsPath, URI.file('/src/test3').fsPath); - assert.equal(actual[1].raw.path, './test3'); + assert.equal((actual[1].raw).path, './test3'); assert.equal(actual[1].index, 1); assert.equal(actual[1].name, 'test3'); assert.equal(actual[2].uri.fsPath, URI.file('/abc/test1').fsPath); - assert.equal(actual[2].raw.path, '/abc/test1'); + assert.equal((actual[2].raw).path, '/abc/test1'); assert.equal(actual[2].index, 2); assert.equal(actual[2].name, 'test1'); }); - - function aWorkspaceFolder(uri: URI, index: number = 0): WorkspaceFolder { - return { - uri, raw: { path: uri.fsPath }, index, name: '' - }; - } - -}); \ No newline at end of file +}); diff --git a/src/vs/platform/workspaces/common/workspaces.ts b/src/vs/platform/workspaces/common/workspaces.ts index 44d42a64d14..adaac16ff0b 100644 --- a/src/vs/platform/workspaces/common/workspaces.ts +++ b/src/vs/platform/workspaces/common/workspaces.ts @@ -14,6 +14,7 @@ import { isLinux } from 'vs/base/common/platform'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import Event from 'vs/base/common/event'; import { tildify, getPathLabel } from 'vs/base/common/labels'; +import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; export const IWorkspacesMainService = createDecorator('workspacesMainService'); export const IWorkspacesService = createDecorator('workspacesService'); @@ -32,17 +33,44 @@ export interface IWorkspaceIdentifier { configPath: string; } -export interface IStoredWorkspaceFolder { +export function isStoredWorkspaceFolder(thing: any): thing is IStoredWorkspaceFolder { + return isRawFileWorkspaceFolder(thing) || isRawUriWorkspaceFolder(thing); +} + +export function isRawFileWorkspaceFolder(thing: any): thing is IRawFileWorkspaceFolder { + return thing + && typeof thing === 'object' + && typeof thing.path === 'string' + && (!thing.name || typeof thing.name === 'string'); +} + +export function isRawUriWorkspaceFolder(thing: any): thing is IRawUriWorkspaceFolder { + return thing + && typeof thing === 'object' + && typeof thing.uri === 'string' + && (!thing.name || typeof thing.name === 'string'); +} + +export interface IRawFileWorkspaceFolder { path: string; name?: string; } +export interface IRawUriWorkspaceFolder { + uri: string; + name?: string; +} + +export type IStoredWorkspaceFolder = IRawFileWorkspaceFolder | IRawUriWorkspaceFolder; + +export interface IResolvedWorkspace extends IWorkspaceIdentifier { + folders: IWorkspaceFolder[]; +} + export interface IStoredWorkspace { folders: IStoredWorkspaceFolder[]; } -export interface IResolvedWorkspace extends IWorkspaceIdentifier, IStoredWorkspace { } - export interface IWorkspaceSavedEvent { workspace: IWorkspaceIdentifier; oldConfigPath: string; @@ -105,4 +133,4 @@ export function isWorkspaceIdentifier(obj: any): obj is IWorkspaceIdentifier { const workspaceIdentifier = obj as IWorkspaceIdentifier; return workspaceIdentifier && typeof workspaceIdentifier.id === 'string' && typeof workspaceIdentifier.configPath === 'string'; -} \ No newline at end of file +} diff --git a/src/vs/platform/workspaces/electron-main/workspacesMainService.ts b/src/vs/platform/workspaces/electron-main/workspacesMainService.ts index c5b552022a3..432850ff32d 100644 --- a/src/vs/platform/workspaces/electron-main/workspacesMainService.ts +++ b/src/vs/platform/workspaces/electron-main/workspacesMainService.ts @@ -5,7 +5,7 @@ 'use strict'; -import { IWorkspacesMainService, IWorkspaceIdentifier, IStoredWorkspace, WORKSPACE_EXTENSION, IWorkspaceSavedEvent, UNTITLED_WORKSPACE_NAME, IResolvedWorkspace } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspacesMainService, IWorkspaceIdentifier, WORKSPACE_EXTENSION, IWorkspaceSavedEvent, UNTITLED_WORKSPACE_NAME, IResolvedWorkspace, IStoredWorkspaceFolder, isRawFileWorkspaceFolder, isStoredWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces'; import { TPromise } from 'vs/base/common/winjs.base'; import { isParent } from 'vs/platform/files/common/files'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; @@ -23,6 +23,12 @@ import * as json from 'vs/base/common/json'; import * as jsonEdit from 'vs/base/common/jsonEdit'; import { applyEdit } from 'vs/base/common/jsonFormatter'; import { massageFolderPathForWorkspace } from 'vs/platform/workspaces/node/workspaces'; +import { toWorkspaceFolders } from 'vs/platform/workspace/common/workspace'; +import URI from 'vs/base/common/uri'; + +export interface IStoredWorkspace { + folders: IStoredWorkspaceFolder[]; +} export class WorkspacesMainService implements IWorkspacesMainService { @@ -75,17 +81,10 @@ export class WorkspacesMainService implements IWorkspacesMainService { try { const workspace = this.doParseStoredWorkspace(path, contents); - // relative paths get resolved against the workspace location - workspace.folders.forEach(folder => { - if (!isAbsolute(folder.path)) { - folder.path = resolve(dirname(path), folder.path); - } - }); - return { id: this.getWorkspaceId(path), configPath: path, - folders: workspace.folders + folders: toWorkspaceFolders(workspace.folders, URI.file(dirname(path))) }; } catch (error) { this.logService.log(error.toString()); @@ -104,13 +103,13 @@ export class WorkspacesMainService implements IWorkspacesMainService { throw new Error(`${path} cannot be parsed as JSON file (${error}).`); } - // Filter out folders which do not have a path set + // Filter out folders which do not have a path or uri set if (Array.isArray(storedWorkspace.folders)) { - storedWorkspace.folders = storedWorkspace.folders.filter(folder => !!folder.path); + storedWorkspace.folders = storedWorkspace.folders.filter(folder => isStoredWorkspaceFolder(folder)); } // Validate - if (!Array.isArray(storedWorkspace.folders) || storedWorkspace.folders.length === 0) { + if (!Array.isArray(storedWorkspace.folders)) { throw new Error(`${path} looks like an invalid workspace file.`); } @@ -200,11 +199,13 @@ export class WorkspacesMainService implements IWorkspacesMainService { // is a parent of the location of the workspace file itself. Otherwise keep // using absolute paths. storedWorkspace.folders.forEach(folder => { - if (!isAbsolute(folder.path)) { - folder.path = resolve(sourceConfigFolder, folder.path); // relative paths get resolved against the workspace location + if (isRawFileWorkspaceFolder(folder)) { + if (!isAbsolute(folder.path)) { + folder.path = resolve(sourceConfigFolder, folder.path); // relative paths get resolved against the workspace location + } + folder.path = massageFolderPathForWorkspace(folder.path, targetConfigFolder, storedWorkspace.folders); } - folder.path = massageFolderPathForWorkspace(folder.path, targetConfigFolder, storedWorkspace.folders); }); // Preserve as much of the existing workspace as possible by using jsonEdit diff --git a/src/vs/platform/workspaces/node/workspaces.ts b/src/vs/platform/workspaces/node/workspaces.ts index 95757880908..8b149ff2693 100644 --- a/src/vs/platform/workspaces/node/workspaces.ts +++ b/src/vs/platform/workspaces/node/workspaces.ts @@ -5,7 +5,7 @@ 'use strict'; -import { IStoredWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces'; +import { IStoredWorkspaceFolder, isRawFileWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces'; import { isWindows, isLinux } from 'vs/base/common/platform'; import { isAbsolute, relative } from 'path'; import { isEqualOrParent, normalize } from 'vs/base/common/paths'; @@ -56,11 +56,11 @@ function shouldUseSlashForPath(storedFolders: IStoredWorkspaceFolder[]): boolean let useSlashesForPath = !isWindows; if (isWindows) { storedFolders.forEach(folder => { - if (!useSlashesForPath && folder.path.indexOf(SLASH) >= 0) { + if (isRawFileWorkspaceFolder(folder) && !useSlashesForPath && folder.path.indexOf(SLASH) >= 0) { useSlashesForPath = true; } }); } return useSlashesForPath; -} \ No newline at end of file +} diff --git a/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts b/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts index 03bddbec9e3..918dd0ef819 100644 --- a/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts +++ b/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts @@ -13,8 +13,8 @@ import extfs = require('vs/base/node/extfs'); import pfs = require('vs/base/node/pfs'); import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; import { parseArgs } from 'vs/platform/environment/node/argv'; -import { WorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService'; -import { IStoredWorkspace, WORKSPACE_EXTENSION, IWorkspaceSavedEvent, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { WorkspacesMainService, IStoredWorkspace } from 'vs/platform/workspaces/electron-main/workspacesMainService'; +import { WORKSPACE_EXTENSION, IWorkspaceSavedEvent, IWorkspaceIdentifier, IRawFileWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces'; import { LogMainService } from 'vs/platform/log/common/log'; import URI from 'vs/base/common/uri'; @@ -66,8 +66,8 @@ suite('WorkspacesMainService', () => { const ws = JSON.parse(fs.readFileSync(workspace.configPath).toString()) as IStoredWorkspace; assert.equal(ws.folders.length, 2); // - assert.equal(ws.folders[0].path, process.cwd()); - assert.equal(ws.folders[1].path, os.tmpdir()); + assert.equal((ws.folders[0]).path, process.cwd()); + assert.equal((ws.folders[1]).path, os.tmpdir()); done(); }); @@ -81,8 +81,8 @@ suite('WorkspacesMainService', () => { const ws = JSON.parse(fs.readFileSync(workspace.configPath).toString()) as IStoredWorkspace; assert.equal(ws.folders.length, 2); // - assert.equal(ws.folders[0].path, process.cwd()); - assert.equal(ws.folders[1].path, os.tmpdir()); + assert.equal((ws.folders[0]).path, process.cwd()); + assert.equal((ws.folders[1]).path, os.tmpdir()); }); test('resolveWorkspaceSync', done => { @@ -138,7 +138,7 @@ suite('WorkspacesMainService', () => { fs.writeFileSync(workspace.configPath, JSON.stringify({ folders: [{ path: './ticino-playground/lib' }] })); const resolved = service.resolveWorkspaceSync(workspace.configPath); - assert.equal(URI.file(resolved.folders[0].path).fsPath, URI.file(path.join(path.dirname(workspace.configPath), 'ticino-playground', 'lib')).fsPath); + assert.equal(resolved.folders[0].uri.fsPath, URI.file(path.join(path.dirname(workspace.configPath), 'ticino-playground', 'lib')).fsPath); done(); }); @@ -149,7 +149,7 @@ suite('WorkspacesMainService', () => { fs.writeFileSync(workspace.configPath, JSON.stringify({ folders: [{ path: './ticino-playground/lib/../other' }] })); const resolved = service.resolveWorkspaceSync(workspace.configPath); - assert.equal(URI.file(resolved.folders[0].path).fsPath, URI.file(path.join(path.dirname(workspace.configPath), 'ticino-playground', 'other')).fsPath); + assert.equal(resolved.folders[0].uri.fsPath, URI.file(path.join(path.dirname(workspace.configPath), 'ticino-playground', 'other')).fsPath); done(); }); @@ -160,7 +160,7 @@ suite('WorkspacesMainService', () => { fs.writeFileSync(workspace.configPath, JSON.stringify({ folders: [{ path: 'ticino-playground/lib' }] })); const resolved = service.resolveWorkspaceSync(workspace.configPath); - assert.equal(URI.file(resolved.folders[0].path).fsPath, URI.file(path.join(path.dirname(workspace.configPath), 'ticino-playground', 'lib')).fsPath); + assert.equal(resolved.folders[0].uri.fsPath, URI.file(path.join(path.dirname(workspace.configPath), 'ticino-playground', 'lib')).fsPath); done(); }); @@ -171,7 +171,7 @@ suite('WorkspacesMainService', () => { fs.writeFileSync(workspace.configPath, '{ "folders": [ { "path": "./ticino-playground/lib" } , ] }'); // trailing comma const resolved = service.resolveWorkspaceSync(workspace.configPath); - assert.equal(URI.file(resolved.folders[0].path).fsPath, URI.file(path.join(path.dirname(workspace.configPath), 'ticino-playground', 'lib')).fsPath); + assert.equal(resolved.folders[0].uri.fsPath, URI.file(path.join(path.dirname(workspace.configPath), 'ticino-playground', 'lib')).fsPath); done(); }); @@ -200,9 +200,9 @@ suite('WorkspacesMainService', () => { const ws = JSON.parse(fs.readFileSync(savedWorkspace.configPath).toString()) as IStoredWorkspace; assert.equal(ws.folders.length, 3); - assert.equal(ws.folders[0].path, process.cwd()); // absolute - assert.equal(ws.folders[1].path, '.'); // relative - assert.equal(ws.folders[2].path, path.relative(path.dirname(workspaceConfigPath), path.join(os.tmpdir(), 'somefolder'))); // relative + assert.equal((ws.folders[0]).path, process.cwd()); // absolute + assert.equal((ws.folders[1]).path, '.'); // relative + assert.equal((ws.folders[2]).path, path.relative(path.dirname(workspaceConfigPath), path.join(os.tmpdir(), 'somefolder'))); // relative assert.equal(savedWorkspace, savedEvent.workspace); assert.equal(workspace.configPath, savedEvent.oldConfigPath); @@ -232,9 +232,9 @@ suite('WorkspacesMainService', () => { const ws = JSON.parse(fs.readFileSync(newSavedWorkspace.configPath).toString()) as IStoredWorkspace; assert.equal(ws.folders.length, 3); - assert.equal(ws.folders[0].path, process.cwd()); // absolute path because outside of tmpdir - assert.equal(ws.folders[1].path, '.'); // relative path because inside of tmpdir - assert.equal(ws.folders[2].path, path.relative(path.dirname(workspaceConfigPath), path.join(os.tmpdir(), 'somefolder'))); // relative + assert.equal((ws.folders[0]).path, process.cwd()); // absolute path because outside of tmpdir + assert.equal((ws.folders[1]).path, '.'); // relative path because inside of tmpdir + assert.equal((ws.folders[2]).path, path.relative(path.dirname(workspaceConfigPath), path.join(os.tmpdir(), 'somefolder'))); // relative extfs.delSync(workspaceConfigPath); extfs.delSync(newWorkspaceConfigPath); @@ -286,7 +286,7 @@ suite('WorkspacesMainService', () => { assert.equal(newSavedWorkspace.configPath, newWorkspaceConfigPath); const ws = JSON.parse(fs.readFileSync(newSavedWorkspace.configPath).toString()) as IStoredWorkspace; - assert.ok(ws.folders.every(f => f.path.indexOf('\\') < 0)); + assert.ok(ws.folders.every(f => (f).path.indexOf('\\') < 0)); extfs.delSync(workspaceConfigPath); extfs.delSync(newWorkspaceConfigPath); @@ -350,4 +350,4 @@ suite('WorkspacesMainService', () => { }); }); }); -}); \ No newline at end of file +}); diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 5229f8743d1..eb7385fd93f 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -1179,6 +1179,11 @@ declare module 'vscode' { */ static parse(value: string): Uri; + /** + * Use the `file` and `parse` factory functions to create new `Uri` objects. + */ + private constructor(scheme: string, authority: string, path: string, query: string, fragment: string); + /** * Scheme is the `http` part of `http://www.msft.com/some/path?query#fragment`. * The part before the first colon. @@ -1498,6 +1503,75 @@ declare module 'vscode' { onDidSelectItem?(item: QuickPickItem | string): any; } + /** + * Options to configure the behaviour of a file open dialog. + */ + export interface OpenDialogOptions { + /** + * The resource the dialog shows when opened. + */ + defaultUri?: Uri; + + /** + * A human-readable string for the open button. + */ + openLabel?: string; + + /** + * Only allow to select files. *Note* that not all operating systems support + * to select files and folders in one dialog instance. + */ + openFiles?: boolean; + + /** + * Only allow to select folders. *Note* that not all operating systems support + * to select files and folders in one dialog instance. + */ + openFolders?: boolean; + + /** + * Allow to select many files or folders. + */ + openMany?: boolean; + + /** + * A set of file filters that are shown in the dialog, e.g. + * ```ts + * { + * ['Images']: ['*.png', '*.jpg'] + * ['TypeScript']: ['*.ts', '*.tsx'] + * } + * ``` + */ + filters: { [name: string]: string[] }; + } + + /** + * Options to configure the behaviour of a file save dialog. + */ + export interface SaveDialogOptions { + /** + * The resource the dialog shows when opened. + */ + defaultUri?: Uri; + + /** + * A human-readable string for the save button. + */ + saveLabel?: string; + + /** + * A set of file filters that are shown in the dialog, e.g. + * ```ts + * { + * ['Images']: ['*.png', '*.jpg'] + * ['TypeScript']: ['*.ts', '*.tsx'] + * } + * ``` + */ + filters: { [name: string]: string[] }; + } + /** * Represents an action that is shown with an information, warning, or * error message. @@ -1584,6 +1658,31 @@ declare module 'vscode' { validateInput?(value: string): string | undefined | null; } + class RelativePattern { + + /** + * A base file path to which the pattern will be matched against relatively. + */ + base: string; + + /** + * A file glob pattern like `*.{ts,js}` that will be matched on file paths + * relative to the base path. + * + * Example: Given a base of `/home/work/folder` and a file path of `/home/work/folder/index.js`, + * the file glob pattern will match on `index.js`. + */ + pattern: string; + + constructor(pattern: string, base: WorkspaceFolder | string) + } + + /** + * A file glob pattern to match file paths against. This can either be a glob pattern string + * (like `**∕*.{ts,js}` or `*.{ts,js}`) or a [relative pattern](#RelativePattern). + */ + export type GlobPattern = string | RelativePattern; + /** * A document filter denotes a document by different properties like * the [language](#TextDocument.languageId), the [scheme](#Uri.scheme) of @@ -1605,9 +1704,10 @@ declare module 'vscode' { scheme?: string; /** - * A glob pattern, like `*.{ts,js}`. + * A [glob pattern](#GlobPattern) that is matched on the absolute path of the document. Use a [relative pattern](#RelativePattern) + * to filter documents to a [workspace folder](#WorkspaceFolder). */ - pattern?: string; + pattern?: GlobPattern; } /** @@ -1619,7 +1719,6 @@ declare module 'vscode' { */ export type DocumentSelector = string | DocumentFilter | (string | DocumentFilter)[]; - /** * A provider result represents the values a provider, like the [`HoverProvider`](#HoverProvider), * may return. For once this is the actual result type `T`, like `Hover`, or a thenable that resolves @@ -2087,6 +2186,11 @@ declare module 'vscode' { * skip the [location](#SymbolInformation.location) of symbols and implement `resolveWorkspaceSymbol` to do that * later. * + * The `query`-parameter should be interpreted in a *relaxed way* as the editor will apply its own highlighting + * and scoring on the results. A good rule of thumb is to match case-insensitive and to simply check that the + * characters of *query* appear in their order in a candidate symbol. Don't use prefix, substring, or similar + * strict matching. + * * @param query A non-empty query string. * @param token A cancellation token. * @return An array of document highlights or a thenable that resolves to such. The lack of a result can be @@ -2718,6 +2822,40 @@ declare module 'vscode' { constructor(items?: CompletionItem[], isIncomplete?: boolean); } + /** + * How a [completion provider](#CompletionItemProvider) was triggered + */ + export enum CompletionTriggerKind { + /** + * Completion was triggered normally. + */ + Invoke = 0, + /** + * Completion was triggered by a trigger character. + */ + TriggerCharacter = 1 + } + + /** + * Contains additional information about the context in which + * [completion provider](#CompletionItemProvider.provideCompletionItems) is triggered. + */ + export interface CompletionContext { + /** + * How the completion was triggered. + */ + readonly triggerKind: CompletionTriggerKind; + + /** + * Character that triggered the completion item provider. + * + * `undefined` if provider was not triggered by a character. + * + * The trigger character is already in the document when the completion provider is triggered. + */ + readonly triggerCharacter?: string; + } + /** * The completion item provider interface defines the contract between extensions and * [IntelliSense](https://code.visualstudio.com/docs/editor/intellisense). @@ -2740,10 +2878,12 @@ declare module 'vscode' { * @param document The document in which the command was invoked. * @param position The position at which the command was invoked. * @param token A cancellation token. + * @param context How the completion was triggered. + * * @return An array of completions, a [completion list](#CompletionList), or a thenable that resolves to either. * The lack of a result can be signaled by returning `undefined`, `null`, or an empty array. */ - provideCompletionItems(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; + provideCompletionItems(document: TextDocument, position: Position, token: CancellationToken, context: CompletionContext): ProviderResult; /** * Given a completion item fill in more data, like [doc-comment](#CompletionItem.documentation) @@ -3311,6 +3451,7 @@ declare module 'vscode' { * used to show editors side by side. */ export enum ViewColumn { + Active = -1, One = 1, Two = 2, Three = 3 @@ -3890,9 +4031,9 @@ declare module 'vscode' { export class Task { /** - * Creates a new task. + * ~~Creates a new task.~~ * - * @deprecated: Use the new constructors that allow specifying a target for the task. + * @deprecated Use the new constructors that allow specifying a target for the task. * * @param definition The task definition as defined in the taskDefinitions extension point. * @param scope The task's name. Is presented in the user interface. @@ -3917,7 +4058,7 @@ declare module 'vscode' { * or '$eslint'. Problem matchers can be contributed by an extension using * the `problemMatchers` extension point. */ - constructor(taskDefinition: TaskDefinition, target: TaskScope.Global | TaskScope.Workspace | WorkspaceFolder, name: string, source: string, execution?: ProcessExecution | ShellExecution, problemMatchers?: string | string[]); + constructor(taskDefinition: TaskDefinition, target: WorkspaceFolder | TaskScope.Global | TaskScope.Workspace, name: string, source: string, execution?: ProcessExecution | ShellExecution, problemMatchers?: string | string[]); /** * The task's definition. @@ -4404,6 +4545,22 @@ declare module 'vscode' { */ export function showQuickPick(items: T[] | Thenable, options?: QuickPickOptions, token?: CancellationToken): Thenable; + /** + * Shows a file open dialog to the user. + * + * @param options Options that control the dialog. + * @returns A promise that resolves to the selected resources or `undefined`. + */ + export function showOpenDialog(options: OpenDialogOptions): Thenable; + + /** + * Shows a file save dialog to the user. + * + * @param options Options that control the dialog. + * @returns A promise that resolves to the selected resource or `undefined`. + */ + export function showSaveDialog(options: SaveDialogOptions): Thenable; + /** * Opens an input box to ask the user for input. * @@ -4793,13 +4950,16 @@ declare module 'vscode' { export interface WorkspaceFolder { /** - * The associated URI for this workspace folder. + * The associated uri for this workspace folder. + * + * *Note:* The [Uri](#Uri)-type was intentionally chosen such that future releases of the editor can support + * workspace folders that are not stored on the local disk, e.g. `ftp://server/workspaces/foo`. */ readonly uri: Uri; /** * The name of this workspace folder. Defaults to - * the basename its [uri-path](#Uri.path) + * the basename of its [uri-path](#Uri.path) */ readonly name: string; @@ -4869,30 +5029,35 @@ declare module 'vscode' { /** * Creates a file system watcher. * - * A glob pattern that filters the file events must be provided. Optionally, flags to ignore certain - * kinds of events can be provided. To stop listening to events the watcher must be disposed. + * A glob pattern that filters the file events on their absolute path must be provided. Optionally, + * flags to ignore certain kinds of events can be provided. To stop listening to events the watcher must be disposed. * * *Note* that only files within the current [workspace folders](#workspace.workspaceFolders) can be watched. * - * @param globPattern A glob pattern that is applied to the names of created, changed, and deleted files. + * @param globPattern A [glob pattern](#GlobPattern) that is applied to the absolute paths of created, changed, + * and deleted files. Use a [relative pattern](#RelativePattern) to limit events to a certain [workspace folder](#WorkspaceFolder). * @param ignoreCreateEvents Ignore when files have been created. * @param ignoreChangeEvents Ignore when files have been changed. * @param ignoreDeleteEvents Ignore when files have been deleted. * @return A new file system watcher instance. */ - export function createFileSystemWatcher(globPattern: string, ignoreCreateEvents?: boolean, ignoreChangeEvents?: boolean, ignoreDeleteEvents?: boolean): FileSystemWatcher; + export function createFileSystemWatcher(globPattern: GlobPattern, ignoreCreateEvents?: boolean, ignoreChangeEvents?: boolean, ignoreDeleteEvents?: boolean): FileSystemWatcher; /** - * Find files in the workspace. + * Find files across all [workspace folders](#workspace.workspaceFolders) in the workspace. * * @sample `findFiles('**∕*.js', '**∕node_modules∕**', 10)` - * @param include A glob pattern that defines the files to search for. - * @param exclude A glob pattern that defines files and folders to exclude. + * @param include A [glob pattern](#GlobPattern) that defines the files to search for. The glob pattern + * will be matched against the file paths of resulting matches relative to their workspace. Use a [relative pattern](#RelativePattern) + * to restrict the search results to a [workspace folder](#WorkspaceFolder). + * @param exclude A [glob pattern](#GlobPattern) that defines files and folders to exclude. The glob pattern + * will be matched against the file paths of resulting matches relative to their workspace. * @param maxResults An upper-bound for the result. * @param token A token that can be used to signal cancellation to the underlying search engine. - * @return A thenable that resolves to an array of resource identifiers. + * @return A thenable that resolves to an array of resource identifiers. Will return no results if no + * [workspace folders](#workspace.workspaceFolders) are opened. */ - export function findFiles(include: string, exclude?: string, maxResults?: number, token?: CancellationToken): Thenable; + export function findFiles(include: GlobPattern, exclude?: GlobPattern, maxResults?: number, token?: CancellationToken): Thenable; /** * Save all dirty files. diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 9c413707718..85ca4a72cd1 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -7,35 +7,154 @@ declare module 'vscode' { - export interface OpenDialogOptions { - defaultResource?: Uri; - openLabel?: string; - openFiles?: boolean; - openFolders?: boolean; - openMany?: boolean; - } - - export interface SaveDialogOptions { - defaultResource?: Uri; - saveLabel?: string; - } - export namespace window { - export function showOpenDialog(options: OpenDialogOptions): Thenable; - export function showSaveDialog(options: SaveDialogOptions): Thenable; + + /** + * Shows a selection list of [workspace folders](#workspace.workspaceFolders) to pick from. + * Returns `undefined` if no folder is open. + * + * @param options Configures the behavior of the workspace folder list. + * @return A promise that resolves to the workspace folder or `undefined`. + */ + export function showWorkspaceFolderPick(options?: WorkspaceFolderPickOptions): Thenable; + } + + /** + * Options to configure the behaviour of the [workspace folder](#WorkspaceFolder) pick UI. + */ + export interface WorkspaceFolderPickOptions { + + /** + * An optional string to show as place holder in the input box to guide the user what to pick on. + */ + placeHolder?: string; + + /** + * Set to `true` to keep the picker open when focus moves to another part of the editor or to another window. + */ + ignoreFocusOut?: boolean; + } + + + // export enum FileErrorCodes { + // /** + // * Not owner. + // */ + // EPERM = 1, + // /** + // * No such file or directory. + // */ + // ENOENT = 2, + // /** + // * I/O error. + // */ + // EIO = 5, + // /** + // * Permission denied. + // */ + // EACCES = 13, + // /** + // * File exists. + // */ + // EEXIST = 17, + // /** + // * Not a directory. + // */ + // ENOTDIR = 20, + // /** + // * Is a directory. + // */ + // EISDIR = 21, + // /** + // * File too large. + // */ + // EFBIG = 27, + // /** + // * No space left on device. + // */ + // ENOSPC = 28, + // /** + // * Directory is not empty. + // */ + // ENOTEMPTY = 66, + // /** + // * Invalid file handle. + // */ + // ESTALE = 70, + // /** + // * Illegal NFS file handle. + // */ + // EBADHANDLE = 10001, + // } + + export enum FileChangeType { + Updated = 0, + Added = 1, + Deleted = 2 + } + + export interface FileChange { + type: FileChangeType; + resource: Uri; + } + + export enum FileType { + File = 0, + Dir = 1, + Symlink = 2 + } + + export interface FileStat { + id: number | string; + mtime: number; + // atime: number; + size: number; + type: FileType; } // todo@joh discover files etc export interface FileSystemProvider { - // todo@joh -> added, deleted, renamed, changed - onDidChange: Event; - resolveContents(resource: Uri): string | Thenable; - writeContents(resource: Uri, contents: string): void | Thenable; + onDidChange?: Event; - // -- search - // todo@joh - extract into its own provider? - findFiles(query: string, progress: Progress, token?: CancellationToken): Thenable; + root: Uri; + + // more... + // + utimes(resource: Uri, mtime: number, atime: number): Thenable; + + stat(resource: Uri): Thenable; + + read(resource: Uri, offset: number, length: number, progress: Progress): Thenable; + + // todo@remote + // offset - byte offset to start + // count - number of bytes to write + // Thenable - number of bytes actually written + write(resource: Uri, content: Uint8Array): Thenable; + + // todo@remote + // Thenable + move(resource: Uri, target: Uri): Thenable; + + // todo@remote + // helps with performance bigly + // copy?(from: Uri, to: Uri): Thenable; + + // todo@remote + // Thenable + mkdir(resource: Uri): Thenable; + + readdir(resource: Uri): Thenable<[Uri, FileStat][]>; + + // todo@remote + // ? merge both + // ? recursive del + rmdir(resource: Uri): Thenable; + unlink(resource: Uri): Thenable; + + // todo@remote + // create(resource: Uri): Thenable; } export namespace workspace { @@ -147,6 +266,13 @@ declare module 'vscode' { * selecting this color presentation. Edits must not overlap with the main [edit](#ColorPresentation.textEdit) nor with themselves. */ additionalTextEdits?: TextEdit[]; + + /** + * Creates a new color presentation. + * + * @param label The label of this color presentation. + */ + constructor(label: string); } /** @@ -166,7 +292,7 @@ declare module 'vscode' { /** * Provide representations for a color. */ - provideColorPresentations(colorInfo: ColorInformation, token: CancellationToken): ProviderResult; + provideColorPresentations(document: TextDocument, colorInfo: ColorInformation, token: CancellationToken): ProviderResult; } export namespace languages { diff --git a/src/vs/workbench/api/electron-browser/extensionHost.contribution.ts b/src/vs/workbench/api/electron-browser/extensionHost.contribution.ts index 746d242419b..be065d33308 100644 --- a/src/vs/workbench/api/electron-browser/extensionHost.contribution.ts +++ b/src/vs/workbench/api/electron-browser/extensionHost.contribution.ts @@ -28,6 +28,7 @@ import './mainThreadEditor'; import './mainThreadEditors'; import './mainThreadErrors'; import './mainThreadExtensionService'; +import './mainThreadFileSystem'; import './mainThreadFileSystemEventService'; import './mainThreadHeapService'; import './mainThreadLanguageFeatures'; diff --git a/src/vs/workbench/api/electron-browser/mainThreadDialogs.ts b/src/vs/workbench/api/electron-browser/mainThreadDialogs.ts index 1ec24d28977..b79710c297b 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadDialogs.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadDialogs.ts @@ -9,6 +9,7 @@ import { isFalsyOrEmpty } from 'vs/base/common/arrays'; import { MainThreadDiaglogsShape, MainContext, IExtHostContext, MainThreadDialogOpenOptions, MainThreadDialogSaveOptions } from '../node/extHost.protocol'; import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; import { IWindowService } from 'vs/platform/windows/common/windows'; +import { forEach } from 'vs/base/common/collections'; @extHostNamedCustomer(MainContext.MainThreadDialogs) export class MainThreadDialogs implements MainThreadDiaglogsShape { @@ -26,7 +27,7 @@ export class MainThreadDialogs implements MainThreadDiaglogsShape { $showOpenDialog(options: MainThreadDialogOpenOptions): TPromise { // TODO@joh what about remote dev setup? - if (options.defaultResource && options.defaultResource.scheme !== 'file') { + if (options.defaultUri && options.defaultUri.scheme !== 'file') { return TPromise.wrapError(new Error('bad path')); } return new TPromise(resolve => { @@ -39,7 +40,7 @@ export class MainThreadDialogs implements MainThreadDiaglogsShape { $showSaveDialog(options: MainThreadDialogSaveOptions): TPromise { // TODO@joh what about remote dev setup? - if (options.defaultResource && options.defaultResource.scheme !== 'file') { + if (options.defaultUri && options.defaultUri.scheme !== 'file') { return TPromise.wrapError(new Error('bad path')); } return new TPromise(resolve => { @@ -57,8 +58,8 @@ export class MainThreadDialogs implements MainThreadDiaglogsShape { if (options.openLabel) { result.buttonLabel = options.openLabel; } - if (options.defaultResource) { - result.defaultPath = options.defaultResource.fsPath; + if (options.defaultUri) { + result.defaultPath = options.defaultUri.fsPath; } if (!options.openFiles && !options.openFolders) { options.openFiles = true; @@ -72,6 +73,10 @@ export class MainThreadDialogs implements MainThreadDiaglogsShape { if (options.openMany) { result.properties.push('multiSelections'); } + if (options.filters) { + result.filters = []; + forEach(options.filters, entry => result.filters.push({ name: entry.key, extensions: entry.value })); + } return result; } @@ -79,12 +84,16 @@ export class MainThreadDialogs implements MainThreadDiaglogsShape { const result: Electron.SaveDialogOptions = { }; - if (options.defaultResource) { - result.defaultPath = options.defaultResource.fsPath; + if (options.defaultUri) { + result.defaultPath = options.defaultUri.fsPath; } if (options.saveLabel) { result.buttonLabel = options.saveLabel; } + if (options.filters) { + result.filters = []; + forEach(options.filters, entry => result.filters.push({ name: entry.key, extensions: entry.value })); + } return result; } } diff --git a/src/vs/workbench/api/electron-browser/mainThreadFileSystem.ts b/src/vs/workbench/api/electron-browser/mainThreadFileSystem.ts new file mode 100644 index 00000000000..76aac923806 --- /dev/null +++ b/src/vs/workbench/api/electron-browser/mainThreadFileSystem.ts @@ -0,0 +1,118 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import URI from 'vs/base/common/uri'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { ExtHostContext, MainContext, IExtHostContext, MainThreadFileSystemShape, ExtHostFileSystemShape } from '../node/extHost.protocol'; +import { IFileService, IFileSystemProvider, IStat, IFileChange } from 'vs/platform/files/common/files'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import Event, { Emitter } from 'vs/base/common/event'; +import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; +import { IProgress } from 'vs/platform/progress/common/progress'; +import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing'; + +@extHostNamedCustomer(MainContext.MainThreadFileSystem) +export class MainThreadFileSystem implements MainThreadFileSystemShape { + + private readonly _toDispose: IDisposable[] = []; + private readonly _proxy: ExtHostFileSystemShape; + private readonly _provider = new Map(); + + constructor( + extHostContext: IExtHostContext, + @IFileService private readonly _fileService: IFileService, + @IWorkspaceEditingService private readonly _workspaceEditService: IWorkspaceEditingService + ) { + this._proxy = extHostContext.get(ExtHostContext.ExtHostFileSystem); + } + + dispose(): void { + dispose(this._toDispose); + } + + $registerFileSystemProvider(handle: number, scheme: string): void { + this._provider.set(handle, new RemoteFileSystemProvider(this._fileService, scheme, handle, this._proxy)); + } + + $unregisterFileSystemProvider(handle: number): void { + dispose(this._provider.get(handle)); + this._provider.delete(handle); + } + + $onDidAddFileSystemRoot(uri: URI): void { + this._workspaceEditService.addFolders([uri]); + } + + $onFileSystemChange(handle: number, changes: IFileChange[]): void { + this._provider.get(handle).$onFileSystemChange(changes); + } + + $reportFileChunk(handle: number, resource: URI, chunk: number[]): void { + this._provider.get(handle).reportFileChunk(resource, chunk); + } +} + +class RemoteFileSystemProvider implements IFileSystemProvider { + + private readonly _onDidChange = new Emitter(); + private readonly _registration: IDisposable; + private readonly _reads = new Map>(); + + readonly onDidChange: Event = this._onDidChange.event; + + + constructor( + service: IFileService, + scheme: string, + private readonly _handle: number, + private readonly _proxy: ExtHostFileSystemShape + ) { + this._registration = service.registerProvider(scheme, this); + } + + dispose(): void { + this._registration.dispose(); + this._onDidChange.dispose(); + } + + $onFileSystemChange(changes: IFileChange[]): void { + this._onDidChange.fire(changes); + } + + // --- forwarding calls + + utimes(resource: URI, mtime: number, atime: number): TPromise { + return this._proxy.$utimes(this._handle, resource, mtime, atime); + } + stat(resource: URI): TPromise { + return this._proxy.$stat(this._handle, resource); + } + read(resource: URI, offset: number, count: number, progress: IProgress): TPromise { + this._reads.set(resource.toString(), progress); + return this._proxy.$read(this._handle, offset, count, resource); + } + reportFileChunk(resource: URI, chunk: number[]): void { + this._reads.get(resource.toString()).report(Buffer.from(chunk)); + } + write(resource: URI, content: Uint8Array): TPromise { + return this._proxy.$write(this._handle, resource, [].slice.call(content)); + } + unlink(resource: URI): TPromise { + return this._proxy.$unlink(this._handle, resource); + } + move(resource: URI, target: URI): TPromise { + return this._proxy.$move(this._handle, resource, target); + } + mkdir(resource: URI): TPromise { + return this._proxy.$mkdir(this._handle, resource); + } + readdir(resource: URI): TPromise<[URI, IStat][], any> { + return this._proxy.$readdir(this._handle, resource); + } + rmdir(resource: URI): TPromise { + return this._proxy.$rmdir(this._handle, resource); + } +} diff --git a/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts index 9a2428c031d..757bab38cea 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts @@ -233,8 +233,8 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha this._registrations[handle] = modes.SuggestRegistry.register(selector, { triggerCharacters, - provideCompletionItems: (model: IReadOnlyModel, position: EditorPosition, token: CancellationToken): Thenable => { - return wireCancellationToken(token, this._proxy.$provideCompletionItems(handle, model.uri, position)).then(result => { + provideCompletionItems: (model: IReadOnlyModel, position: EditorPosition, context: modes.SuggestContext, token: CancellationToken): Thenable => { + return wireCancellationToken(token, this._proxy.$provideCompletionItems(handle, model.uri, position, context)).then(result => { if (!result) { return result; } @@ -292,9 +292,9 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha return documentColors.map(documentColor => { const [red, green, blue, alpha] = documentColor.color; const color = { - red: red / 255.0, - green: green / 255.0, - blue: blue / 255.0, + red: red, + green: green, + blue: blue, alpha }; @@ -306,8 +306,8 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha }); }, - provideColorPresentations: (colorInfo, token) => { - return wireCancellationToken(token, proxy.$provideColorPresentations(handle, { + provideColorPresentations: (model, colorInfo, token) => { + return wireCancellationToken(token, proxy.$provideColorPresentations(handle, model.uri, { color: [colorInfo.color.red, colorInfo.color.green, colorInfo.color.blue, colorInfo.color.alpha], range: colorInfo.range })); diff --git a/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts b/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts index f72611dd369..12777a683c8 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts @@ -6,17 +6,16 @@ import { isPromiseCanceledError } from 'vs/base/common/errors'; import URI from 'vs/base/common/uri'; -import { ISearchService, QueryType, ISearchQuery, ISearchProgressItem, ISearchComplete } from 'vs/platform/search/common/search'; +import { ISearchService, QueryType, ISearchQuery, IFolderQuery } from 'vs/platform/search/common/search'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; -import { TPromise, PPromise } from 'vs/base/common/winjs.base'; +import { TPromise } from 'vs/base/common/winjs.base'; import { MainThreadWorkspaceShape, ExtHostWorkspaceShape, ExtHostContext, MainContext, IExtHostContext } from '../node/extHost.protocol'; import { IFileService } from 'vs/platform/files/common/files'; -import { IDisposable, dispose, combinedDisposable } from 'vs/base/common/lifecycle'; -import { RemoteFileService } from 'vs/workbench/services/files/electron-browser/remoteFileService'; -import { Emitter } from 'vs/base/common/event'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; import { IExperimentService } from 'vs/platform/telemetry/common/experiments'; +import { IRelativePattern } from 'vs/base/common/glob'; @extHostNamedCustomer(MainContext.MainThreadWorkspace) export class MainThreadWorkspace implements MainThreadWorkspaceShape { @@ -30,11 +29,12 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape { @ISearchService private readonly _searchService: ISearchService, @IWorkspaceContextService private readonly _contextService: IWorkspaceContextService, @ITextFileService private readonly _textFileService: ITextFileService, - @IExperimentService private experimentService: IExperimentService, + @IExperimentService private _experimentService: IExperimentService, @IFileService private readonly _fileService: IFileService ) { this._proxy = extHostContext.get(ExtHostContext.ExtHostWorkspace); - this._contextService.onDidChangeWorkbenchState(this._onDidChangeWorkspaceState, this, this._toDispose); + this._contextService.onDidChangeWorkspaceFolders(this._onDidChangeWorkspace, this, this._toDispose); + this._contextService.onDidChangeWorkbenchState(this._onDidChangeWorkspace, this, this._toDispose); } dispose(): void { @@ -48,24 +48,32 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape { // --- workspace --- - private _onDidChangeWorkspaceState(): void { + private _onDidChangeWorkspace(): void { this._proxy.$acceptWorkspaceData(this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? null : this._contextService.getWorkspace()); } // --- search --- - $startSearch(include: string, exclude: string, maxResults: number, requestId: number): Thenable { + $startSearch(include: string | IRelativePattern, exclude: string | IRelativePattern, maxResults: number, requestId: number): Thenable { const workspace = this._contextService.getWorkspace(); if (!workspace.folders.length) { return undefined; } + + let folderQueries: IFolderQuery[]; + if (typeof include === 'string' || !include) { + folderQueries = workspace.folders.map(folder => ({ folder: folder.uri })); // absolute pattern: search across all folders + } else { + folderQueries = [{ folder: URI.file(include.base) }]; // relative pattern: search only in base folder + } + const query: ISearchQuery = { - folderQueries: workspace.folders.map(folder => ({ folder: folder.uri })), + folderQueries, type: QueryType.File, maxResults, - includePattern: { [include]: true }, - excludePattern: { [exclude]: true }, - useRipgrep: this.experimentService.getExperiments().ripgrepQuickSearch + includePattern: { [typeof include === 'string' ? include : !!include ? include.pattern : undefined]: true }, + excludePattern: { [typeof exclude === 'string' ? exclude : !!exclude ? exclude.pattern : undefined]: true }, + useRipgrep: this._experimentService.getExperiments().ripgrepQuickSearch }; this._searchService.extendQuery(query); @@ -102,81 +110,5 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape { return result.results.every(each => each.success === true); }); } - - // --- EXPERIMENT: workspace provider - - private _idPool: number = 0; - private readonly _provider = new Map]>(); - private readonly _searchSessions = new Map void, reject: Function, progress: (item: ISearchProgressItem) => void, matches: URI[] }>(); - - $registerFileSystemProvider(handle: number, authority: string): void { - if (!(this._fileService instanceof RemoteFileService)) { - throw new Error(); - } - const emitter = new Emitter(); - const provider = { - onDidChange: emitter.event, - resolve: (resource: URI) => { - return this._proxy.$resolveFile(handle, resource); - }, - update: (resource: URI, value: string) => { - return this._proxy.$storeFile(handle, resource, value); - } - }; - const searchProvider = { - search: (query: ISearchQuery) => { - if (query.type !== QueryType.File) { - return undefined; - } - const session = ++this._idPool; - return new PPromise((resolve, reject, progress) => { - this._searchSessions.set(session, { resolve, reject, progress, matches: [] }); - this._proxy.$startSearch(handle, session, query.filePattern); - }, () => { - this._proxy.$cancelSearch(handle, session); - }); - } - }; - const registrations = combinedDisposable([ - this._fileService.registerProvider(authority, provider), - this._searchService.registerSearchResultProvider(searchProvider), - ]); - this._provider.set(handle, [registrations, emitter]); - } - - $unregisterFileSystemProvider(handle: number): void { - if (this._provider.has(handle)) { - dispose(this._provider.get(handle)[0]); - this._provider.delete(handle); - } - } - - $onFileSystemChange(handle: number, resource: URI) { - const [, emitter] = this._provider.get(handle); - emitter.fire(resource); - }; - - $updateSearchSession(session: number, data: URI): void { - if (this._searchSessions.has(session)) { - this._searchSessions.get(session).progress({ resource: data }); - this._searchSessions.get(session).matches.push(data); - } - } - - $finishSearchSession(session: number, err?: any): void { - if (this._searchSessions.has(session)) { - const { matches, resolve, reject } = this._searchSessions.get(session); - this._searchSessions.delete(session); - if (err) { - reject(err); - } else { - resolve({ - limitHit: false, - stats: undefined, - results: matches.map(resource => ({ resource })) - }); - } - } - } } diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index 215451c68f6..90df60d5188 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -5,7 +5,7 @@ 'use strict'; import { Emitter } from 'vs/base/common/event'; -import { TrieMap } from 'vs/base/common/map'; +import { StringTrieMap } from 'vs/base/common/map'; import { score } from 'vs/editor/common/modes/languageSelector'; import * as Platform from 'vs/base/common/platform'; import * as errors from 'vs/base/common/errors'; @@ -53,6 +53,8 @@ import { TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions'; import { ExtHostThreadService } from 'vs/workbench/services/thread/node/extHostThreadService'; import { ProxyIdentifier } from 'vs/workbench/services/thread/common/threadService'; import { ExtHostDialogs } from 'vs/workbench/api/node/extHostDialogs'; +import { ExtHostFileSystem } from 'vs/workbench/api/node/extHostFileSystem'; +import { FileChangeType, FileType } from 'vs/platform/files/common/files'; export interface IExtensionApiFactory { (extension: IExtensionDescription): typeof vscode; @@ -93,8 +95,9 @@ export function createApiFactory( const extHostConfiguration = threadService.set(ExtHostContext.ExtHostConfiguration, new ExtHostConfiguration(threadService.get(MainContext.MainThreadConfiguration), extHostWorkspace, initData.configuration)); const extHostDiagnostics = threadService.set(ExtHostContext.ExtHostDiagnostics, new ExtHostDiagnostics(threadService)); const languageFeatures = threadService.set(ExtHostContext.ExtHostLanguageFeatures, new ExtHostLanguageFeatures(threadService, extHostDocuments, extHostCommands, extHostHeapService, extHostDiagnostics)); + const extHostFileSystem = threadService.set(ExtHostContext.ExtHostFileSystem, new ExtHostFileSystem(threadService)); const extHostFileSystemEvent = threadService.set(ExtHostContext.ExtHostFileSystemEventService, new ExtHostFileSystemEventService()); - const extHostQuickOpen = threadService.set(ExtHostContext.ExtHostQuickOpen, new ExtHostQuickOpen(threadService)); + const extHostQuickOpen = threadService.set(ExtHostContext.ExtHostQuickOpen, new ExtHostQuickOpen(threadService, extHostWorkspace, extHostCommands)); const extHostTerminalService = threadService.set(ExtHostContext.ExtHostTerminalService, new ExtHostTerminalService(threadService)); const extHostSCM = threadService.set(ExtHostContext.ExtHostSCM, new ExtHostSCM(threadService, extHostCommands)); const extHostTask = threadService.set(ExtHostContext.ExtHostTask, new ExtHostTask(threadService, extHostWorkspace)); @@ -347,9 +350,18 @@ export function createApiFactory( showQuickPick(items: any, options: vscode.QuickPickOptions, token?: vscode.CancellationToken) { return extHostQuickOpen.showQuickPick(items, options, token); }, + showWorkspaceFolderPick: proposedApiFunction(extension, (options: vscode.WorkspaceFolderPickOptions) => { + return extHostQuickOpen.showWorkspaceFolderPick(options); + }), showInputBox(options?: vscode.InputBoxOptions, token?: vscode.CancellationToken) { return extHostQuickOpen.showInput(options, token); }, + showOpenDialog(options) { + return extHostDialogs.showOpenDialog(options); + }, + showSaveDialog(options) { + return extHostDialogs.showSaveDialog(options); + }, createStatusBarItem(position?: vscode.StatusBarAlignment, priority?: number): vscode.StatusBarItem { return extHostStatusBar.createStatusBarEntry(extension.id, position, priority); }, @@ -379,12 +391,6 @@ export function createApiFactory( sampleFunction: proposedApiFunction(extension, () => { return extHostMessageService.showMessage(extension, Severity.Info, 'Hello Proposed Api!', {}, []); }), - showOpenDialog: proposedApiFunction(extension, options => { - return extHostDialogs.showOpenDialog(options); - }), - showSaveDialog: proposedApiFunction(extension, options => { - return extHostDialogs.showSaveDialog(options); - }) }; // namespace: workspace @@ -397,6 +403,7 @@ export function createApiFactory( throw errors.readonly(); }, getWorkspaceFolder(resource) { + apiUsage.publicLog('workspace#getWorkspaceFolder'); return extHostWorkspace.getWorkspaceFolder(resource); }, get workspaceFolders() { @@ -432,12 +439,12 @@ export function createApiFactory( let uriPromise: TPromise; let options = uriOrFileNameOrOptions as { language?: string; content?: string; }; - if (!options || typeof options.language === 'string') { - uriPromise = extHostDocuments.createDocumentData(options); - } else if (typeof uriOrFileNameOrOptions === 'string') { + if (typeof uriOrFileNameOrOptions === 'string') { uriPromise = TPromise.as(URI.file(uriOrFileNameOrOptions)); } else if (uriOrFileNameOrOptions instanceof URI) { uriPromise = TPromise.as(uriOrFileNameOrOptions); + } else if (!options || typeof options === 'object') { + uriPromise = extHostDocuments.createDocumentData(options); } else { throw new Error('illegal argument - uriOrFileNameOrOptions'); } @@ -477,7 +484,7 @@ export function createApiFactory( return extHostTask.registerTaskProvider(extension, provider); }, registerFileSystemProvider: proposedApiFunction(extension, (authority, provider) => { - return extHostWorkspace.registerFileSystemProvider(authority, provider); + return extHostFileSystem.registerFileSystemProvider(authority, provider); }) }; @@ -557,6 +564,7 @@ export function createApiFactory( CompletionItem: extHostTypes.CompletionItem, CompletionItemKind: extHostTypes.CompletionItemKind, CompletionList: extHostTypes.CompletionList, + CompletionTriggerKind: extHostTypes.CompletionTriggerKind, Diagnostic: extHostTypes.Diagnostic, DiagnosticSeverity: extHostTypes.DiagnosticSeverity, Disposable: extHostTypes.Disposable, @@ -586,7 +594,7 @@ export function createApiFactory( TextEditorRevealType: extHostTypes.TextEditorRevealType, TextEditorSelectionChangeKind: extHostTypes.TextEditorSelectionChangeKind, DecorationRangeBehavior: extHostTypes.DecorationRangeBehavior, - Uri: URI, + Uri: URI, ViewColumn: extHostTypes.ViewColumn, WorkspaceEdit: extHostTypes.WorkspaceEdit, ProgressLocation: extHostTypes.ProgressLocation, @@ -601,7 +609,12 @@ export function createApiFactory( ShellExecution: extHostTypes.ShellExecution, TaskScope: extHostTypes.TaskScope, Task: extHostTypes.Task, - ConfigurationTarget: extHostTypes.ConfigurationTarget + ConfigurationTarget: extHostTypes.ConfigurationTarget, + RelativePattern: extHostTypes.RelativePattern, + + // TODO@JOH,remote + FileChangeType: FileChangeType, + FileType: FileType }; if (extension.enableProposedApi && extension.isBuiltin) { api['credentials'] = credentials; @@ -642,7 +655,7 @@ export function initializeExtensionApi(extensionService: ExtHostExtensionService return extensionService.getExtensionPathIndex().then(trie => defineAPI(apiFactory, trie)); } -function defineAPI(factory: IExtensionApiFactory, extensionPaths: TrieMap): void { +function defineAPI(factory: IExtensionApiFactory, extensionPaths: StringTrieMap): void { // each extension is meant to get its own api implementation const extApiImpl = new Map(); diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 20dc029fe9a..1a04c2f8dd0 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -47,7 +47,9 @@ import { ITreeItem } from 'vs/workbench/common/views'; import { ThemeColor } from 'vs/platform/theme/common/themeService'; import { IDisposable } from 'vs/base/common/lifecycle'; import { SerializedError } from 'vs/base/common/errors'; -import { WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { IRelativePattern } from 'vs/base/common/glob'; +import { IWorkspaceFolderData } from 'vs/platform/workspace/common/workspace'; +import { IStat, IFileChange } from 'vs/platform/files/common/files'; export interface IEnvironment { isExtensionDevelopmentDebug: boolean; @@ -64,7 +66,7 @@ export interface IEnvironment { export interface IWorkspaceData { id: string; name: string; - folders: WorkspaceFolder[]; + folders: IWorkspaceFolderData[]; } export interface IInitData { @@ -115,16 +117,18 @@ export interface MainThreadDiagnosticsShape extends IDisposable { } export interface MainThreadDialogOpenOptions { - defaultResource?: URI; + defaultUri?: URI; openLabel?: string; openFiles?: boolean; openFolders?: boolean; openMany?: boolean; + filters: { [name: string]: string[] }; } export interface MainThreadDialogSaveOptions { - defaultResource?: URI; + defaultUri?: URI; saveLabel?: string; + filters: { [name: string]: string[] }; } export interface MainThreadDiaglogsShape extends IDisposable { @@ -308,15 +312,18 @@ export interface MainThreadTelemetryShape extends IDisposable { } export interface MainThreadWorkspaceShape extends IDisposable { - $startSearch(include: string, exclude: string, maxResults: number, requestId: number): Thenable; + $startSearch(include: string | IRelativePattern, exclude: string | IRelativePattern, maxResults: number, requestId: number): Thenable; $cancelSearch(requestId: number): Thenable; $saveAll(includeUntitled?: boolean): Thenable; +} - $registerFileSystemProvider(handle: number, authority: string): void; +export interface MainThreadFileSystemShape extends IDisposable { + $registerFileSystemProvider(handle: number, scheme: string): void; $unregisterFileSystemProvider(handle: number): void; - $onFileSystemChange(handle: number, resource: URI): void; - $updateSearchSession(session: number, data): void; - $finishSearchSession(session: number, err?: any): void; + + $onDidAddFileSystemRoot(root: URI): void; + $onFileSystemChange(handle: number, resource: IFileChange[]): void; + $reportFileChunk(handle: number, resource: URI, chunk: number[] | null): void; } export interface MainThreadTaskShape extends IDisposable { @@ -470,11 +477,18 @@ export interface ExtHostTreeViewsShape { export interface ExtHostWorkspaceShape { $acceptWorkspaceData(workspace: IWorkspaceData): void; +} - $resolveFile(handle: number, resource: URI): TPromise; - $storeFile(handle: number, resource: URI, content: string): TPromise; - $startSearch(handle: number, session: number, query: string): void; - $cancelSearch(handle: number, session: number): void; +export interface ExtHostFileSystemShape { + $utimes(handle: number, resource: URI, mtime: number, atime: number): TPromise; + $stat(handle: number, resource: URI): TPromise; + $read(handle: number, offset: number, count: number, resource: URI): TPromise; + $write(handle: number, resource: URI, content: number[]): TPromise; + $unlink(handle: number, resource: URI): TPromise; + $move(handle: number, resource: URI, target: URI): TPromise; + $mkdir(handle: number, resource: URI): TPromise; + $readdir(handle: number, resource: URI): TPromise<[URI, IStat][]>; + $rmdir(handle: number, resource: URI): TPromise; } export interface ExtHostExtensionServiceShape { @@ -541,14 +555,14 @@ export interface ExtHostLanguageFeaturesShape { $provideWorkspaceSymbols(handle: number, search: string): TPromise; $resolveWorkspaceSymbol(handle: number, symbol: modes.SymbolInformation): TPromise; $provideRenameEdits(handle: number, resource: URI, position: IPosition, newName: string): TPromise; - $provideCompletionItems(handle: number, resource: URI, position: IPosition): TPromise; + $provideCompletionItems(handle: number, resource: URI, position: IPosition, context: modes.SuggestContext): TPromise; $resolveCompletionItem(handle: number, resource: URI, position: IPosition, suggestion: modes.ISuggestion): TPromise; $releaseCompletionItems(handle: number, id: number): void; $provideSignatureHelp(handle: number, resource: URI, position: IPosition): TPromise; $provideDocumentLinks(handle: number, resource: URI): TPromise; $resolveDocumentLink(handle: number, link: modes.ILink): TPromise; $provideDocumentColors(handle: number, resource: URI): TPromise; - $provideColorPresentations(handle: number, colorInfo: IRawColorInfo): TPromise; + $provideColorPresentations(handle: number, resource: URI, colorInfo: IRawColorInfo): TPromise; } export interface ExtHostQuickOpenShape { @@ -611,6 +625,7 @@ export const MainContext = { MainThreadTelemetry: createMainId('MainThreadTelemetry'), MainThreadTerminalService: createMainId('MainThreadTerminalService'), MainThreadWorkspace: createMainId('MainThreadWorkspace'), + MainThreadFileSystem: createMainId('MainThreadFileSystem'), MainThreadExtensionService: createMainId('MainThreadExtensionService'), MainThreadSCM: createMainId('MainThreadSCM'), MainThreadTask: createMainId('MainThreadTask'), @@ -629,6 +644,7 @@ export const ExtHostContext = { ExtHostDocumentSaveParticipant: createExtId('ExtHostDocumentSaveParticipant'), ExtHostEditors: createExtId('ExtHostEditors'), ExtHostTreeViews: createExtId('ExtHostTreeViews'), + ExtHostFileSystem: createExtId('ExtHostFileSystem'), ExtHostFileSystemEventService: createExtId('ExtHostFileSystemEventService'), ExtHostHeapService: createExtId('ExtHostHeapMonitor'), ExtHostLanguageFeatures: createExtId('ExtHostLanguageFeatures'), diff --git a/src/vs/workbench/api/node/extHostDialogs.ts b/src/vs/workbench/api/node/extHostDialogs.ts index 48af26e7928..fc0bc2b807c 100644 --- a/src/vs/workbench/api/node/extHostDialogs.ts +++ b/src/vs/workbench/api/node/extHostDialogs.ts @@ -17,13 +17,13 @@ export class ExtHostDialogs { } showOpenDialog(options: vscode.OpenDialogOptions): Thenable { - return this._proxy.$showOpenDialog(options).then(filepaths => { + return this._proxy.$showOpenDialog(options).then(filepaths => { return filepaths && filepaths.map(URI.file); }); } showSaveDialog(options: vscode.SaveDialogOptions): Thenable { - return this._proxy.$showSaveDialog(options).then(filepath => { + return this._proxy.$showSaveDialog(options).then(filepath => { return filepath && URI.file(filepath); }); } diff --git a/src/vs/workbench/api/node/extHostExtensionService.ts b/src/vs/workbench/api/node/extHostExtensionService.ts index f13519818d8..d8ade5408f0 100644 --- a/src/vs/workbench/api/node/extHostExtensionService.ts +++ b/src/vs/workbench/api/node/extHostExtensionService.ts @@ -18,7 +18,7 @@ import { IExtensionMemento, ExtensionsActivator, ActivatedExtension, IExtensionA import { Barrier } from 'vs/workbench/services/extensions/node/barrier'; import { ExtHostThreadService } from 'vs/workbench/services/thread/node/extHostThreadService'; import { realpath } from 'fs'; -import { TrieMap } from 'vs/base/common/map'; +import { StringTrieMap } from 'vs/base/common/map'; class ExtensionMemento implements IExtensionMemento { @@ -116,7 +116,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { private readonly _storagePath: ExtensionStoragePath; private readonly _proxy: MainThreadExtensionServiceShape; private _activator: ExtensionsActivator; - private _extensionPathIndex: TPromise>; + private _extensionPathIndex: TPromise>; /** * This class is constructed manually because it is a service, so it doesn't use any ctor injection */ @@ -204,9 +204,9 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { } // create trie to enable fast 'filename -> extension id' look up - public getExtensionPathIndex(): TPromise> { + public getExtensionPathIndex(): TPromise> { if (!this._extensionPathIndex) { - const trie = new TrieMap(); + const trie = new StringTrieMap(); const extensions = this.getAllExtensionDescriptions().map(ext => { if (!ext.main) { return undefined; diff --git a/src/vs/workbench/api/node/extHostFileSystem.ts b/src/vs/workbench/api/node/extHostFileSystem.ts new file mode 100644 index 00000000000..6b7466381f5 --- /dev/null +++ b/src/vs/workbench/api/node/extHostFileSystem.ts @@ -0,0 +1,77 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import URI from 'vs/base/common/uri'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { MainContext, IMainContext, ExtHostFileSystemShape, MainThreadFileSystemShape } from './extHost.protocol'; +import * as vscode from 'vscode'; +import { IStat } from 'vs/platform/files/common/files'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { asWinJsPromise } from 'vs/base/common/async'; + +export class ExtHostFileSystem implements ExtHostFileSystemShape { + + private readonly _proxy: MainThreadFileSystemShape; + private readonly _provider = new Map(); + private _handlePool: number = 0; + + constructor(mainContext: IMainContext) { + this._proxy = mainContext.get(MainContext.MainThreadFileSystem); + } + + registerFileSystemProvider(scheme: string, provider: vscode.FileSystemProvider) { + const handle = this._handlePool++; + this._provider.set(handle, provider); + this._proxy.$registerFileSystemProvider(handle, scheme); + this._proxy.$onDidAddFileSystemRoot(provider.root); + let reg: IDisposable; + if (provider.onDidChange) { + reg = provider.onDidChange(event => this._proxy.$onFileSystemChange(handle, event)); + } + return { + dispose: () => { + if (reg) { + reg.dispose(); + } + this._provider.delete(handle); + this._proxy.$unregisterFileSystemProvider(handle); + } + }; + } + + $utimes(handle: number, resource: URI, mtime: number, atime: number): TPromise { + return asWinJsPromise(token => this._provider.get(handle).utimes(resource, mtime, atime)); + } + $stat(handle: number, resource: URI): TPromise { + return asWinJsPromise(token => this._provider.get(handle).stat(resource)); + } + $read(handle: number, offset: number, count: number, resource: URI): TPromise { + const progress = { + report: chunk => { + this._proxy.$reportFileChunk(handle, resource, [].slice.call(chunk)); + } + }; + return asWinJsPromise(token => this._provider.get(handle).read(resource, offset, count, progress)); + } + $write(handle: number, resource: URI, content: number[]): TPromise { + return asWinJsPromise(token => this._provider.get(handle).write(resource, Buffer.from(content))); + } + $unlink(handle: number, resource: URI): TPromise { + return asWinJsPromise(token => this._provider.get(handle).unlink(resource)); + } + $move(handle: number, resource: URI, target: URI): TPromise { + return asWinJsPromise(token => this._provider.get(handle).move(resource, target)); + } + $mkdir(handle: number, resource: URI): TPromise { + return asWinJsPromise(token => this._provider.get(handle).mkdir(resource)); + } + $readdir(handle: number, resource: URI): TPromise<[URI, IStat][], any> { + return asWinJsPromise(token => this._provider.get(handle).readdir(resource)); + } + $rmdir(handle: number, resource: URI): TPromise { + return asWinJsPromise(token => this._provider.get(handle).rmdir(resource)); + } +} diff --git a/src/vs/workbench/api/node/extHostFileSystemEventService.ts b/src/vs/workbench/api/node/extHostFileSystemEventService.ts index 6038446ffc4..1c5aa2b1e28 100644 --- a/src/vs/workbench/api/node/extHostFileSystemEventService.ts +++ b/src/vs/workbench/api/node/extHostFileSystemEventService.ts @@ -6,7 +6,7 @@ import Event, { Emitter } from 'vs/base/common/event'; import { Disposable } from './extHostTypes'; -import { parse } from 'vs/base/common/glob'; +import { parse, IRelativePattern } from 'vs/base/common/glob'; import { Uri, FileSystemWatcher as _FileSystemWatcher } from 'vscode'; import { FileSystemEvents, ExtHostFileSystemEventServiceShape } from './extHost.protocol'; @@ -30,7 +30,7 @@ class FileSystemWatcher implements _FileSystemWatcher { return Boolean(this._config & 0b100); } - constructor(dispatcher: Event, globPattern: string, ignoreCreateEvents?: boolean, ignoreChangeEvents?: boolean, ignoreDeleteEvents?: boolean) { + constructor(dispatcher: Event, globPattern: string | IRelativePattern, ignoreCreateEvents?: boolean, ignoreChangeEvents?: boolean, ignoreDeleteEvents?: boolean) { this._config = 0; if (ignoreCreateEvents) { @@ -96,7 +96,7 @@ export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServ constructor() { } - public createFileSystemWatcher(globPattern: string, ignoreCreateEvents?: boolean, ignoreChangeEvents?: boolean, ignoreDeleteEvents?: boolean): _FileSystemWatcher { + public createFileSystemWatcher(globPattern: string | IRelativePattern, ignoreCreateEvents?: boolean, ignoreChangeEvents?: boolean, ignoreDeleteEvents?: boolean): _FileSystemWatcher { return new FileSystemWatcher(this._emitter.event, globPattern, ignoreCreateEvents, ignoreChangeEvents, ignoreDeleteEvents); } diff --git a/src/vs/workbench/api/node/extHostLanguageFeatures.ts b/src/vs/workbench/api/node/extHostLanguageFeatures.ts index 6b4bfc769aa..9439c69b9b7 100644 --- a/src/vs/workbench/api/node/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/node/extHostLanguageFeatures.ts @@ -486,12 +486,14 @@ class SuggestAdapter { this._provider = provider; } - provideCompletionItems(resource: URI, position: IPosition): TPromise { + provideCompletionItems(resource: URI, position: IPosition, context: modes.SuggestContext): TPromise { const doc = this._documents.getDocumentData(resource).document; const pos = TypeConverters.toPosition(position); - return asWinJsPromise(token => this._provider.provideCompletionItems(doc, pos, token)).then(value => { + return asWinJsPromise(token => { + return this._provider.provideCompletionItems(doc, pos, token, TypeConverters.CompletionContext.from(context)); + }).then(value => { const _id = this._idPool++; @@ -729,7 +731,7 @@ class ColorProviderAdapter { }); } - provideColorPresentations(rawColorInfo: IRawColorInfo): TPromise { + provideColorPresentations(resource: URI, rawColorInfo: IRawColorInfo): TPromise { let colorInfo: vscode.ColorInformation = { range: TypeConverters.toRange(rawColorInfo.range), color: { @@ -739,7 +741,8 @@ class ColorProviderAdapter { alpha: rawColorInfo.color[3] } }; - return asWinJsPromise(token => this._provider.provideColorPresentations(colorInfo, token)).then(value => { + const doc = this._documents.getDocumentData(resource).document; + return asWinJsPromise(token => this._provider.provideColorPresentations(doc, colorInfo, token)).then(value => { return value.map(v => TypeConverters.ColorPresentation.from(v)); }); } @@ -1000,8 +1003,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { return this._createDisposable(handle); } - $provideCompletionItems(handle: number, resource: URI, position: IPosition): TPromise { - return this._withAdapter(handle, SuggestAdapter, adapter => adapter.provideCompletionItems(resource, position)); + $provideCompletionItems(handle: number, resource: URI, position: IPosition, context: modes.SuggestContext): TPromise { + return this._withAdapter(handle, SuggestAdapter, adapter => adapter.provideCompletionItems(resource, position, context)); } $resolveCompletionItem(handle: number, resource: URI, position: IPosition, suggestion: modes.ISuggestion): TPromise { @@ -1053,8 +1056,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { return this._withAdapter(handle, ColorProviderAdapter, adapter => adapter.provideColors(resource)); } - $provideColorPresentations(handle: number, colorInfo: IRawColorInfo): TPromise { - return this._withAdapter(handle, ColorProviderAdapter, adapter => adapter.provideColorPresentations(colorInfo)); + $provideColorPresentations(handle: number, resource: URI, colorInfo: IRawColorInfo): TPromise { + return this._withAdapter(handle, ColorProviderAdapter, adapter => adapter.provideColorPresentations(resource, colorInfo)); } // --- configuration diff --git a/src/vs/workbench/api/node/extHostQuickOpen.ts b/src/vs/workbench/api/node/extHostQuickOpen.ts index edaaf12385d..75acd008704 100644 --- a/src/vs/workbench/api/node/extHostQuickOpen.ts +++ b/src/vs/workbench/api/node/extHostQuickOpen.ts @@ -7,19 +7,26 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { wireCancellationToken } from 'vs/base/common/async'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { QuickPickOptions, QuickPickItem, InputBoxOptions } from 'vscode'; +import { QuickPickOptions, QuickPickItem, InputBoxOptions, WorkspaceFolderPickOptions, WorkspaceFolder } from 'vscode'; import { MainContext, MainThreadQuickOpenShape, ExtHostQuickOpenShape, MyQuickPickItems, IMainContext } from './extHost.protocol'; +import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace'; +import { ExtHostCommands } from 'vs/workbench/api/node/extHostCommands'; export type Item = string | QuickPickItem; export class ExtHostQuickOpen implements ExtHostQuickOpenShape { private _proxy: MainThreadQuickOpenShape; + private _workspace: ExtHostWorkspace; + private _commands: ExtHostCommands; + private _onDidSelectItem: (handle: number) => void; private _validateInput: (input: string) => string; - constructor(mainContext: IMainContext) { + constructor(mainContext: IMainContext, workspace: ExtHostWorkspace, commands: ExtHostCommands) { this._proxy = mainContext.get(MainContext.MainThreadQuickOpen); + this._workspace = workspace; + this._commands = commands; } showQuickPick(itemsOrItemsPromise: string[] | Thenable, options?: QuickPickOptions, token?: CancellationToken): Thenable; @@ -117,4 +124,16 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape { } return undefined; } + + // ---- workspace folder picker + + showWorkspaceFolderPick(options?: WorkspaceFolderPickOptions, token = CancellationToken.None): Thenable { + return this._commands.executeCommand('_workbench.pickWorkspaceFolder', [options]).then((folder: WorkspaceFolder) => { + if (!folder) { + return undefined; + } + + return this._workspace.getWorkspaceFolders().filter(folder => folder.uri.toString() === folder.uri.toString())[0]; + }); + } } diff --git a/src/vs/workbench/api/node/extHostTypeConverters.ts b/src/vs/workbench/api/node/extHostTypeConverters.ts index 55436407d05..81addd2688b 100644 --- a/src/vs/workbench/api/node/extHostTypeConverters.ts +++ b/src/vs/workbench/api/node/extHostTypeConverters.ts @@ -115,6 +115,8 @@ export function fromViewColumn(column?: vscode.ViewColumn): EditorPosition { editorColumn = EditorPosition.TWO; } else if (column === types.ViewColumn.Three) { editorColumn = EditorPosition.THREE; + } else if (column === types.ViewColumn.Active) { + editorColumn = undefined; } return editorColumn; } @@ -156,7 +158,7 @@ export namespace MarkdownString { } function isCodeblock(thing: any): thing is Codeblock { - return typeof thing === 'object' + return thing && typeof thing === 'object' && typeof (thing).language === 'string' && typeof (thing).value === 'string'; } @@ -305,6 +307,28 @@ export function toDocumentHighlight(occurrence: modes.DocumentHighlight): types. return new types.DocumentHighlight(toRange(occurrence.range), occurrence.kind); } +export namespace CompletionTriggerKind { + export function from(kind: modes.SuggestTriggerKind) { + switch (kind) { + case modes.SuggestTriggerKind.TriggerCharacter: + return types.CompletionTriggerKind.TriggerCharacter; + + case modes.SuggestTriggerKind.Invoke: + default: + return types.CompletionTriggerKind.Invoke; + } + } +} + +export namespace CompletionContext { + export function from(context: modes.SuggestContext): types.CompletionContext { + return { + triggerKind: CompletionTriggerKind.from(context.triggerKind), + triggerCharacter: context.triggerCharacter + }; + } +} + export const CompletionItemKind = { from(kind: types.CompletionItemKind): modes.SuggestionType { @@ -511,4 +535,4 @@ export namespace ProgressLocation { } return undefined; } -} +} \ No newline at end of file diff --git a/src/vs/workbench/api/node/extHostTypes.ts b/src/vs/workbench/api/node/extHostTypes.ts index 48930903cef..e6678bfaab4 100644 --- a/src/vs/workbench/api/node/extHostTypes.ts +++ b/src/vs/workbench/api/node/extHostTypes.ts @@ -10,6 +10,7 @@ import URI from 'vs/base/common/uri'; import { illegalArgument } from 'vs/base/common/errors'; import * as vscode from 'vscode'; import { isMarkdownString } from 'vs/base/common/htmlContent'; +import { IRelativePattern } from 'vs/base/common/glob'; export class Disposable { @@ -879,6 +880,16 @@ export class SignatureHelp { } } +export enum CompletionTriggerKind { + Invoke = 0, + TriggerCharacter = 1 +} + +export interface CompletionContext { + triggerKind: CompletionTriggerKind; + triggerCharacter: string; +} + export enum CompletionItemKind { Text = 0, Method = 1, @@ -953,6 +964,7 @@ export class CompletionList { } export enum ViewColumn { + Active = -1, One = 1, Two = 2, Three = 3 @@ -1076,6 +1088,13 @@ export class ColorPresentation { label: string; textEdit?: TextEdit; additionalTextEdits?: TextEdit[]; + + constructor(label: string) { + if (!label || typeof label !== 'string') { + throw illegalArgument('label'); + } + this.label = label; + } } export enum ColorFormat { @@ -1427,3 +1446,13 @@ export enum ConfigurationTarget { WorkspaceFolder = 3 } + +export class RelativePattern implements IRelativePattern { + base: string; + pattern: string; + + constructor(pattern: string, base: vscode.WorkspaceFolder | string) { + this.pattern = pattern; + this.base = typeof base === 'string' ? base : base.uri.fsPath; + } +} \ No newline at end of file diff --git a/src/vs/workbench/api/node/extHostWorkspace.ts b/src/vs/workbench/api/node/extHostWorkspace.ts index 629947ee2a5..1d7fd1b940f 100644 --- a/src/vs/workbench/api/node/extHostWorkspace.ts +++ b/src/vs/workbench/api/node/extHostWorkspace.ts @@ -8,17 +8,13 @@ import URI from 'vs/base/common/uri'; import Event, { Emitter } from 'vs/base/common/event'; import { normalize } from 'vs/base/common/paths'; import { delta } from 'vs/base/common/arrays'; -import { relative } from 'path'; -import { Workspace } from 'vs/platform/workspace/common/workspace'; -import { TPromise } from 'vs/base/common/winjs.base'; +import { relative, dirname } from 'path'; +import { Workspace, WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { IWorkspaceData, ExtHostWorkspaceShape, MainContext, MainThreadWorkspaceShape, IMainContext } from './extHost.protocol'; import * as vscode from 'vscode'; import { compare } from 'vs/base/common/strings'; -import { asWinJsPromise } from 'vs/base/common/async'; -import { Disposable } from 'vs/workbench/api/node/extHostTypes'; import { TrieMap } from 'vs/base/common/map'; -import { CancellationTokenSource } from 'vs/base/common/cancellation'; -import { Progress } from 'vs/platform/progress/common/progress'; +import { IRelativePattern } from 'vs/base/common/glob'; class Workspace2 extends Workspace { @@ -27,16 +23,16 @@ class Workspace2 extends Workspace { } private readonly _workspaceFolders: vscode.WorkspaceFolder[] = []; - private readonly _structure = new TrieMap(s => s.split('/')); + private readonly _structure = new TrieMap(uri => [uri.scheme, uri.authority].concat(uri.path.split('/'))); private constructor(data: IWorkspaceData) { - super(data.id, data.name, data.folders); + super(data.id, data.name, data.folders.map(folder => new WorkspaceFolder(folder))); // setup the workspace folder data structure this.folders.forEach(({ name, uri, index }) => { const workspaceFolder = { name, uri, index }; this._workspaceFolders.push(workspaceFolder); - this._structure.insert(workspaceFolder.uri.toString(), workspaceFolder); + this._structure.insert(workspaceFolder.uri, workspaceFolder); }); } @@ -45,19 +41,13 @@ class Workspace2 extends Workspace { } getWorkspaceFolder(uri: URI): vscode.WorkspaceFolder { - let str = uri.toString(); - let folder = this._structure.lookUp(str); + let folder = this._structure.lookUp(uri); if (folder) { - // `uri` is a workspace folder so we - let parts = str.split('/'); - while (parts.length) { - if (parts.pop()) { - break; - } - } - str = parts.join('/'); + // `uri` is a workspace folder so we check for + // its parent + uri = uri.with({ path: dirname(uri.path) }); } - return this._structure.findSubstr(str); + return this._structure.findSubstr(uri); } } @@ -167,7 +157,7 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape { // --- search --- - findFiles(include: string, exclude: string, maxResults?: number, token?: vscode.CancellationToken): Thenable { + findFiles(include: string | IRelativePattern, exclude: string | IRelativePattern, maxResults?: number, token?: vscode.CancellationToken): Thenable { const requestId = ExtHostWorkspace._requestIdPool++; const result = this._proxy.$startSearch(include, exclude, maxResults, requestId); if (token) { @@ -179,52 +169,4 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape { saveAll(includeUntitled?: boolean): Thenable { return this._proxy.$saveAll(includeUntitled); } - - // --- EXPERIMENT: workspace resolver - - - private _handlePool = 0; - private readonly _fsProvider = new Map(); - private readonly _searchSession = new Map(); - - registerFileSystemProvider(authority: string, provider: vscode.FileSystemProvider): vscode.Disposable { - const handle = ++this._handlePool; - this._fsProvider.set(handle, provider); - const reg = provider.onDidChange(e => this._proxy.$onFileSystemChange(handle, e)); - this._proxy.$registerFileSystemProvider(handle, authority); - return new Disposable(() => { - this._fsProvider.delete(handle); - reg.dispose(); - }); - } - - $resolveFile(handle: number, resource: URI): TPromise { - const provider = this._fsProvider.get(handle); - return asWinJsPromise(token => provider.resolveContents(resource)); - } - - $storeFile(handle: number, resource: URI, content: string): TPromise { - const provider = this._fsProvider.get(handle); - return asWinJsPromise(token => provider.writeContents(resource, content)); - } - - $startSearch(handle: number, session: number, query: string): void { - const provider = this._fsProvider.get(handle); - const source = new CancellationTokenSource(); - const progress = new Progress(chunk => this._proxy.$updateSearchSession(session, chunk)); - - this._searchSession.set(session, source); - TPromise.wrap(provider.findFiles(query, progress, source.token)).then(() => { - this._proxy.$finishSearchSession(session); - }, err => { - this._proxy.$finishSearchSession(session, err); - }); - } - - $cancelSearch(handle: number, session: number): void { - if (this._searchSession.has(session)) { - this._searchSession.get(session).cancel(); - this._searchSession.delete(session); - } - } } diff --git a/src/vs/workbench/browser/actions/workspaceActions.ts b/src/vs/workbench/browser/actions/workspaceActions.ts index 3ab372f4166..0989bcd5418 100644 --- a/src/vs/workbench/browser/actions/workspaceActions.ts +++ b/src/vs/workbench/browser/actions/workspaceActions.ts @@ -17,7 +17,7 @@ import URI from 'vs/base/common/uri'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { WORKSPACE_FILTER } from 'vs/platform/workspaces/common/workspaces'; -import { IMessageService, Severity } from 'vs/platform/message/common/message'; +import { IMessageService } from 'vs/platform/message/common/message'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { isLinux } from 'vs/base/common/platform'; import { dirname } from 'vs/base/common/paths'; @@ -203,17 +203,10 @@ export class SaveWorkspaceAsAction extends BaseWorkspacesAction { } public run(): TPromise { - const workspaceState = this.contextService.getWorkbenchState(); - if (workspaceState === WorkbenchState.EMPTY) { - this.messageService.show(Severity.Info, nls.localize('saveEmptyWorkspaceNotSupported', "Please open a workspace first to save.")); - - return TPromise.as(null); - } - const configPath = this.getNewWorkspaceConfigPath(); if (configPath) { - switch (workspaceState) { - + switch (this.contextService.getWorkbenchState()) { + case WorkbenchState.EMPTY: case WorkbenchState.FOLDER: const workspaceFolders = this.contextService.getWorkspace().folders.map(root => root.uri.fsPath); return this.workspaceEditingService.createAndEnterWorkspace(workspaceFolders, configPath); @@ -287,9 +280,9 @@ export class OpenWorkspaceConfigFileAction extends Action { } } -export const PICK_WORKSPACE_COMMAND = '_workbench.pickWorkspace'; +export const PICK_WORKSPACE_FOLDER_COMMAND = '_workbench.pickWorkspaceFolder'; -CommandsRegistry.registerCommand(PICK_WORKSPACE_COMMAND, function (accessor: ServicesAccessor, args?: [IPickOptions, CancellationToken]) { +CommandsRegistry.registerCommand(PICK_WORKSPACE_FOLDER_COMMAND, function (accessor: ServicesAccessor, args?: [IPickOptions, CancellationToken]) { const contextService = accessor.get(IWorkspaceContextService); const quickOpenService = accessor.get(IQuickOpenService); const environmentService = accessor.get(IEnvironmentService); diff --git a/src/vs/workbench/browser/labels.ts b/src/vs/workbench/browser/labels.ts index ac2e3bc2d34..afcd51a8b91 100644 --- a/src/vs/workbench/browser/labels.ts +++ b/src/vs/workbench/browser/labels.ts @@ -6,7 +6,7 @@ 'use strict'; import uri from 'vs/base/common/uri'; -import paths = require('vs/base/common/paths'); +import resources = require('vs/base/common/resources'); import { IconLabel, IIconLabelOptions, IIconLabelCreationOptions } from 'vs/base/browser/ui/iconLabel/iconLabel'; import { IExtensionService } from 'vs/platform/extensions/common/extensions'; import { IModeService } from 'vs/editor/common/services/modeService'; @@ -147,7 +147,7 @@ export class ResourceLabel extends IconLabel { if (this.options && typeof this.options.title === 'string') { title = this.options.title; } else if (resource) { - title = getPathLabel(resource.fsPath, void 0, this.environmentService); + title = getPathLabel(resource, void 0, this.environmentService); } if (!this.computedIconClasses) { @@ -210,14 +210,19 @@ export class FileLabel extends ResourceLabel { } public setFile(resource: uri, options?: IFileLabelOptions): void { + const hideLabel = options && options.hideLabel; let name: string; - if (options && options.hideLabel) { - name = void 0; - } else if (options && options.fileKind === FileKind.ROOT_FOLDER) { - const workspaceFolder = this.contextService.getWorkspaceFolder(resource); - name = workspaceFolder.name; - } else { - name = paths.basename(resource.fsPath); + if (!hideLabel) { + if (options && options.fileKind === FileKind.ROOT_FOLDER) { + const workspaceFolder = this.contextService.getWorkspaceFolder(resource); + if (workspaceFolder) { + name = workspaceFolder.name; + } + } + + if (!name) { + name = resources.basenameOrAuthority(resource); + } } let description: string; @@ -233,7 +238,7 @@ export class FileLabel extends ResourceLabel { rootProvider = this.contextService; } - description = getPathLabel(paths.dirname(resource.fsPath), rootProvider, this.environmentService); + description = getPathLabel(resources.dirname(resource), rootProvider, this.environmentService); } this.setLabel({ resource, name, description }, options); @@ -245,34 +250,30 @@ export function getIconClasses(modelService: IModelService, modeService: IModeSe // we always set these base classes even if we do not have a path const classes = fileKind === FileKind.ROOT_FOLDER ? ['rootfolder-icon'] : fileKind === FileKind.FOLDER ? ['folder-icon'] : ['file-icon']; - let path: string; - if (resource) { - path = resource.fsPath; - } - if (path) { - const basename = cssEscape(paths.basename(path).toLowerCase()); + if (resource) { + const name = cssEscape(resources.basenameOrAuthority(resource).toLowerCase()); // Folders if (fileKind === FileKind.FOLDER) { - classes.push(`${basename}-name-folder-icon`); + classes.push(`${name}-name-folder-icon`); } // Files else { // Name - classes.push(`${basename}-name-file-icon`); + classes.push(`${name}-name-file-icon`); // Extension(s) - const dotSegments = basename.split('.'); + const dotSegments = name.split('.'); for (let i = 1; i < dotSegments.length; i++) { classes.push(`${dotSegments.slice(i).join('.')}-ext-file-icon`); // add each combination of all found extensions if more than one } // Configured Language let configuredLangId = getConfiguredLangId(modelService, resource); - configuredLangId = configuredLangId || modeService.getModeIdByFilenameOrFirstLine(path); + configuredLangId = configuredLangId || modeService.getModeIdByFilenameOrFirstLine(name); if (configuredLangId) { classes.push(`${cssEscape(configuredLangId)}-lang-file-icon`); } @@ -298,4 +299,4 @@ function getConfiguredLangId(modelService: IModelService, resource: uri): string function cssEscape(val: string): string { return val.replace(/\s/g, '\\$&'); // make sure to not introduce CSS classes from files that contain whitespace -} \ No newline at end of file +} diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts index 639612c9d72..8add806cda5 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts @@ -672,22 +672,32 @@ export class GlobalActivityActionItem extends ActivityActionItem { // Context menus are triggered on mouse down so that an item can be picked // and executed with releasing the mouse over it this.$container.on(DOM.EventType.MOUSE_DOWN, (e: MouseEvent) => { - DOM.EventHelper.stop(e, true); - - const event = new StandardMouseEvent(e); - this.showContextMenu({ x: event.posx, y: event.posy }); + this.onClick(e); }); + // Extra listener for keyboard interaction this.$container.on(DOM.EventType.KEY_UP, (e: KeyboardEvent) => { let event = new StandardKeyboardEvent(e); if (event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) { - DOM.EventHelper.stop(e, true); - - this.showContextMenu(this.$container.getHTMLElement()); + this.onClick(e); } }); } + public onClick(event?: MouseEvent | KeyboardEvent): void { + DOM.EventHelper.stop(event, true); + + let location: HTMLElement | { x: number, y: number }; + if (event instanceof MouseEvent) { + const mouseEvent = new StandardMouseEvent(event); + location = { x: mouseEvent.posx, y: mouseEvent.posy }; + } else { + location = this.$container.getHTMLElement(); + } + + this.showContextMenu(location); + } + private showContextMenu(location: HTMLElement | { x: number, y: number }): void { const globalAction = this._action as GlobalActivityAction; const activity = globalAction.activity as IGlobalActivity; diff --git a/src/vs/workbench/browser/parts/compositePart.ts b/src/vs/workbench/browser/parts/compositePart.ts index 31d7e3c1ee8..4c9a6580090 100644 --- a/src/vs/workbench/browser/parts/compositePart.ts +++ b/src/vs/workbench/browser/parts/compositePart.ts @@ -351,7 +351,7 @@ export abstract class CompositePart extends Part { private updateTitle(compositeId: string, compositeTitle?: string): void { let compositeDescriptor = this.registry.getComposite(compositeId); - if (!compositeDescriptor) { + if (!compositeDescriptor || !this.titleLabel) { return; } diff --git a/src/vs/workbench/browser/parts/editor/editorStatus.ts b/src/vs/workbench/browser/parts/editor/editorStatus.ts index 7f8fe09b18d..544307a16d4 100644 --- a/src/vs/workbench/browser/parts/editor/editorStatus.ts +++ b/src/vs/workbench/browser/parts/editor/editorStatus.ts @@ -43,7 +43,7 @@ import { TabFocus } from 'vs/editor/common/config/commonEditorConfig'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; -import { getCodeEditor as getEditorWidget } from 'vs/editor/common/services/codeEditorService'; +import { getCodeEditor as getEditorWidget, getCodeOrDiffEditor } from 'vs/editor/common/services/codeEditorService'; import { ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration'; @@ -873,28 +873,38 @@ export class ChangeModeAction extends Action { // Change mode for active editor activeEditor = this.editorService.getActiveEditor(); - const editorWidget = getEditorWidget(activeEditor); - if (editorWidget) { - const models: IModel[] = []; - - const textModel = editorWidget.getModel(); - if (textModel) { - models.push(textModel); + const codeOrDiffEditor = getCodeOrDiffEditor(activeEditor); + const models: IModel[] = []; + if (codeOrDiffEditor.codeEditor) { + const codeEditorModel = codeOrDiffEditor.codeEditor.getModel(); + if (codeEditorModel) { + models.push(codeEditorModel); } - - // Find mode - let mode: TPromise; - if (pick === autoDetectMode) { - mode = this.modeService.getOrCreateModeByFilenameOrFirstLine(toResource(activeEditor.input, { supportSideBySide: true, filter: ['file', 'untitled'] }).fsPath, textModel.getLineContent(1)); - } else { - mode = this.modeService.getOrCreateModeByLanguageName(pick.label); - } - - // Change mode - models.forEach(textModel => { - this.modelService.setMode(textModel, mode); - }); } + if (codeOrDiffEditor.diffEditor) { + const diffEditorModel = codeOrDiffEditor.diffEditor.getModel(); + if (diffEditorModel) { + if (diffEditorModel.original) { + models.push(diffEditorModel.original); + } + if (diffEditorModel.modified) { + models.push(diffEditorModel.modified); + } + } + } + + // Find mode + let mode: TPromise; + if (pick === autoDetectMode) { + mode = this.modeService.getOrCreateModeByFilenameOrFirstLine(toResource(activeEditor.input, { supportSideBySide: true, filter: ['file', 'untitled'] }).fsPath, textModel.getLineContent(1)); + } else { + mode = this.modeService.getOrCreateModeByLanguageName(pick.label); + } + + // Change mode + models.forEach(textModel => { + this.modelService.setMode(textModel, mode); + }); }); } diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index 2d63ae042a1..f3484f3f7d0 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -32,6 +32,7 @@ import { Verbosity } from 'vs/platform/editor/common/editor'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { TITLE_BAR_ACTIVE_BACKGROUND, TITLE_BAR_ACTIVE_FOREGROUND, TITLE_BAR_INACTIVE_FOREGROUND, TITLE_BAR_INACTIVE_BACKGROUND, TITLE_BAR_BORDER } from 'vs/workbench/common/theme'; import { IPartService } from 'vs/workbench/services/part/common/partService'; +import { isMacintosh } from 'vs/base/common/platform'; export class TitlebarPart extends Part implements ITitleService { @@ -40,7 +41,7 @@ export class TitlebarPart extends Part implements ITitleService { private static NLS_UNSUPPORTED = nls.localize('patchedWindowTitle', "[Unsupported]"); private static NLS_EXTENSION_HOST = nls.localize('devExtensionWindowTitlePrefix', "[Extension Development Host]"); private static TITLE_DIRTY = '\u25cf '; - private static TITLE_SEPARATOR = ' — '; + private static TITLE_SEPARATOR = isMacintosh ? ' - ' : ' - '; // macOS uses special - separator private titleContainer: Builder; private title: Builder; diff --git a/src/vs/workbench/common/editor/untitledEditorInput.ts b/src/vs/workbench/common/editor/untitledEditorInput.ts index 8ce430fb0f5..dac7180d617 100644 --- a/src/vs/workbench/common/editor/untitledEditorInput.ts +++ b/src/vs/workbench/common/editor/untitledEditorInput.ts @@ -7,9 +7,11 @@ import { TPromise } from 'vs/base/common/winjs.base'; import URI from 'vs/base/common/uri'; import { suggestFilename } from 'vs/base/common/mime'; +import { memoize } from 'vs/base/common/decorators'; import labels = require('vs/base/common/labels'); import { PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry'; import paths = require('vs/base/common/paths'); +import resources = require('vs/base/common/resources'); import { EditorInput, IEncodingSupport, EncodingMode, ConfirmResult } from 'vs/workbench/common/editor'; import { UntitledEditorModel } from 'vs/workbench/common/editor/untitledEditorModel'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -37,14 +39,6 @@ export class UntitledEditorInput extends EditorInput implements IEncodingSupport private toUnbind: IDisposable[]; - private shortDescription: string; - private mediumDescription: string; - private longDescription: string; - - private shortTitle: string; - private mediumTitle: string; - private longTitle: string; - constructor( private resource: URI, hasAssociatedFilePath: boolean, @@ -94,7 +88,22 @@ export class UntitledEditorInput extends EditorInput implements IEncodingSupport } public getName(): string { - return this.hasAssociatedFilePath ? paths.basename(this.resource.fsPath) : this.resource.fsPath; + return this.hasAssociatedFilePath ? resources.basenameOrAuthority(this.resource) : this.resource.path; + } + + @memoize + private get shortDescription(): string { + return paths.basename(labels.getPathLabel(resources.dirname(this.resource), void 0, this.environmentService)); + } + + @memoize + private get mediumDescription(): string { + return labels.getPathLabel(resources.dirname(this.resource), this.contextService, this.environmentService); + } + + @memoize + private get longDescription(): string { + return labels.getPathLabel(resources.dirname(this.resource), void 0, this.environmentService); } public getDescription(verbosity: Verbosity = Verbosity.MEDIUM): string { @@ -105,20 +114,35 @@ export class UntitledEditorInput extends EditorInput implements IEncodingSupport let description: string; switch (verbosity) { case Verbosity.SHORT: - description = this.shortDescription ? this.shortDescription : (this.shortDescription = paths.basename(labels.getPathLabel(paths.dirname(this.resource.fsPath), void 0, this.environmentService))); + description = this.shortDescription; break; case Verbosity.LONG: - description = this.longDescription ? this.longDescription : (this.longDescription = labels.getPathLabel(paths.dirname(this.resource.fsPath), void 0, this.environmentService)); + description = this.longDescription; break; case Verbosity.MEDIUM: default: - description = this.mediumDescription ? this.mediumDescription : (this.mediumDescription = labels.getPathLabel(paths.dirname(this.resource.fsPath), this.contextService, this.environmentService)); + description = this.mediumDescription; break; } return description; } + @memoize + private get shortTitle(): string { + return this.getName(); + } + + @memoize + private get mediumTitle(): string { + return labels.getPathLabel(this.resource, this.contextService, this.environmentService); + } + + @memoize + private get longTitle(): string { + return labels.getPathLabel(this.resource, void 0, this.environmentService); + } + public getTitle(verbosity: Verbosity): string { if (!this.hasAssociatedFilePath) { return this.getName(); @@ -127,13 +151,13 @@ export class UntitledEditorInput extends EditorInput implements IEncodingSupport let title: string; switch (verbosity) { case Verbosity.SHORT: - title = this.shortTitle ? this.shortTitle : (this.shortTitle = this.getName()); + title = this.shortTitle; break; case Verbosity.MEDIUM: - title = this.mediumTitle ? this.mediumTitle : (this.mediumTitle = labels.getPathLabel(this.resource, this.contextService, this.environmentService)); + title = this.mediumTitle; break; case Verbosity.LONG: - title = this.longTitle ? this.longTitle : (this.longTitle = labels.getPathLabel(this.resource, void 0, this.environmentService)); + title = this.longTitle; break; } diff --git a/src/vs/workbench/common/editor/untitledEditorModel.ts b/src/vs/workbench/common/editor/untitledEditorModel.ts index f58b37497f1..5442ba80ab6 100644 --- a/src/vs/workbench/common/editor/untitledEditorModel.ts +++ b/src/vs/workbench/common/editor/untitledEditorModel.ts @@ -194,7 +194,7 @@ export class UntitledEditorModel extends BaseTextEditorModel implements IEncodin this.toDispose.push(this.textEditorModel.onDidChangeContent(() => this.onModelContentChanged())); // Listen to mode changes - this.toDispose.push(this.textEditorModel.onDidChangeLanguage(() => this.onModelModeChanged())); + this.toDispose.push(this.textEditorModel.onDidChangeLanguage(() => this.onConfigurationChange())); // mode change can have impact on config return model; }); @@ -234,10 +234,6 @@ export class UntitledEditorModel extends BaseTextEditorModel implements IEncodin this.contentChangeEventScheduler.schedule(); } - private onModelModeChanged(): void { - this.onConfigurationChange(); // mode change can have impact on config - } - public dispose(): void { super.dispose(); diff --git a/src/vs/workbench/browser/actions/configureLocale.ts b/src/vs/workbench/electron-browser/configureLocale.ts similarity index 94% rename from src/vs/workbench/browser/actions/configureLocale.ts rename to src/vs/workbench/electron-browser/configureLocale.ts index 41ca1f377c5..d360f840752 100644 --- a/src/vs/workbench/browser/actions/configureLocale.ts +++ b/src/vs/workbench/electron-browser/configureLocale.ts @@ -69,6 +69,12 @@ class ConfigureLocaleAction extends Action { const registry = Registry.as(Extensions.WorkbenchActions); registry.registerWorkbenchAction(new SyncActionDescriptor(ConfigureLocaleAction, ConfigureLocaleAction.ID, ConfigureLocaleAction.LABEL), 'Configure Language'); +let enumValues: string[] = ['de', 'en', 'en-US', 'es', 'fr', 'it', 'ja', 'ko', 'ru', 'zh-CN', 'zh-TW']; +import product from 'vs/platform/node/product'; +if (product.quality !== 'stable') { + enumValues.push('hu'); +} + const schemaId = 'vscode://schemas/locale'; // Keep en-US since we generated files with that content. const schema: IJSONSchema = @@ -83,7 +89,7 @@ const schema: IJSONSchema = properties: { locale: { type: 'string', - enum: ['de', 'en', 'en-US', 'es', 'fr', 'it', 'ja', 'ko', 'ru', 'zh-CN', 'zh-TW'], + enum: enumValues, description: nls.localize('JsonSchema.locale', 'The UI Language to use.') } } diff --git a/src/vs/workbench/electron-browser/main.ts b/src/vs/workbench/electron-browser/main.ts index 0e08a60442a..f461e27e464 100644 --- a/src/vs/workbench/electron-browser/main.ts +++ b/src/vs/workbench/electron-browser/main.ts @@ -58,7 +58,7 @@ export function startup(configuration: IWindowConfiguration): TPromise { browser.setFullscreen(!!configuration.fullscreen); - KeyboardMapperFactory.INSTANCE._onKeyboardLayoutChanged(configuration.isISOKeyboard); + KeyboardMapperFactory.INSTANCE._onKeyboardLayoutChanged(); browser.setAccessibilitySupport(configuration.accessibilitySupport ? platform.AccessibilitySupport.Enabled : platform.AccessibilitySupport.Disabled); diff --git a/src/vs/workbench/electron-browser/nodeCachedDataManager.ts b/src/vs/workbench/electron-browser/nodeCachedDataManager.ts index 52d57348df6..c21787e2e28 100644 --- a/src/vs/workbench/electron-browser/nodeCachedDataManager.ts +++ b/src/vs/workbench/electron-browser/nodeCachedDataManager.ts @@ -4,41 +4,21 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { onUnexpectedError } from 'vs/base/common/errors'; -import { TPromise } from 'vs/base/common/winjs.base'; -import { join, basename } from 'path'; -import { readdir, rimraf, stat } from 'vs/base/node/pfs'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { basename } from 'path'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import product from 'vs/platform/node/product'; declare type OnNodeCachedDataArgs = [{ errorCode: string, path: string, detail?: string }, { path: string, length: number }]; declare const MonacoEnvironment: { onNodeCachedData: OnNodeCachedDataArgs[] }; export class NodeCachedDataManager { - private static _DataMaxAge = product.nameLong.indexOf('Insiders') >= 0 - ? 1000 * 60 * 60 * 24 * 7 // roughly 1 week - : 1000 * 60 * 60 * 24 * 30 * 3; // roughly 3 months - - private _telemetryService: ITelemetryService; - private _environmentService: IEnvironmentService; - private _disposables: IDisposable[] = []; + private readonly _telemetryService: ITelemetryService; constructor( @ITelemetryService telemetryService: ITelemetryService, - @IEnvironmentService environmentService: IEnvironmentService ) { this._telemetryService = telemetryService; - this._environmentService = environmentService; - this._handleCachedDataInfo(); - this._manageCachedDataSoon(); - } - - dispose(): void { - this._disposables = dispose(this._disposables); } private _handleCachedDataInfo(): void { @@ -69,42 +49,4 @@ export class NodeCachedDataManager { global.require.config({ onNodeCachedData: undefined }); delete MonacoEnvironment.onNodeCachedData; } - - private _manageCachedDataSoon(): void { - // Cached data is stored as user data and we run a cleanup task everytime - // the editor starts. The strategy is to delete all files that are older than - // 3 months - - const { nodeCachedDataDir } = this._environmentService; - if (!nodeCachedDataDir) { - return; - } - - let handle = setTimeout(() => { - handle = undefined; - - readdir(nodeCachedDataDir).then(entries => { - - const now = Date.now(); - const deletes = entries.map(entry => { - const path = join(nodeCachedDataDir, entry); - return stat(path).then(stats => { - const diff = now - stats.mtime.getTime(); - if (diff > NodeCachedDataManager._DataMaxAge) { - return rimraf(path); - } - return undefined; - }); - }); - - return TPromise.join(deletes); - - }).done(undefined, onUnexpectedError); - - }, 30 * 1000); - - this._disposables.push({ - dispose() { clearTimeout(handle); } - }); - } } diff --git a/src/vs/workbench/electron-browser/shell.ts b/src/vs/workbench/electron-browser/shell.ts index e188d34337d..fd99f05e00a 100644 --- a/src/vs/workbench/electron-browser/shell.ts +++ b/src/vs/workbench/electron-browser/shell.ts @@ -277,7 +277,7 @@ export class WorkbenchShell { const config: ITelemetryServiceConfig = { appender: new TelemetryAppenderClient(channel), - commonProperties: resolveWorkbenchCommonProperties(this.storageService, commit, version), + commonProperties: resolveWorkbenchCommonProperties(this.storageService, commit, version, this.environmentService.installSource), piiPaths: [this.environmentService.appRoot, this.environmentService.extensionsPath] }; diff --git a/src/vs/workbench/electron-browser/window.ts b/src/vs/workbench/electron-browser/window.ts index b0f2614a60b..622cf14e8d3 100644 --- a/src/vs/workbench/electron-browser/window.ts +++ b/src/vs/workbench/electron-browser/window.ts @@ -25,7 +25,7 @@ import { IEditorGroupService } from 'vs/workbench/services/group/common/groupSer import { IMessageService } from 'vs/platform/message/common/message'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; -import { IWindowsService, IWindowService, IWindowSettings, IPath, IOpenFileRequest, IWindowsConfiguration, IAddFoldersRequest } from 'vs/platform/windows/common/windows'; +import { IWindowsService, IWindowService, IWindowSettings, IPath, IOpenFileRequest, IWindowsConfiguration, IAddFoldersRequest, IRunActionInWindowRequest } from 'vs/platform/windows/common/windows'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; @@ -127,9 +127,25 @@ export class ElectronWindow extends Themable { }); // Support runAction event - ipc.on('vscode:runAction', (event, actionId: string) => { - this.commandService.executeCommand(actionId, { from: 'menu' }).done(_ => { - this.telemetryService.publicLog('commandExecuted', { id: actionId, from: 'menu' }); + ipc.on('vscode:runAction', (event, request: IRunActionInWindowRequest) => { + const args: any[] = []; + + // If we run an action from the touchbar, we fill in the currently active resource + // as payload because the touch bar items are context aware depending on the editor + if (request.from === 'touchbar') { + const activeEditor = this.editorService.getActiveEditor(); + if (activeEditor) { + const resource = toResource(activeEditor.input, { supportSideBySide: true }); + if (resource) { + args.push(resource); + } + } + } else { + args.push({ from: request.from }); // TODO@telemetry this is a bit weird to send this to every action? + } + + this.commandService.executeCommand(request.id, ...args).done(_ => { + this.telemetryService.publicLog('commandExecuted', { id: request.id, from: request.from }); }, err => { this.messageService.show(Severity.Error, err); }); @@ -209,8 +225,8 @@ export class ElectronWindow extends Themable { }); // keyboard layout changed event - ipc.on('vscode:keyboardLayoutChanged', (event, isISOKeyboard: boolean) => { - KeyboardMapperFactory.INSTANCE._onKeyboardLayoutChanged(isISOKeyboard); + ipc.on('vscode:keyboardLayoutChanged', event => { + KeyboardMapperFactory.INSTANCE._onKeyboardLayoutChanged(); }); // keyboard layout changed event diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts index 8133a94c1b8..1917de3e094 100644 --- a/src/vs/workbench/electron-browser/workbench.ts +++ b/src/vs/workbench/electron-browser/workbench.ts @@ -554,6 +554,11 @@ export class Workbench implements IPartService { this.toShutdown.push(this.activitybarPart); serviceCollection.set(IActivityBarService, this.activitybarPart); + // File Service + this.fileService = this.instantiationService.createInstance(RemoteFileService); + serviceCollection.set(IFileService, this.fileService); + this.toDispose.push(this.fileService.onFileChanges(e => this.configurationService.handleWorkspaceFileEvents(e))); + // Editor service (editor part) this.editorPart = this.instantiationService.createInstance(EditorPart, Identifiers.EDITOR_PART, !this.hasFilesToCreateOpenOrDiff); this.toDispose.push(this.editorPart); @@ -568,11 +573,6 @@ export class Workbench implements IPartService { this.toShutdown.push(this.titlebarPart); serviceCollection.set(ITitleService, this.titlebarPart); - // File Service - this.fileService = this.instantiationService.createInstance(RemoteFileService); - serviceCollection.set(IFileService, this.fileService); - this.toDispose.push(this.fileService.onFileChanges(e => this.configurationService.handleWorkspaceFileEvents(e))); - // History serviceCollection.set(IHistoryService, new SyncDescriptor(HistoryService)); diff --git a/src/vs/workbench/node/extensionHostMain.ts b/src/vs/workbench/node/extensionHostMain.ts index ccd25d7eadb..c08dd047c5d 100644 --- a/src/vs/workbench/node/extensionHostMain.ts +++ b/src/vs/workbench/node/extensionHostMain.ts @@ -18,6 +18,7 @@ import { DiskSearch } from 'vs/workbench/services/search/node/searchService'; import { IInitData, IEnvironment, IWorkspaceData, MainContext } from 'vs/workbench/api/node/extHost.protocol'; import * as errors from 'vs/base/common/errors'; import * as watchdog from 'native-watchdog'; +import * as glob from 'vs/base/common/glob'; // const nativeExit = process.exit.bind(process); process.exit = function () { @@ -149,64 +150,91 @@ export class ExtensionHostMain { return TPromise.as(null); } - const desiredFilesMap: { - [filename: string]: boolean; - } = {}; + return TPromise.join( + this._extensionService.getAllExtensionDescriptions().map((desc) => { + return this.handleWorkspaceContainsEagerExtension(desc); + }) + ).then(() => { }); + } - this._extensionService.getAllExtensionDescriptions().forEach((desc) => { - let activationEvents = desc.activationEvents; - if (!activationEvents) { - return; - } + private handleWorkspaceContainsEagerExtension(desc: IExtensionDescription): TPromise { + let activationEvents = desc.activationEvents; + if (!activationEvents) { + return TPromise.as(void 0); + } - for (let i = 0; i < activationEvents.length; i++) { - if (/^workspaceContains:/.test(activationEvents[i])) { - let fileName = activationEvents[i].substr('workspaceContains:'.length); - desiredFilesMap[fileName] = true; + const fileNames: string[] = []; + const globPatterns: string[] = []; + + for (let i = 0; i < activationEvents.length; i++) { + if (/^workspaceContains:/.test(activationEvents[i])) { + let fileNameOrGlob = activationEvents[i].substr('workspaceContains:'.length); + if (fileNameOrGlob.indexOf('*') >= 0 || fileNameOrGlob.indexOf('?') >= 0) { + globPatterns.push(fileNameOrGlob); + } else { + fileNames.push(fileNameOrGlob); } } - }); + } - const matchingPatterns = Object.keys(desiredFilesMap).map(p => { - // TODO: This is a bit hacky -- maybe this should be implemented by using something like - // `workspaceGlob` or something along those lines? - if (p.indexOf('*') > -1 || p.indexOf('?') > -1) { - if (!this._diskSearch) { - // Shut down this search process after 1s - this._diskSearch = new DiskSearch(false, 1000); - } + if (fileNames.length === 0 && globPatterns.length === 0) { + return TPromise.as(void 0); + } - const query: ISearchQuery = { - folderQueries: this._workspace.folders.map(folder => ({ folder: folder.uri })), - type: QueryType.File, - maxResults: 1, - includePattern: { [p]: true } - }; + let fileNamePromise = TPromise.join(fileNames.map((fileName) => this.activateIfFileName(desc.id, fileName))).then(() => { }); + let globPatternPromise = this.activateIfGlobPatterns(desc.id, globPatterns); - return this._diskSearch.search(query).then(result => result.results.length ? p : undefined); - } else { - // find exact path - return (async resolve => { - for (const { uri } of this._workspace.folders) { - if (await pfs.exists(join(uri.fsPath, p))) { - return p; - } - } - return undefined; - })(); + return TPromise.join([fileNamePromise, globPatternPromise]).then(() => { }); + } + + private async activateIfFileName(extensionId: string, fileName: string): TPromise { + // find exact path + + for (const { uri } of this._workspace.folders) { + if (await pfs.exists(join(uri.fsPath, fileName))) { + // the file was found + return ( + this._extensionService.activateById(extensionId, true) + .done(null, err => console.error(err)) + ); } + } + + return undefined; + } + + private async activateIfGlobPatterns(extensionId: string, globPatterns: string[]): TPromise { + if (globPatterns.length === 0) { + return TPromise.as(void 0); + } + + if (!this._diskSearch) { + // Shut down this search process after 1s + this._diskSearch = new DiskSearch(false, 1000); + } + + let includes: glob.IExpression = {}; + globPatterns.forEach((globPattern) => { + includes[globPattern] = true; }); - return TPromise.join(matchingPatterns).then(patterns => { - patterns - .filter(p => p !== undefined) - .forEach(p => { - const activationEvent = `workspaceContains:${p}`; + const query: ISearchQuery = { + folderQueries: this._workspace.folders.map(folder => ({ folder: folder.uri })), + type: QueryType.File, + maxResults: 1, + includePattern: includes + }; - this._extensionService.activateByEvent(activationEvent, true) - .done(null, err => console.error(err)); - }); - }); + let result = await this._diskSearch.search(query); + if (result.results.length > 0) { + // a file was found matching one of the glob patterns + return ( + this._extensionService.activateById(extensionId, true) + .done(null, err => console.error(err)) + ); + } + + return TPromise.as(void 0); } private handleExtensionTests(): TPromise { diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.ts b/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.ts index c5ba3a10a1b..d6532f578bd 100644 --- a/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.ts +++ b/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.ts @@ -7,7 +7,7 @@ import * as nls from 'vs/nls'; import { parse, ParseError } from 'vs/base/common/json'; import { readFile } from 'vs/base/node/pfs'; -import { CharacterPair, LanguageConfiguration, IAutoClosingPair, IAutoClosingPairConditional, IndentationRule, CommentRule } from 'vs/editor/common/modes/languageConfiguration'; +import { CharacterPair, LanguageConfiguration, IAutoClosingPair, IAutoClosingPairConditional, IndentationRule, CommentRule, FoldingRules } from 'vs/editor/common/modes/languageConfiguration'; import { IModeService } from 'vs/editor/common/services/modeService'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { Extensions, IJSONContributionRegistry } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; @@ -35,6 +35,7 @@ interface ILanguageConfiguration { surroundingPairs?: (CharacterPair | IAutoClosingPair)[]; wordPattern?: string | IRegExp; indentationRules?: IIndentationRules; + folding?: FoldingRules; } export class LanguageConfigurationFileHandler { @@ -117,6 +118,10 @@ export class LanguageConfigurationFileHandler { } } + if (configuration.folding) { + richEditConfig.folding = configuration.folding; + } + LanguageConfigurationRegistry.register(languageIdentifier, richEditConfig); } @@ -377,7 +382,18 @@ const schema: IJSONSchema = { } } } + }, + folding: { + type: 'object', + description: nls.localize('schema.folding', 'The language\'s folding settings.'), + properties: { + offSide: { + type: 'boolean', + description: nls.localize('schema.folding.offSide', 'A language adheres to the off-side rule if blocks in that language are expressed by their indentation. If set, empty lines belong to the subsequent block.'), + } + } } + } }; let schemaRegistry = Registry.as(Extensions.JSONContribution); diff --git a/src/vs/workbench/parts/debug/browser/debugQuickOpen.ts b/src/vs/workbench/parts/debug/browser/debugQuickOpen.ts index 10e86cda486..943f64d823a 100644 --- a/src/vs/workbench/parts/debug/browser/debugQuickOpen.ts +++ b/src/vs/workbench/parts/debug/browser/debugQuickOpen.ts @@ -11,16 +11,21 @@ import QuickOpen = require('vs/base/parts/quickopen/common/quickOpen'); import Model = require('vs/base/parts/quickopen/browser/quickOpenModel'); import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; import { IDebugService, ILaunch } from 'vs/workbench/parts/debug/common/debug'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import * as errors from 'vs/base/common/errors'; class DebugEntry extends Model.QuickOpenEntry { - constructor(private debugService: IDebugService, private launch: ILaunch, private configurationName: string, highlights: Model.IHighlight[] = []) { + constructor(private debugService: IDebugService, private contextService: IWorkspaceContextService, private launch: ILaunch, private configurationName: string, highlights: Model.IHighlight[] = []) { super(highlights); } public getLabel(): string { - return this.debugService.getConfigurationManager().getLaunches().length <= 1 ? this.configurationName : `${this.configurationName} (${this.launch.workspace.name})`; + return this.configurationName; + } + + public getDescription(): string { + return this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE ? this.launch.workspace.name : ''; } public getAriaLabel(): string { @@ -43,7 +48,8 @@ export class DebugQuickOpenHandler extends Quickopen.QuickOpenHandler { constructor( @IQuickOpenService private quickOpenService: IQuickOpenService, - @IDebugService private debugService: IDebugService + @IDebugService private debugService: IDebugService, + @IWorkspaceContextService private contextService: IWorkspaceContextService ) { super(); } @@ -58,7 +64,7 @@ export class DebugQuickOpenHandler extends Quickopen.QuickOpenHandler { for (let launch of this.debugService.getConfigurationManager().getLaunches()) { launch.getConfigurationNames().map(config => ({ config: config, highlights: Filters.matchesContiguousSubString(input, config) })) .filter(({ highlights }) => !!highlights) - .forEach(({ config, highlights }) => configurations.push(new DebugEntry(this.debugService, launch, config, highlights))); + .forEach(({ config, highlights }) => configurations.push(new DebugEntry(this.debugService, this.contextService, launch, config, highlights))); } return TPromise.as(new Model.QuickOpenModel(configurations)); diff --git a/src/vs/workbench/parts/debug/browser/linkDetector.ts b/src/vs/workbench/parts/debug/browser/linkDetector.ts index 29dd86998c0..ac31e70c14a 100644 --- a/src/vs/workbench/parts/debug/browser/linkDetector.ts +++ b/src/vs/workbench/parts/debug/browser/linkDetector.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import strings = require('vs/base/common/strings'); import uri from 'vs/base/common/uri'; import { isMacintosh } from 'vs/base/common/platform'; import * as errors from 'vs/base/common/errors'; @@ -37,7 +36,6 @@ export class LinkDetector { * If no links were detected, returns the original string. */ public handleLinks(text: string): HTMLElement | string { - const workspaceFolder = this.contextService.getWorkspace().folders[0]; let linkContainer: HTMLElement; for (let pattern of LinkDetector.FILE_LOCATION_PATTERNS) { @@ -47,13 +45,6 @@ export class LinkDetector { let match = pattern.exec(text); while (match !== null) { let resource: uri = null; - if (workspaceFolder) { - try { - resource = (match && !strings.startsWith(match[0], 'http')) - && (match[2] || strings.startsWith(match[1], '/') ? uri.file(match[1]) : this.contextService.toResource(match[1], workspaceFolder)); // TODO@Michel TODO@Isidor (https://github.com/Microsoft/vscode/issues/29190) - } catch (e) { } - } - if (!resource) { match = pattern.exec(text); continue; diff --git a/src/vs/workbench/parts/debug/browser/media/repl.css b/src/vs/workbench/parts/debug/browser/media/repl.css index 01c8566b02b..694cb4c9de8 100644 --- a/src/vs/workbench/parts/debug/browser/media/repl.css +++ b/src/vs/workbench/parts/debug/browser/media/repl.css @@ -36,6 +36,22 @@ font-size: 12px; } +.monaco-workbench.mac .repl .repl-tree .monaco-tree-row .output.expression.value-and-source { + display: flex; +} + + +.monaco-workbench.mac .repl .repl-tree .monaco-tree-row .output.expression.value-and-source .value { + flex: 1; +} + +.monaco-workbench.mac .repl .repl-tree .monaco-tree-row .output.expression.value-and-source .source { + margin-left: 4px; + margin-right: 8px; + cursor: pointer; + text-decoration: underline; +} + .monaco-workbench.windows .repl .repl-tree .monaco-tree-row .input.expression, .monaco-workbench.windows .repl .repl-tree .monaco-tree-row .output.expression, .monaco-workbench.linux .repl .repl-tree .monaco-tree-row .input.expression, diff --git a/src/vs/workbench/parts/debug/common/debug.ts b/src/vs/workbench/parts/debug/common/debug.ts index 6cb4347fe67..465742b8560 100644 --- a/src/vs/workbench/parts/debug/common/debug.ts +++ b/src/vs/workbench/parts/debug/common/debug.ts @@ -17,7 +17,7 @@ import { ISuggestion } from 'vs/editor/common/modes'; import { Source } from 'vs/workbench/parts/debug/common/debugSource'; import { Range, IRange } from 'vs/editor/common/core/range'; import { RawContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; export const VIEWLET_ID = 'workbench.view.debug'; @@ -73,6 +73,13 @@ export interface ITreeElement { export interface IReplElement extends ITreeElement { toString(): string; + sourceData?: IReplElementSource; +} + +export interface IReplElementSource { + source: Source; + lineNumber: number; + column: number; } export interface IExpressionContainer extends ITreeElement { @@ -88,7 +95,7 @@ export interface IExpression extends IReplElement, IExpressionContainer { } export interface ISession { - root: WorkspaceFolder; + root: IWorkspaceFolder; stackTrace(args: DebugProtocol.StackTraceArguments): TPromise; exceptionInfo(args: DebugProtocol.ExceptionInfoArguments): TPromise; scopes(args: DebugProtocol.ScopesArguments): TPromise; @@ -422,7 +429,7 @@ export interface ILaunch { */ uri: uri; - workspace: WorkspaceFolder; + workspace: IWorkspaceFolder; /** * Returns a configuration with the specified name. @@ -585,12 +592,12 @@ export interface IDebugService { * Also saves all files, manages if compounds are present in the configuration * and calls the startSessionCommand if an adapter registered it. */ - startDebugging(root: WorkspaceFolder, configOrName?: IConfig | string, noDebug?: boolean): TPromise; + startDebugging(root: IWorkspaceFolder, configOrName?: IConfig | string, noDebug?: boolean): TPromise; /** * Creates a new debug process. Depending on the configuration will either 'launch' or 'attach'. */ - createProcess(root: WorkspaceFolder, config: IConfig): TPromise; + createProcess(root: IWorkspaceFolder, config: IConfig): TPromise; /** * Find process by ID. diff --git a/src/vs/workbench/parts/debug/common/debugModel.ts b/src/vs/workbench/parts/debug/common/debugModel.ts index c104055acee..f831c74c5e6 100644 --- a/src/vs/workbench/parts/debug/common/debugModel.ts +++ b/src/vs/workbench/parts/debug/common/debugModel.ts @@ -5,7 +5,7 @@ import * as nls from 'vs/nls'; import uri from 'vs/base/common/uri'; -import * as paths from 'vs/base/common/paths'; +import * as resources from 'vs/base/common/resources'; import { TPromise } from 'vs/base/common/winjs.base'; import * as lifecycle from 'vs/base/common/lifecycle'; import Event, { Emitter } from 'vs/base/common/event'; @@ -19,7 +19,7 @@ import { Range, IRange } from 'vs/editor/common/core/range'; import { ISuggestion } from 'vs/editor/common/modes'; import { Position } from 'vs/editor/common/core/position'; import { - ITreeElement, IExpression, IExpressionContainer, IProcess, IStackFrame, IExceptionBreakpoint, IBreakpoint, IFunctionBreakpoint, IModel, + ITreeElement, IExpression, IExpressionContainer, IProcess, IStackFrame, IExceptionBreakpoint, IBreakpoint, IFunctionBreakpoint, IModel, IReplElementSource, IConfig, ISession, IThread, IRawModelUpdate, IScope, IRawStoppedDetails, IEnablement, IRawBreakpoint, IExceptionInfo, IReplElement, ProcessState } from 'vs/workbench/parts/debug/common/debug'; import { Source } from 'vs/workbench/parts/debug/common/debugSource'; @@ -30,7 +30,7 @@ const MAX_REPL_LENGTH = 10000; export abstract class AbstractOutputElement implements IReplElement { private static ID_COUNTER = 0; - constructor(private id = AbstractOutputElement.ID_COUNTER++) { + constructor(public sourceData: IReplElementSource, private id = AbstractOutputElement.ID_COUNTER++) { // noop } @@ -47,8 +47,9 @@ export class OutputElement extends AbstractOutputElement { constructor( public value: string, public severity: severity, + source: IReplElementSource, ) { - super(); + super(source); } public toString(): string { @@ -60,8 +61,8 @@ export class OutputNameValueElement extends AbstractOutputElement implements IEx private static MAX_CHILDREN = 1000; // upper bound of children per value - constructor(public name: string, public valueObj: any, public annotation?: string) { - super(); + constructor(public name: string, public valueObj: any, source?: IReplElementSource, public annotation?: string) { + super(source); } public get value(): string { @@ -379,18 +380,8 @@ export class StackFrame implements IStackFrame { } public openInEditor(editorService: IWorkbenchEditorService, preserveFocus?: boolean, sideBySide?: boolean): TPromise { - - return !this.source.available ? TPromise.as(null) : editorService.openEditor({ - resource: this.source.uri, - description: this.source.origin, - options: { - preserveFocus, - selection: this.range, - revealIfVisible: true, - revealInCenterIfOutsideViewport: true, - pinned: !preserveFocus && !this.source.inMemory - } - }, sideBySide); + return !this.source.available ? TPromise.as(null) : + this.source.openInEditor(editorService, this.range, preserveFocus, sideBySide); } } @@ -553,7 +544,7 @@ export class Process implements IProcess { } public getName(includeRoot: boolean): string { - return includeRoot ? `${this.configuration.name} (${paths.basename(this.session.root.uri.fsPath)})` : this.configuration.name; + return includeRoot ? `${this.configuration.name} (${resources.basenameOrAuthority(this.session.root.uri)})` : this.configuration.name; } public get state(): ProcessState { @@ -691,6 +682,7 @@ export class Breakpoint implements IBreakpoint { public enabled: boolean, public condition: string, public hitCondition: string, + public adapterData: any ) { if (enabled === undefined) { this.enabled = true; @@ -871,9 +863,9 @@ export class Model implements IModel { this._onDidChangeBreakpoints.fire(); } - public addBreakpoints(uri: uri, rawData: IRawBreakpoint[]): void { + public addBreakpoints(uri: uri, rawData: IRawBreakpoint[], adapterData: any = undefined): void { this.breakpoints = this.breakpoints.concat(rawData.map(rawBp => - new Breakpoint(uri, rawBp.lineNumber, rawBp.column, rawBp.enabled, rawBp.condition, rawBp.hitCondition))); + new Breakpoint(uri, rawBp.lineNumber, rawBp.column, rawBp.enabled, rawBp.condition, rawBp.hitCondition, adapterData))); this.breakpointsActivated = true; this.breakpoints = distinct(this.breakpoints, bp => `${bp.uri.toString()}:${bp.lineNumber}:${bp.column}`); this._onDidChangeBreakpoints.fire(); @@ -895,6 +887,7 @@ export class Model implements IModel { bp.verified = bpData.verified; bp.idFromAdapter = bpData.id; bp.message = bpData.message; + bp.adapterData = bpData.source ? bpData.source.adapterData : bp.adapterData; } }); this.breakpoints = distinct(this.breakpoints, bp => `${bp.uri.toString()}:${bp.lineNumber}:${bp.column}`); @@ -960,22 +953,22 @@ export class Model implements IModel { .then(() => this._onDidChangeREPLElements.fire()); } - public appendToRepl(output: string | IExpression, severity: severity): void { + public appendToRepl(output: string | IExpression, severity: severity, source?: IReplElementSource): void { if (typeof output === 'string') { const previousOutput = this.replElements.length && (this.replElements[this.replElements.length - 1] as OutputElement); - const toAdd = output.split('\n').map(line => new OutputElement(line, severity)); - if (previousOutput instanceof OutputElement && severity === previousOutput.severity && toAdd.length) { - previousOutput.value += toAdd.shift().value; - } - if (previousOutput && previousOutput.value === '' && previousOutput.severity !== severity) { + const toAdd = output.split('\n').map((line, index) => new OutputElement(line, severity, index === 0 ? source : undefined)); + if (previousOutput && previousOutput.value === '') { // remove potential empty lines between different output types this.replElements.pop(); + } else if (previousOutput instanceof OutputElement && severity === previousOutput.severity && toAdd.length && toAdd[0].sourceData === previousOutput.sourceData) { + previousOutput.value += toAdd.shift().value; } this.addReplElements(toAdd); } else { // TODO@Isidor hack, we should introduce a new type which is an output that can fetch children like an expression (output).severity = severity; + (output).sourceData = source; this.addReplElements([output]); } diff --git a/src/vs/workbench/parts/debug/common/debugSource.ts b/src/vs/workbench/parts/debug/common/debugSource.ts index 1d8e8f79c8f..74a2e4c4b25 100644 --- a/src/vs/workbench/parts/debug/common/debugSource.ts +++ b/src/vs/workbench/parts/debug/common/debugSource.ts @@ -4,8 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; +import { TPromise } from 'vs/base/common/winjs.base'; import uri from 'vs/base/common/uri'; +import * as paths from 'vs/base/common/paths'; import { DEBUG_SCHEME } from 'vs/workbench/parts/debug/common/debug'; +import { IRange } from 'vs/editor/common/core/range'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; const UNKNOWN_SOURCE_LABEL = nls.localize('unknownSource', "Unknown Source"); @@ -23,7 +27,11 @@ export class Source { if (this.raw.sourceReference > 0) { this.uri = uri.parse(`${DEBUG_SCHEME}:${encodeURIComponent(path)}?session=${encodeURIComponent(sessionId)}&ref=${this.raw.sourceReference}`); } else { - this.uri = uri.file(path); // path should better be absolute! + if (paths.isAbsolute(path)) { + this.uri = uri.file(path); // path should better be absolute! + } else { + this.uri = uri.parse(path); + } } } @@ -46,4 +54,18 @@ export class Source { public get inMemory() { return this.uri.scheme === DEBUG_SCHEME; } -} + + public openInEditor(editorService: IWorkbenchEditorService, selection: IRange, preserveFocus?: boolean, sideBySide?: boolean): TPromise { + return !this.available ? TPromise.as(null) : editorService.openEditor({ + resource: this.uri, + description: this.origin, + options: { + preserveFocus, + selection, + revealIfVisible: true, + revealInCenterIfOutsideViewport: true, + pinned: !preserveFocus && !this.inMemory + } + }, sideBySide); + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts b/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts index 03821558d9f..6f21bf142ff 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts @@ -203,7 +203,7 @@ if (isMacintosh) { registerTouchBarEntry(StartAction.ID, StartAction.LABEL, 0, CONTEXT_NOT_IN_DEBUG_MODE, 'continue-tb.png'); registerTouchBarEntry(ContinueAction.ID, ContinueAction.LABEL, 0, CONTEXT_DEBUG_STATE.isEqualTo('stopped'), 'continue-tb.png'); - registerTouchBarEntry(PauseAction.ID, PauseAction.LABEL, 1, CONTEXT_IN_DEBUG_MODE, 'pause-tb.png'); + registerTouchBarEntry(PauseAction.ID, PauseAction.LABEL, 1, ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, ContextKeyExpr.notEquals('debugState', 'stopped')), 'pause-tb.png'); registerTouchBarEntry(StepOverAction.ID, StepOverAction.LABEL, 2, CONTEXT_DEBUG_STATE.isEqualTo('stopped'), 'stepover-tb.png'); registerTouchBarEntry(StepIntoAction.ID, StepIntoAction.LABEL, 3, CONTEXT_DEBUG_STATE.isEqualTo('stopped'), 'stepinto-tb.png'); registerTouchBarEntry(StepOutAction.ID, StepOutAction.LABEL, 4, CONTEXT_DEBUG_STATE.isEqualTo('stopped'), 'stepout-tb.png'); diff --git a/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts b/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts index 5d892d5f946..3c704fe5a54 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts @@ -12,7 +12,6 @@ import { first } from 'vs/base/common/arrays'; import { isLinux, isMacintosh, isWindows } from 'vs/base/common/platform'; import * as objects from 'vs/base/common/objects'; import uri from 'vs/base/common/uri'; -import { Schemas } from 'vs/base/common/network'; import * as paths from 'vs/base/common/paths'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; import { IModel, isCommonCodeEditor } from 'vs/editor/common/editorCommon'; @@ -26,10 +25,10 @@ import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/plat import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IFileService } from 'vs/platform/files/common/files'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IWorkspaceContextService, WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ICommandService } from 'vs/platform/commands/common/commands'; -import { IDebugConfigurationProvider, IRawAdapter, ICompound, IDebugConfiguration, DEBUG_SCHEME, IConfig, IEnvConfig, IGlobalConfig, IConfigurationManager, ILaunch } from 'vs/workbench/parts/debug/common/debug'; +import { IDebugConfigurationProvider, IRawAdapter, ICompound, IDebugConfiguration, IConfig, IEnvConfig, IGlobalConfig, IConfigurationManager, ILaunch } from 'vs/workbench/parts/debug/common/debug'; import { Adapter } from 'vs/workbench/parts/debug/node/debugAdapter'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; @@ -398,15 +397,15 @@ export class ConfigurationManager implements IConfigurationManager { } public canSetBreakpointsIn(model: IModel): boolean { - if (model.uri.scheme !== Schemas.file && model.uri.scheme !== DEBUG_SCHEME) { + const modeId = model ? model.getLanguageIdentifier().language : null; + if (!modeId || modeId === 'json') { + // do not allow breakpoints in our settings files return false; } if (this.configurationService.getConfiguration('debug').allowBreakpointsEverywhere) { return true; } - const modeId = model ? model.getLanguageIdentifier().language : null; - return this.breakpointModeIdsSet.has(modeId); } @@ -473,7 +472,7 @@ class Launch implements ILaunch { constructor( private configurationManager: ConfigurationManager, - public workspace: WorkspaceFolder, + public workspace: IWorkspaceFolder, @IFileService private fileService: IFileService, @IWorkbenchEditorService private editorService: IWorkbenchEditorService, @IConfigurationService private configurationService: IConfigurationService, @@ -542,7 +541,7 @@ class Launch implements ILaunch { } public get uri(): uri { - return uri.file(paths.join(this.workspace.uri.fsPath, '/.vscode/launch.json')); + return this.workspace.uri.with({ path: paths.join(this.workspace.uri.path, '/.vscode/launch.json') }); } public openConfigFile(sideBySide: boolean, type?: string): TPromise { diff --git a/src/vs/workbench/parts/debug/electron-browser/debugService.ts b/src/vs/workbench/parts/debug/electron-browser/debugService.ts index 2370ea76f51..ddb4bb6edfe 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugService.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugService.ts @@ -7,6 +7,7 @@ import * as nls from 'vs/nls'; import * as lifecycle from 'vs/base/common/lifecycle'; import Event, { Emitter } from 'vs/base/common/event'; import * as paths from 'vs/base/common/paths'; +import * as resources from 'vs/base/common/resources'; import * as strings from 'vs/base/common/strings'; import { generateUuid } from 'vs/base/common/uuid'; import uri from 'vs/base/common/uri'; @@ -46,10 +47,11 @@ import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IPartService, Parts } from 'vs/workbench/services/part/common/partService'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IWorkspaceContextService, WorkbenchState, WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { ILogEntry, EXTENSION_LOG_BROADCAST_CHANNEL, EXTENSION_ATTACH_BROADCAST_CHANNEL, EXTENSION_TERMINATE_BROADCAST_CHANNEL, EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL, EXTENSION_RELOAD_BROADCAST_CHANNEL } from 'vs/platform/extensions/common/extensionHost'; +import { EXTENSION_LOG_BROADCAST_CHANNEL, EXTENSION_ATTACH_BROADCAST_CHANNEL, EXTENSION_TERMINATE_BROADCAST_CHANNEL, EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL, EXTENSION_RELOAD_BROADCAST_CHANNEL } from 'vs/platform/extensions/common/extensionHost'; import { IBroadcastService, IBroadcast } from 'vs/platform/broadcast/electron-browser/broadcastService'; +import { IRemoteConsoleLog, parse, getFirstFrame } from 'vs/base/node/console'; const DEBUG_BREAKPOINTS_KEY = 'debug.breakpoint'; const DEBUG_BREAKPOINTS_ACTIVATED_KEY = 'debug.breakpointactivated'; @@ -74,7 +76,6 @@ export class DebugService implements debug.IDebugService { private viewModel: ViewModel; private allProcesses: Map; private configurationManager: ConfigurationManager; - private customTelemetryService: ITelemetryService; private toDispose: lifecycle.IDisposable[]; private toDisposeOnSessionEnd: Map; private inDebugMode: IContextKey; @@ -163,15 +164,23 @@ export class DebugService implements debug.IDebugService { // an extension logged output, show it inside the REPL if (broadcast.channel === EXTENSION_LOG_BROADCAST_CHANNEL) { - let extensionOutput: ILogEntry = broadcast.payload.logEntry; + let extensionOutput: IRemoteConsoleLog = broadcast.payload.logEntry; let sev = extensionOutput.severity === 'warn' ? severity.Warning : extensionOutput.severity === 'error' ? severity.Error : severity.Info; - let args: any[] = []; - try { - let parsed = JSON.parse(extensionOutput.arguments); - args.push(...Object.getOwnPropertyNames(parsed).map(o => parsed[o])); - } catch (error) { - args.push(extensionOutput.arguments); + const { args, stack } = parse(extensionOutput); + let source: debug.IReplElementSource; + if (stack) { + const frame = getFirstFrame(stack); + if (frame) { + source = { + column: frame.column, + lineNumber: frame.line, + source: process.getSource({ + name: resources.basenameOrAuthority(frame.uri), + path: frame.uri.fsPath + }) + }; + } } // add output for each argument logged @@ -194,12 +203,12 @@ export class DebugService implements debug.IDebugService { // flush any existing simple values logged if (simpleVals.length) { - this.logToRepl(simpleVals.join(' '), sev); + this.logToRepl(simpleVals.join(' '), sev, source); simpleVals = []; } // show object - this.logToRepl(new OutputNameValueElement((a).prototype, a, nls.localize('snapshotObj', "Only primitive values are shown for this object.")), sev); + this.logToRepl(new OutputNameValueElement((a).prototype, a, undefined, nls.localize('snapshotObj', "Only primitive values are shown for this object.")), sev, source); } // string: watch out for % replacement directive @@ -229,14 +238,14 @@ export class DebugService implements debug.IDebugService { // flush simple values // always append a new line for output coming from an extension such that separate logs go to separate lines #23695 if (simpleVals.length) { - this.logToRepl(simpleVals.join(' ') + '\n', sev); + this.logToRepl(simpleVals.join(' ') + '\n', sev, source); } } } - private tryToAutoFocusStackFrame(thread: debug.IThread): TPromise { + private autoFocusAndOpenStackFrame(thread: debug.IThread): TPromise { const callStack = thread.getCallStack(); - if (!callStack.length || this.viewModel.focusedStackFrame) { + if (!callStack.length || (this.viewModel.focusedStackFrame && this.viewModel.focusedStackFrame.thread.threadId === thread.threadId)) { return TPromise.as(null); } @@ -285,7 +294,7 @@ export class DebugService implements debug.IDebugService { // Call fetch call stack twice, the first only return the top stack frame. // Second retrieves the rest of the call stack. For performance reasons #25605 this.model.fetchCallStack(thread).then(() => { - return this.tryToAutoFocusStackFrame(thread); + return this.autoFocusAndOpenStackFrame(thread); }); } }, errors.onUnexpectedError); @@ -314,7 +323,9 @@ export class DebugService implements debug.IDebugService { const threadId = event.body.allThreadsContinued !== false ? undefined : event.body.threadId; this.model.clearThreads(session.getId(), false, threadId); if (this.viewModel.focusedProcess.getId() === session.getId()) { - this.focusStackFrameAndEvaluate(null, this.viewModel.focusedProcess).done(null, errors.onUnexpectedError); + this.focusStackFrameAndEvaluate(undefined).done(() => { + return this.viewModel.focusedStackFrame ? this.viewModel.focusedStackFrame.openInEditor(this.editorService, true) : undefined; + }, errors.onUnexpectedError); } this.updateStateAndEmit(session.getId(), debug.State.Running); })); @@ -328,24 +339,30 @@ export class DebugService implements debug.IDebugService { if (event.body.category === 'telemetry') { // only log telemetry events from debug adapter if the adapter provided the telemetry key // and the user opted in telemetry - if (this.customTelemetryService && this.telemetryService.isOptedIn) { - this.customTelemetryService.publicLog(event.body.output, event.body.data); + if (session.customTelemetryService && this.telemetryService.isOptedIn) { + session.customTelemetryService.publicLog(event.body.output, event.body.data); } return; } + const source = event.body.source ? { + lineNumber: event.body.line, + column: event.body.column, + source: process.getSource(event.body.source) + } : undefined; + if (event.body.variablesReference) { const container = new ExpressionContainer(process, event.body.variablesReference, generateUuid()); container.getChildren().then(children => { children.forEach(child => { // Since we can not display multiple trees in a row, we are displaying these variables one after the other (ignoring their names) child.name = null; - this.logToRepl(child, outputSeverity); + this.logToRepl(child, outputSeverity, source); }); }); } else if (typeof event.body.output === 'string') { - this.logToRepl(event.body.output, outputSeverity); + this.logToRepl(event.body.output, outputSeverity, source); } })); @@ -360,7 +377,7 @@ export class DebugService implements debug.IDebugService { column: event.body.breakpoint.column, enabled: true, lineNumber: event.body.breakpoint.line - }]); + }], source.raw.adapterData); const newBreakpoint = this.model.getBreakpoints().filter(bp => bp.idFromAdapter === event.body.breakpoint.id).pop(); this.model.updateBreakpoints({ [newBreakpoint.getId()]: event.body.breakpoint }); } @@ -375,7 +392,8 @@ export class DebugService implements debug.IDebugService { } // For compatibilty reasons check if wrong reason and source not present - if (event.body.reason === 'changed' || (event.body.reason === 'new' && !event.body.breakpoint.source)) { + // TODO@Isidor clean up these checks in October + if (event.body.reason === 'changed' || (event.body.reason === 'new' && !event.body.breakpoint.source) || event.body.reason === 'update') { if (breakpoint) { if (!breakpoint.column) { event.body.breakpoint.column = undefined; @@ -426,7 +444,7 @@ export class DebugService implements debug.IDebugService { let result: Breakpoint[]; try { result = JSON.parse(this.storageService.get(DEBUG_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((breakpoint: any) => { - return new Breakpoint(uri.parse(breakpoint.uri.external || breakpoint.source.uri.external), breakpoint.lineNumber, breakpoint.column, breakpoint.enabled, breakpoint.condition, breakpoint.hitCondition); + return new Breakpoint(uri.parse(breakpoint.uri.external || breakpoint.source.uri.external), breakpoint.lineNumber, breakpoint.column, breakpoint.enabled, breakpoint.condition, breakpoint.hitCondition, breakpoint.adapterData); }); } catch (e) { } @@ -518,11 +536,15 @@ export class DebugService implements debug.IDebugService { public focusStackFrameAndEvaluate(stackFrame: debug.IStackFrame, process?: debug.IProcess, explicit?: boolean): TPromise { if (!process) { const processes = this.model.getProcesses(); - process = stackFrame ? stackFrame.thread.process : processes.length ? processes[0] : null; + if (stackFrame) { + process = stackFrame.thread.process; + } else if (processes.length > 0) { + process = processes.filter(p => p.getAllThreads().some(t => t.stopped)).shift() || processes[0]; + } } if (!stackFrame) { const threads = process ? process.getAllThreads() : null; - const callStack = threads && threads.length ? threads[0].getCallStack() : null; + const callStack = threads && threads.length === 1 ? threads[0].getCallStack() : null; stackFrame = callStack && callStack.length ? callStack[0] : null; } @@ -594,12 +616,12 @@ export class DebugService implements debug.IDebugService { this.model.removeReplExpressions(); } - public logToRepl(value: string | debug.IExpression, sev = severity.Info): void { + public logToRepl(value: string | debug.IExpression, sev = severity.Info, source?: debug.IReplElementSource): void { if (typeof value === 'string' && '[2J'.localeCompare(value) === 0) { // [2J is the ansi escape sequence for clearing the display http://ascii-table.com/ansi-escape-sequences.php this.model.removeReplExpressions(); } else { - this.model.appendToRepl(value, sev); + this.model.appendToRepl(value, sev, source); } } @@ -623,7 +645,7 @@ export class DebugService implements debug.IDebugService { return this.model.evaluateWatchExpressions(this.viewModel.focusedProcess, this.viewModel.focusedStackFrame); } - public startDebugging(root: WorkspaceFolder, configOrName?: debug.IConfig | string, noDebug = false, topCompoundName?: string): TPromise { + public startDebugging(root: IWorkspaceFolder, configOrName?: debug.IConfig | string, noDebug = false, topCompoundName?: string): TPromise { // make sure to save all files and that the configuration is up to date return this.extensionService.activateByEvent('onDebug').then(() => this.textFileService.saveAll().then(() => this.configurationService.reloadConfiguration().then(() => @@ -730,7 +752,7 @@ export class DebugService implements debug.IDebugService { return null; } - public createProcess(root: WorkspaceFolder, config: debug.IConfig): TPromise { + public createProcess(root: IWorkspaceFolder, config: debug.IConfig): TPromise { return this.textFileService.saveAll().then(() => (this.configurationManager.selectedLaunch ? this.configurationManager.selectedLaunch.resolveConfiguration(config) : TPromise.as(config)).then(resolvedConfig => { if (!resolvedConfig) { @@ -800,7 +822,7 @@ export class DebugService implements debug.IDebugService { ); } - private doCreateProcess(root: WorkspaceFolder, configuration: debug.IConfig, sessionId = generateUuid()): TPromise { + private doCreateProcess(root: IWorkspaceFolder, configuration: debug.IConfig, sessionId = generateUuid()): TPromise { configuration.__sessionId = sessionId; this.updateStateAndEmit(sessionId, debug.State.Initializing); this.inDebugMode.set(true); @@ -814,9 +836,9 @@ export class DebugService implements debug.IDebugService { const adapter = this.configurationManager.getAdapter(configuration.type); const { aiKey, type } = adapter; const publisher = adapter.extensionDescription.publisher; - this.customTelemetryService = null; let client: TelemetryClient; + let customTelemetryService: TelemetryService; if (aiKey) { client = new TelemetryClient( uri.parse(require.toUrl('bootstrap')).fsPath, @@ -835,10 +857,10 @@ export class DebugService implements debug.IDebugService { const channel = client.getChannel('telemetryAppender'); const appender = new TelemetryAppenderClient(channel); - this.customTelemetryService = new TelemetryService({ appender }, this.configurationService); + customTelemetryService = new TelemetryService({ appender }, this.configurationService); } - const session = this.instantiationService.createInstance(RawDebugSession, sessionId, configuration.debugServer, adapter, this.customTelemetryService, root); + const session = this.instantiationService.createInstance(RawDebugSession, sessionId, configuration.debugServer, adapter, customTelemetryService, root); const process = this.model.addProcess(configuration, session); this.allProcesses.set(process.getId(), process); @@ -925,7 +947,7 @@ export class DebugService implements debug.IDebugService { }); } - private runPreLaunchTask(root: WorkspaceFolder, taskName: string): TPromise { + private runPreLaunchTask(root: IWorkspaceFolder, taskName: string): TPromise { if (!taskName) { return TPromise.as(null); } @@ -1088,7 +1110,10 @@ export class DebugService implements debug.IDebugService { const breakpointsToSend = this.model.getBreakpoints().filter(bp => this.model.areBreakpointsActivated() && bp.enabled && bp.uri.toString() === modelUri.toString()); const source = process.sources.get(modelUri.toString()); - const rawSource = source ? source.raw : { path: paths.normalize(modelUri.fsPath, true), name: paths.basename(modelUri.fsPath) }; + const rawSource = source ? source.raw : { path: modelUri.scheme === 'file' || modelUri.scheme === debug.DEBUG_SCHEME ? paths.normalize(modelUri.fsPath, true) : modelUri.toString(), name: resources.basenameOrAuthority(modelUri) }; + if (breakpointsToSend.length) { + rawSource.adapterData = breakpointsToSend[0].adapterData; + } return session.setBreakpoints({ source: rawSource, diff --git a/src/vs/workbench/parts/debug/electron-browser/debugViewer.ts b/src/vs/workbench/parts/debug/electron-browser/debugViewer.ts index 5690c49421c..e5be9a9f8c0 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugViewer.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugViewer.ts @@ -8,6 +8,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import * as lifecycle from 'vs/base/common/lifecycle'; import { KeyCode } from 'vs/base/common/keyCodes'; import * as paths from 'vs/base/common/paths'; +import * as resources from 'vs/base/common/resources'; import * as errors from 'vs/base/common/errors'; import { equalsIgnoreCase } from 'vs/base/common/strings'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; @@ -194,7 +195,7 @@ function getSourceName(source: Source, contextService: IWorkspaceContextService, return source.name; } - return paths.basename(source.uri.fsPath); + return resources.basenameOrAuthority(source.uri); } export class BaseDebugController extends DefaultController { @@ -1233,7 +1234,7 @@ export class BreakpointsRenderer implements IRenderer { if (breakpoint.column) { data.lineNumber.textContent += `:${breakpoint.column}`; } - data.filePath.textContent = getPathLabel(paths.dirname(breakpoint.uri.fsPath), this.contextService, this.environmentService); + data.filePath.textContent = getPathLabel(resources.dirname(breakpoint.uri), this.contextService, this.environmentService); data.checkbox.checked = breakpoint.enabled; const debugActive = this.debugService.state === debug.State.Running || this.debugService.state === debug.State.Stopped || this.debugService.state === debug.State.Initializing; @@ -1260,7 +1261,7 @@ export class BreakpointsAccessibilityProvider implements IAccessibilityProvider public getAriaLabel(tree: ITree, element: any): string { if (element instanceof Breakpoint) { - return nls.localize('breakpointAriaLabel', "Breakpoint line {0} {1}, breakpoints, debug", (element).lineNumber, getPathLabel(paths.basename((element).uri.fsPath), this.contextService), this.contextService); + return nls.localize('breakpointAriaLabel', "Breakpoint line {0} {1}, breakpoints, debug", (element).lineNumber, getPathLabel(resources.basenameOrAuthority((element).uri), this.contextService), this.contextService); } if (element instanceof FunctionBreakpoint) { return nls.localize('functionBreakpointAriaLabel', "Function breakpoint {0}, breakpoints, debug", (element).name); diff --git a/src/vs/workbench/parts/debug/electron-browser/debugViews.ts b/src/vs/workbench/parts/debug/electron-browser/debugViews.ts index 6fae5f59586..fca2abcd572 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugViews.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugViews.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import * as paths from 'vs/base/common/paths'; +import * as resources from 'vs/base/common/resources'; import { RunOnceScheduler } from 'vs/base/common/async'; import * as dom from 'vs/base/browser/dom'; import * as builder from 'vs/base/browser/builder'; @@ -428,7 +428,7 @@ export class BreakpointsView extends ViewsViewletPanel { } if (first.uri.toString() !== second.uri.toString()) { - return paths.basename(first.uri.fsPath).localeCompare(paths.basename(second.uri.fsPath)); + return resources.basenameOrAuthority(first.uri).localeCompare(resources.basenameOrAuthority(second.uri)); } if (first.lineNumber === second.lineNumber) { return first.column - second.column; diff --git a/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts b/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts index 035a4b72ad5..992f4a6d673 100644 --- a/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts +++ b/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts @@ -22,7 +22,7 @@ import debug = require('vs/workbench/parts/debug/common/debug'); import { Adapter } from 'vs/workbench/parts/debug/node/debugAdapter'; import { V8Protocol } from 'vs/workbench/parts/debug/node/v8Protocol'; import { IOutputService } from 'vs/workbench/parts/output/common/output'; -import { WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { ExtensionsChannelId } from 'vs/platform/extensionManagement/common/extensionManagement'; import { TerminalSupport } from 'vs/workbench/parts/debug/electron-browser/terminalSupport'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -70,8 +70,8 @@ export class RawDebugSession extends V8Protocol implements debug.ISession { id: string, private debugServerPort: number, private adapter: Adapter, - private customTelemetryService: ITelemetryService, - public root: WorkspaceFolder, + public customTelemetryService: ITelemetryService, + public root: IWorkspaceFolder, @IMessageService private messageService: IMessageService, @ITelemetryService private telemetryService: ITelemetryService, @IOutputService private outputService: IOutputService, diff --git a/src/vs/workbench/parts/debug/electron-browser/repl.ts b/src/vs/workbench/parts/debug/electron-browser/repl.ts index b097d3a3652..a1a9c8250a5 100644 --- a/src/vs/workbench/parts/debug/electron-browser/repl.ts +++ b/src/vs/workbench/parts/debug/electron-browser/repl.ts @@ -180,7 +180,7 @@ export class Repl extends Panel implements IPrivateReplService { modes.SuggestRegistry.register({ scheme: debug.DEBUG_SCHEME }, { triggerCharacters: ['.'], - provideCompletionItems: (model: IReadOnlyModel, position: Position, token: CancellationToken): Thenable => { + provideCompletionItems: (model: IReadOnlyModel, position: Position, _context: modes.SuggestContext, token: CancellationToken): Thenable => { const word = this.replInput.getModel().getWordAtPosition(position); const overwriteBefore = word ? word.word.length : 0; const text = this.replInput.getModel().getLineContent(position.lineNumber); diff --git a/src/vs/workbench/parts/debug/electron-browser/replViewer.ts b/src/vs/workbench/parts/debug/electron-browser/replViewer.ts index bfe1493d1d1..6084cb72d04 100644 --- a/src/vs/workbench/parts/debug/electron-browser/replViewer.ts +++ b/src/vs/workbench/parts/debug/electron-browser/replViewer.ts @@ -6,6 +6,8 @@ import * as nls from 'vs/nls'; import { TPromise } from 'vs/base/common/winjs.base'; import { IAction } from 'vs/base/common/actions'; +import * as lifecycle from 'vs/base/common/lifecycle'; +import * as errors from 'vs/base/common/errors'; import { isFullWidthCharacter, removeAnsiEscapeCodes, endsWith } from 'vs/base/common/strings'; import { IActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; import * as dom from 'vs/base/browser/dom'; @@ -13,7 +15,7 @@ import severity from 'vs/base/common/severity'; import { IMouseEvent } from 'vs/base/browser/mouseEvent'; import { ITree, IAccessibilityProvider, ContextMenuEvent, IDataSource, IRenderer, IActionProvider } from 'vs/base/parts/tree/browser/tree'; import { ICancelableEvent } from 'vs/base/parts/tree/browser/treeDefaults'; -import { IExpressionContainer, IExpression } from 'vs/workbench/parts/debug/common/debug'; +import { IExpressionContainer, IExpression, IReplElementSource } from 'vs/workbench/parts/debug/common/debug'; import { Model, OutputNameValueElement, Expression, OutputElement, Variable } from 'vs/workbench/parts/debug/common/debugModel'; import { renderVariable, renderExpressionValue, IVariableTemplateData, BaseDebugController } from 'vs/workbench/parts/debug/electron-browser/debugViewer'; import { ClearReplAction } from 'vs/workbench/parts/debug/browser/debugActions'; @@ -63,6 +65,9 @@ interface IExpressionTemplateData { interface IValueOutputTemplateData { container: HTMLElement; value: HTMLElement; + source: HTMLElement; + getReplElementSource(): IReplElementSource; + toDispose: lifecycle.IDisposable[]; } interface IKeyValueOutputTemplateData { @@ -174,10 +179,25 @@ export class ReplExpressionsRenderer implements IRenderer { if (templateId === ReplExpressionsRenderer.VALUE_OUTPUT_TEMPLATE_ID) { let data: IValueOutputTemplateData = Object.create(null); dom.addClass(container, 'output'); - let expression = dom.append(container, $('.output.expression')); + let expression = dom.append(container, $('.output.expression.value-and-source')); data.container = container; data.value = dom.append(expression, $('span.value')); + data.source = dom.append(expression, $('.source')); + data.toDispose = []; + data.toDispose.push(dom.addDisposableListener(data.source, 'click', e => { + e.preventDefault(); + e.stopPropagation(); + const source = data.getReplElementSource(); + if (source) { + source.source.openInEditor(this.editorService, { + startLineNumber: source.lineNumber, + startColumn: source.column, + endLineNumber: source.lineNumber, + endColumn: source.column + }).done(undefined, errors.onUnexpectedError); + } + })); return data; } @@ -236,6 +256,9 @@ export class ReplExpressionsRenderer implements IRenderer { } dom.addClass(templateData.value, (output.severity === severity.Warning) ? 'warn' : (output.severity === severity.Error) ? 'error' : 'info'); + templateData.source.textContent = output.sourceData ? `${output.sourceData.source.name}:${output.sourceData.lineNumber}` : ''; + templateData.source.title = output.sourceData ? output.sourceData.source.uri.toString() : ''; + templateData.getReplElementSource = () => output.sourceData; } private renderOutputNameValue(tree: ITree, output: OutputNameValueElement, templateData: IKeyValueOutputTemplateData): void { @@ -352,7 +375,9 @@ export class ReplExpressionsRenderer implements IRenderer { } public disposeTemplate(tree: ITree, templateId: string, templateData: any): void { - // noop + if (templateData.toDispose) { + lifecycle.dispose(templateData.toDispose); + } } } diff --git a/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.ts b/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.ts index 979919f795a..a49f5ec93e9 100644 --- a/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.ts +++ b/src/vs/workbench/parts/debug/electron-browser/statusbarColorProvider.ts @@ -87,7 +87,7 @@ export class StatusBarColorProvider extends Themable implements IWorkbenchContri } private isDebugging(): boolean { - if (this.debugService.state === State.Inactive) { + if (this.debugService.state === State.Inactive || this.debugService.state === State.Initializing) { return false; } diff --git a/src/vs/workbench/parts/debug/node/debugAdapter.ts b/src/vs/workbench/parts/debug/node/debugAdapter.ts index faa649ea280..a403b824a7c 100644 --- a/src/vs/workbench/parts/debug/node/debugAdapter.ts +++ b/src/vs/workbench/parts/debug/node/debugAdapter.ts @@ -14,7 +14,7 @@ import * as objects from 'vs/base/common/objects'; import * as paths from 'vs/base/common/paths'; import * as platform from 'vs/base/common/platform'; import { IJSONSchema, IJSONSchemaSnippet } from 'vs/base/common/jsonSchema'; -import { WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { IConfig, IRawAdapter, IAdapterExecutable, INTERNAL_CONSOLE_OPTIONS_SCHEMA } from 'vs/workbench/parts/debug/common/debug'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; @@ -35,7 +35,7 @@ export class Adapter { public hasConfigurationProvider = false; - public getAdapterExecutable(root: WorkspaceFolder, verifyAgainstFS = true): TPromise { + public getAdapterExecutable(root: IWorkspaceFolder, verifyAgainstFS = true): TPromise { if (this.rawAdapter.adapterExecutableCommand) { return this.commandService.executeCommand(this.rawAdapter.adapterExecutableCommand, root.uri.toString()).then(ad => { diff --git a/src/vs/workbench/parts/debug/test/common/mockDebug.ts b/src/vs/workbench/parts/debug/test/common/mockDebug.ts index 177358f5521..26b2b0b7c71 100644 --- a/src/vs/workbench/parts/debug/test/common/mockDebug.ts +++ b/src/vs/workbench/parts/debug/test/common/mockDebug.ts @@ -7,7 +7,7 @@ import uri from 'vs/base/common/uri'; import Event, { Emitter } from 'vs/base/common/event'; import { TPromise } from 'vs/base/common/winjs.base'; import * as debug from 'vs/workbench/parts/debug/common/debug'; -import { WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; export class MockDebugService implements debug.IDebugService { public _serviceBrand: any; @@ -88,11 +88,11 @@ export class MockDebugService implements debug.IDebugService { return TPromise.as(null); } - public startDebugging(root: WorkspaceFolder, configOrName?: debug.IConfig | string, noDebug?: boolean): TPromise { + public startDebugging(root: IWorkspaceFolder, configOrName?: debug.IConfig | string, noDebug?: boolean): TPromise { return TPromise.as(null); } - public createProcess(root: WorkspaceFolder, config: debug.IConfig): TPromise { + public createProcess(root: IWorkspaceFolder, config: debug.IConfig): TPromise { return TPromise.as(null); } @@ -129,7 +129,7 @@ export class MockSession implements debug.ISession { return 'mockrawsession'; } - public root: WorkspaceFolder; + public root: IWorkspaceFolder; public getLengthInSeconds(): number { return 100; diff --git a/src/vs/workbench/parts/execution/electron-browser/execution.contribution.ts b/src/vs/workbench/parts/execution/electron-browser/execution.contribution.ts index 30ef13ec313..36a53527512 100644 --- a/src/vs/workbench/parts/execution/electron-browser/execution.contribution.ts +++ b/src/vs/workbench/parts/execution/electron-browser/execution.contribution.ts @@ -13,6 +13,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; import paths = require('vs/base/common/paths'); +import resources = require('vs/base/common/resources'); import { Scope, IActionBarRegistry, Extensions as ActionBarExtensions, ActionBarContributor } from 'vs/workbench/browser/actions'; import uri from 'vs/base/common/uri'; import { explorerItemToFileResource } from 'vs/workbench/parts/files/common/files'; @@ -182,7 +183,8 @@ export class ExplorerViewerActionContributor extends ActionBarContributor { } public hasSecondaryActions(context: any): boolean { - return !!explorerItemToFileResource(context.element); + const fileResource = explorerItemToFileResource(context.element); + return fileResource && fileResource.resource.scheme === 'file'; } public getSecondaryActions(context: any): IAction[] { @@ -191,7 +193,7 @@ export class ExplorerViewerActionContributor extends ActionBarContributor { // We want the parent unless this resource is a directory if (!fileResource.isDirectory) { - resource = uri.file(paths.dirname(resource.fsPath)); + resource = resources.dirname(resource); } const configuration = this.configurationService.getConfiguration(); diff --git a/src/vs/workbench/parts/extensions/browser/extensionsActions.ts b/src/vs/workbench/parts/extensions/browser/extensionsActions.ts index 7640073567b..401f5b0e0a3 100644 --- a/src/vs/workbench/parts/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/parts/extensions/browser/extensionsActions.ts @@ -26,19 +26,19 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { Query } from 'vs/workbench/parts/extensions/common/extensionQuery'; import { IFileService, IContent } from 'vs/platform/files/common/files'; -import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { IWindowService } from 'vs/platform/windows/common/windows'; import { IExtensionService, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import URI from 'vs/base/common/uri'; -import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; import { buttonBackground, buttonForeground, buttonHoverBackground, contrastBorder, registerColor, foreground } from 'vs/platform/theme/common/colorRegistry'; import { Color } from 'vs/base/common/color'; -import { IPickOpenEntry, IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing'; import { ITextEditorSelection } from 'vs/platform/editor/common/editor'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { PICK_WORKSPACE_FOLDER_COMMAND } from 'vs/workbench/browser/actions/workspaceActions'; export class InstallAction extends Action { @@ -1368,11 +1368,13 @@ export class ConfigureWorkspaceRecommendedExtensionsAction extends AbstractConfi } public run(event: any): TPromise { - const workspace = this.contextService.getWorkspace(); - if (workspace.configuration) { - return this.openWorkspaceConfigurationFile(workspace.configuration); + switch (this.contextService.getWorkbenchState()) { + case WorkbenchState.FOLDER: + return this.openExtensionsFile(this.contextService.getWorkspace().folders[0].toResource(paths.join('.vscode', 'extensions.json'))); + case WorkbenchState.WORKSPACE: + return this.openWorkspaceConfigurationFile(this.contextService.getWorkspace().configuration); } - return this.openExtensionsFile(this.contextService.toResource(paths.join('.vscode', 'extensions.json'), workspace.folders[0])); + return TPromise.as(null); } dispose(): void { @@ -1391,12 +1393,12 @@ export class ConfigureWorkspaceFolderRecommendedExtensionsAction extends Abstrac constructor( id: string, label: string, - @IQuickOpenService private quickOpenService: IQuickOpenService, @IFileService fileService: IFileService, @IWorkspaceContextService contextService: IWorkspaceContextService, @IWorkbenchEditorService editorService: IWorkbenchEditorService, @IJSONEditingService jsonEditingService: IJSONEditingService, - @ITextModelService textModelResolverService: ITextModelService + @ITextModelService textModelResolverService: ITextModelService, + @ICommandService private commandService: ICommandService ) { super(id, label, contextService, fileService, editorService, jsonEditingService, textModelResolverService); this.contextService.onDidChangeWorkspaceFolders(() => this.update(), this, this.disposables); @@ -1408,19 +1410,12 @@ export class ConfigureWorkspaceFolderRecommendedExtensionsAction extends Abstrac } public run(): TPromise { - const picks: IPickOpenEntry[] = this.contextService.getWorkspace().folders.map((folder, index) => { - return { - label: folder.name, - id: `${index}` - }; - }); - - return this.quickOpenService.pick(picks, { placeHolder: localize('pickFolder', "Select Workspace Folder") }) - .then(pick => { - if (pick) { - return this.openExtensionsFile(this.contextService.toResource(paths.join('.vscode', 'extensions.json'), this.contextService.getWorkspace().folders[parseInt(pick.id)])); + return this.commandService.executeCommand(PICK_WORKSPACE_FOLDER_COMMAND) + .then(workspaceFolder => { + if (workspaceFolder) { + return this.openExtensionsFile(workspaceFolder.toResource(paths.join('.vscode', 'extensions.json'))); } - return undefined; + return null; }); } diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts index 0676fc1ba02..d49a036b406 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts @@ -19,14 +19,15 @@ import { IChoiceService, IMessageService } from 'vs/platform/message/common/mess import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ShowRecommendedExtensionsAction, ShowWorkspaceRecommendedExtensionsAction } from 'vs/workbench/parts/extensions/browser/extensionsActions'; import Severity from 'vs/base/common/severity'; -import { IWorkspaceContextService, WorkspaceFolder, IWorkspace } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, IWorkspaceFolder, IWorkspace } from 'vs/platform/workspace/common/workspace'; import { Schemas } from 'vs/base/common/network'; import { IFileService } from 'vs/platform/files/common/files'; import { IExtensionsConfiguration, ConfigurationKey } from 'vs/workbench/parts/extensions/common/extensions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import * as fs from 'fs'; +import * as pfs from 'vs/base/node/pfs'; +import * as os from 'os'; import { flatten, distinct } from 'vs/base/common/arrays'; interface IExtensionsContent { @@ -68,6 +69,10 @@ export class ExtensionTipsService implements IExtensionTipsService { this._suggestTips(); this._suggestWorkspaceRecommendations(); + + // Executable based recommendations carry out a lot of file stats, so run them after 10 secs + // So that the startup is not affected + setTimeout(() => this._suggestBasedOnExecutables(this._exeBasedRecommendations), 10000); } getWorkspaceRecommendations(): TPromise { @@ -84,8 +89,8 @@ export class ExtensionTipsService implements IExtensionTipsService { return TPromise.as([]); } - private resolveWorkspaceFolderRecommendations(workspaceFolder: WorkspaceFolder): TPromise { - return this.fileService.resolveContent(this.contextService.toResource(paths.join('.vscode', 'extensions.json'), workspaceFolder)) + private resolveWorkspaceFolderRecommendations(workspaceFolder: IWorkspaceFolder): TPromise { + return this.fileService.resolveContent(workspaceFolder.toResource(paths.join('.vscode', 'extensions.json'))) .then(content => this.processWorkspaceRecommendations(json.parse(content.value, [])), err => []); } @@ -102,9 +107,12 @@ export class ExtensionTipsService implements IExtensionTipsService { getRecommendations(): string[] { const allRecomendations = this._getAllRecommendationsInProduct(); const fileBased = Object.keys(this._fileBasedRecommendations) - .filter(recommendation => allRecomendations.indexOf(recommendation) !== -1); + .filter(recommendation => allRecomendations.indexOf(recommendation) !== -1) + .sort((a, b) => { + return this._fileBasedRecommendations[a] > this._fileBasedRecommendations[b] ? -1 : 1; + }); - const exeBased = distinct(this._suggestBasedOnExecutables()); + const exeBased = distinct(this._exeBasedRecommendations); this.telemetryService.publicLog('extensionRecommendations:unfiltered', { fileBased, exeBased }); @@ -239,7 +247,7 @@ export class ExtensionTipsService implements IExtensionTipsService { this.choiceService.choose(Severity.Info, message, options, 2).done(choice => { switch (choice) { case 0: - this.telemetryService.publicLog('extensionRecommendations:popup', { userReaction: 'show' }); + this.telemetryService.publicLog('extensionRecommendations:popup', { userReaction: 'show', extensionId: name }); return recommendationsAction.run(); case 1: this.importantRecommendationsIgnoreList.push(id); this.storageService.store( @@ -247,13 +255,13 @@ export class ExtensionTipsService implements IExtensionTipsService { JSON.stringify(this.importantRecommendationsIgnoreList), StorageScope.GLOBAL ); - this.telemetryService.publicLog('extensionRecommendations:popup', { userReaction: 'neverShowAgain' }); + this.telemetryService.publicLog('extensionRecommendations:popup', { userReaction: 'neverShowAgain', extensionId: name }); return this.ignoreExtensionRecommendations(); case 2: - this.telemetryService.publicLog('extensionRecommendations:popup', { userReaction: 'close' }); + this.telemetryService.publicLog('extensionRecommendations:popup', { userReaction: 'close', extensionId: name }); } }, () => { - this.telemetryService.publicLog('extensionRecommendations:popup', { userReaction: 'cancelled' }); + this.telemetryService.publicLog('extensionRecommendations:popup', { userReaction: 'cancelled', extensionId: name }); }); }); }); @@ -331,42 +339,38 @@ export class ExtensionTipsService implements IExtensionTipsService { }); } - private _suggestBasedOnExecutables(): string[] { - if (!process.env.PATH || this._exeBasedRecommendations.length > 0) { - return this._exeBasedRecommendations; - } - - let envpaths = process.env.PATH.split(process.platform === 'win32' ? ';' : ':'); + private _suggestBasedOnExecutables(recommendations: string[]): void { + const homeDir = os.homedir(); let foundExecutables: Set = new Set(); + let findExecutable = (exeName, path) => { + return pfs.fileExists(path).then(exists => { + if (exists && !foundExecutables.has(exeName)) { + foundExecutables.add(exeName); + recommendations.push(...product.exeBasedExtensionTips[exeName]['recommendations']); + } + }); + }; + // Loop through recommended extensions forEach(product.exeBasedExtensionTips, entry => { - let executables = entry.value.split(','); + if (typeof entry.value !== 'object' || !Array.isArray(entry.value['recommendations'])) { + return; + } - // Loop through executables that would result in recommending current extension - for (let i = 0; i < executables.length; i++) { - if (!foundExecutables.has(executables[i])) { - - // Loop through paths in PATH to find current executable - for (let pathEntry of envpaths) { - let fullPath = paths.join(pathEntry, executables[i]); - if (process.platform === 'win32') { - fullPath += '.exe'; - } - if (fs.existsSync(fullPath)) { - foundExecutables.add(executables[i]); - break; - } - } - } - if (foundExecutables.has(executables[i])) { - this._exeBasedRecommendations.push(entry.key); - break; + let exeName = entry.key; + if (process.platform === 'win32') { + let windowsPath = entry.value['windowsPath']; + if (!windowsPath || typeof windowsPath !== 'string') { + return; } + windowsPath = windowsPath.replace('%USERPROFILE%', process.env['USERPROFILE']); + findExecutable(exeName, windowsPath); + } else { + findExecutable(exeName, paths.join('/usr/local/bin', exeName)); + findExecutable(exeName, paths.join(homeDir, exeName)); } }); - - return this._exeBasedRecommendations; } private setIgnoreRecommendationsConfig(configVal: boolean) { diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts index 7539b00153c..d8fd1c8ec8f 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts @@ -291,12 +291,16 @@ export class ExtensionsListView extends ViewsViewletPanel { .filter(name => local.every(ext => `${ext.publisher}.${ext.name}` !== name)) .filter(name => name.toLowerCase().indexOf(value) > -1); + this.telemetryService.publicLog('extensionAllRecommendations:open', { count: names.length }); if (!names.length) { return TPromise.as(new PagedModel([])); } options.source = 'recommendations-all'; return this.extensionsWorkbenchService.queryGallery(assign(options, { names, pageSize: names.length })) - .then(pager => new PagedModel(pager || [])); + .then(pager => { + this.sortFirstPage(pager, names); + return new PagedModel(pager || []); + }); }); }); } @@ -318,7 +322,10 @@ export class ExtensionsListView extends ViewsViewletPanel { } options.source = 'recommendations'; return this.extensionsWorkbenchService.queryGallery(assign(options, { names, pageSize: names.length })) - .then(pager => new PagedModel(pager || [])); + .then(pager => { + this.sortFirstPage(pager, names); + return new PagedModel(pager || []); + }); }); } @@ -352,6 +359,23 @@ export class ExtensionsListView extends ViewsViewletPanel { .then(result => new PagedModel(result)); } + // Sorts the firsPage of the pager in the same order as given array of extension ids + private sortFirstPage(pager: IPager, ids: string[]) { + if (ids.length !== pager.pageSize) { + return; + } + ids = ids.map(x => x.toLowerCase()); + let newFirstPage = new Array(pager.pageSize); + for (let i = 0; i < pager.pageSize; i++) { + let index = ids.indexOf(pager.firstPage[i].id.toLowerCase()); + if (index === -1) { + return; // Something went wrong, Abort! Abort! + } + newFirstPage[index] = pager.firstPage[i]; + } + pager.firstPage = newFirstPage; + } + private setModel(model: IPagedModel) { this.list.model = model; this.list.scrollTop = 0; diff --git a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts index 9cebb13bc64..39795d8e8a3 100644 --- a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts +++ b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts @@ -20,7 +20,7 @@ import { IPager, mapPager, singlePagePager } from 'vs/base/common/paging'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IExtensionManagementService, IExtensionGalleryService, ILocalExtension, IGalleryExtension, IQueryOptions, IExtensionManifest, - InstallExtensionEvent, DidInstallExtensionEvent, LocalExtensionType, DidUninstallExtensionEvent, IExtensionEnablementService, IExtensionTipsService + InstallExtensionEvent, DidInstallExtensionEvent, LocalExtensionType, DidUninstallExtensionEvent, IExtensionEnablementService, IExtensionTipsService, ErrorCode } from 'vs/platform/extensionManagement/common/extensionManagement'; import { getGalleryExtensionIdFromLocal, getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -725,7 +725,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService { } if (extension.gallery) { // Report telemetry only for gallery extensions - this.reportTelemetry(installing, !error); + this.reportTelemetry(installing, error); } } this._onChange.fire(); @@ -759,7 +759,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService { } if (!error) { - this.reportTelemetry(uninstalling, true); + this.reportTelemetry(uninstalling); } this._onChange.fire(); @@ -789,12 +789,12 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService { return local ? ExtensionState.Installed : ExtensionState.Uninstalled; } - private reportTelemetry(active: IActiveExtension, success: boolean): void { + private reportTelemetry(active: IActiveExtension, errorcode?: ErrorCode): void { const data = active.extension.telemetryData; const duration = new Date().getTime() - active.start.getTime(); const eventName = toTelemetryEventName(active.operation); - this.telemetryService.publicLog(eventName, assign(data, { success, duration })); + this.telemetryService.publicLog(eventName, assign(data, { success: !errorcode, duration, errorcode })); } private onError(err: any): void { @@ -825,7 +825,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService { return TPromise.as(null); } - return this.queryGallery({ names: [extensionId] }).then(result => { + return this.queryGallery({ names: [extensionId], source: 'uri' }).then(result => { if (result.total < 1) { return TPromise.as(null); } diff --git a/src/vs/workbench/parts/files/browser/fileActions.contribution.ts b/src/vs/workbench/parts/files/browser/fileActions.contribution.ts index c56992637f1..c64ab4ad996 100644 --- a/src/vs/workbench/parts/files/browser/fileActions.contribution.ts +++ b/src/vs/workbench/parts/files/browser/fileActions.contribution.ts @@ -26,7 +26,6 @@ import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { explorerItemToFileResource, ExplorerFocusCondition, FilesExplorerFocusCondition } from 'vs/workbench/parts/files/common/files'; -import URI from 'vs/base/common/uri'; class FilesViewerActionContributor extends ActionBarContributor { @@ -93,14 +92,13 @@ class FilesViewerActionContributor extends ActionBarContributor { } if (stat.isRoot && this.environmentService.appQuality !== 'stable') { - let action: Action = this.instantiationService.createInstance(AddRootFolderAction, AddRootFolderAction.ID, AddRootFolderAction.LABEL); - action.order = 52; - actions.push(action); - if (this.contextService.getWorkspace().folders.length > 1) { - action = this.instantiationService.createInstance(RemoveRootFolderAction, stat.resource, RemoveRootFolderAction.ID, RemoveRootFolderAction.LABEL); - action.order = 53; - actions.push(action); - } + const addRootFolderAction: Action = this.instantiationService.createInstance(AddRootFolderAction, AddRootFolderAction.ID, AddRootFolderAction.LABEL); + addRootFolderAction.order = 52; + actions.push(addRootFolderAction); + + const removeRootFolderAction = this.instantiationService.createInstance(RemoveRootFolderAction, stat.resource, RemoveRootFolderAction.ID, RemoveRootFolderAction.LABEL); + removeRootFolderAction.order = 53; + actions.push(removeRootFolderAction); actions.push(new Separator(null, 54)); } @@ -166,18 +164,17 @@ class ExplorerViewersActionContributor extends ActionBarContributor { public getSecondaryActions(context: any): IAction[] { const actions: IAction[] = []; + const fileResource = explorerItemToFileResource(context.element); + const resource = fileResource.resource; - if (this.hasSecondaryActions(context)) { - const fileResource = explorerItemToFileResource(context.element); - const resource = fileResource.resource; - - // Reveal file in OS native explorer + // Reveal file in OS native explorer + if (resource.scheme === 'file') { actions.push(this.instantiationService.createInstance(RevealInOSAction, resource)); - - // Copy Path - actions.push(this.instantiationService.createInstance(CopyPathAction, resource)); } + // Copy Path + actions.push(this.instantiationService.createInstance(CopyPathAction, resource)); + return actions; } } @@ -340,12 +337,4 @@ function appendSaveConflictEditorTitleAction(id: string, title: string, iconClas group: 'navigation', order }); -} - -// Touch Bar -if (isMacintosh) { - MenuRegistry.appendMenuItem(MenuId.TouchBarContext, { - command: { id: GlobalNewUntitledFileAction.ID, title: GlobalNewUntitledFileAction.LABEL, iconPath: URI.parse(require.toUrl('vs/workbench/parts/files/browser/media/new-file-tb.png')).fsPath }, - group: '1_modification' - }); } \ No newline at end of file diff --git a/src/vs/workbench/parts/files/browser/fileActions.ts b/src/vs/workbench/parts/files/browser/fileActions.ts index b4a7816f4e1..fa5d98003d8 100644 --- a/src/vs/workbench/parts/files/browser/fileActions.ts +++ b/src/vs/workbench/parts/files/browser/fileActions.ts @@ -11,6 +11,7 @@ import nls = require('vs/nls'); import { isWindows, isLinux, isMacintosh } from 'vs/base/common/platform'; import { sequence, ITask } from 'vs/base/common/async'; import paths = require('vs/base/common/paths'); +import resources = require('vs/base/common/resources'); import URI from 'vs/base/common/uri'; import errors = require('vs/base/common/errors'); import { toErrorMessage } from 'vs/base/common/errorMessage'; @@ -296,22 +297,20 @@ class RenameFileAction extends BaseRenameAction { } public runAction(newName: string): TPromise { - - const dirty = this.textFileService.getDirty().filter(d => paths.isEqualOrParent(d.fsPath, this.element.resource.fsPath, !isLinux /* ignorecase */)); + const dirty = this.textFileService.getDirty().filter(d => resources.isEqualOrParent(d, this.element.resource, !isLinux /* ignorecase */)); const dirtyRenamed: URI[] = []; return TPromise.join(dirty.map(d => { - - const targetPath = paths.join(this.element.parent.resource.fsPath, newName); let renamed: URI; // If the dirty file itself got moved, just reparent it to the target folder - if (paths.isEqual(this.element.resource.fsPath, d.fsPath)) { - renamed = URI.file(targetPath); + const targetPath = paths.join(this.element.parent.resource.path, newName); + if (this.element.resource.toString() === d.toString()) { + renamed = this.element.parent.resource.with({ path: targetPath }); } // Otherwise, a parent of the dirty resource got moved, so we have to reparent more complicated. Example: else { - renamed = URI.file(paths.join(targetPath, d.fsPath.substr(this.element.resource.fsPath.length + 1))); + renamed = this.element.parent.resource.with({ path: paths.join(targetPath, d.path.substr(this.element.resource.path.length + 1)) }); } dirtyRenamed.push(renamed); @@ -589,7 +588,8 @@ export class CreateFileAction extends BaseCreateAction { } public runAction(fileName: string): TPromise { - return this.fileService.createFile(URI.file(paths.join(this.element.parent.resource.fsPath, fileName))).then(stat => { + const resource = this.element.parent.resource; + return this.fileService.createFile(resource.with({ path: paths.join(resource.path, fileName) })).then(stat => { return this.editorService.openEditor({ resource: stat.resource, options: { pinned: true } }); }, (error) => { this.onErrorWithRetry(error, () => this.runAction(fileName)); @@ -615,7 +615,8 @@ export class CreateFolderAction extends BaseCreateAction { } public runAction(fileName: string): TPromise { - return this.fileService.createFolder(URI.file(paths.join(this.element.parent.resource.fsPath, fileName))).then(null, (error) => { + const resource = this.element.parent.resource; + return this.fileService.createFolder(resource.with({ path: paths.join(resource.path, fileName) })).then(null, (error) => { this.onErrorWithRetry(error, () => this.runAction(fileName)); }); } @@ -673,7 +674,7 @@ export class BaseDeleteFileAction extends BaseFileAction { // Handle dirty let revertPromise: TPromise = TPromise.as(null); - const dirty = this.textFileService.getDirty().filter(d => paths.isEqualOrParent(d.fsPath, this.element.resource.fsPath, !isLinux /* ignorecase */)); + const dirty = this.textFileService.getDirty().filter(d => resources.isEqualOrParent(d, this.element.resource, !isLinux /* ignorecase */)); if (dirty.length) { let message: string; if (this.element.isDirectory) { @@ -798,10 +799,9 @@ export class ImportFileAction extends BaseFileAction { return this.tree; } - public run(context?: any): TPromise { + public run(resources: URI[]): TPromise { const importPromise = TPromise.as(null).then(() => { - const input = context.input as { paths: string[] }; - if (input.paths && input.paths.length > 0) { + if (resources && resources.length > 0) { // Find parent for import let targetElement: FileStat; @@ -826,8 +826,8 @@ export class ImportFileAction extends BaseFileAction { }); let overwrite = true; - if (input.paths.some(path => { - return !!targetNames[isLinux ? paths.basename(path) : paths.basename(path).toLowerCase()]; + if (resources.some(resource => { + return !!targetNames[isLinux ? paths.basename(resource.fsPath) : paths.basename(resource.fsPath).toLowerCase()]; })) { const confirm: IConfirmation = { message: nls.localize('confirmOverwrite', "A file or folder with the same name already exists in the destination folder. Do you want to replace it?"), @@ -845,10 +845,10 @@ export class ImportFileAction extends BaseFileAction { // Run import in sequence const importPromisesFactory: ITask>[] = []; - input.paths.forEach(path => { + resources.forEach(resource => { importPromisesFactory.push(() => { - const sourceFile = URI.file(path); - const targetFile = URI.file(paths.join(targetElement.resource.fsPath, paths.basename(path))); + const sourceFile = resource; + const targetFile = targetElement.resource.with({ path: paths.join(targetElement.resource.path, paths.basename(sourceFile.path)) }); // if the target exists and is dirty, make sure to revert it. otherwise the dirty contents // of the target file would replace the contents of the imported file. since we already @@ -862,7 +862,7 @@ export class ImportFileAction extends BaseFileAction { return this.fileService.importFile(sourceFile, targetElement.resource).then(res => { // if we only import one file, just open it directly - if (input.paths.length === 1) { + if (resources.length === 1) { this.editorService.openEditor({ resource: res.stat.resource, options: { pinned: true } }).done(null, errors.onUnexpectedError); } }, error => this.onError(error)); @@ -964,7 +964,7 @@ export class PasteFileAction extends BaseFileAction { } // Check if target is ancestor of pasted folder - if (!paths.isEqual(this.element.resource.fsPath, fileToCopy.resource.fsPath) && paths.isEqualOrParent(this.element.resource.fsPath, fileToCopy.resource.fsPath, !isLinux /* ignorecase */)) { + if (this.element.resource.toString() !== fileToCopy.resource.toString() && resources.isEqualOrParent(this.element.resource, fileToCopy.resource, !isLinux /* ignorecase */)) { return false; } @@ -1047,14 +1047,14 @@ export class DuplicateFileAction extends BaseFileAction { private findTarget(): URI { let name = this.element.name; - let candidate = URI.file(paths.join(this.target.resource.fsPath, name)); + let candidate = this.target.resource.with({ path: paths.join(this.target.resource.fsPath, name) }); while (true) { if (!this.element.root.find(candidate)) { break; } name = this.toCopyName(name, this.element.isDirectory); - candidate = URI.file(paths.join(this.target.resource.fsPath, name)); + candidate = this.target.resource.with({ path: paths.join(this.target.resource.fsPath, name) }); } return candidate; @@ -1210,7 +1210,7 @@ export class GlobalCompareResourcesAction extends Action { } label = paths.basename(resource.fsPath); - description = resource.scheme === 'file' ? labels.getPathLabel(paths.dirname(resource.fsPath), this.contextService, this.environmentService) : void 0; + description = labels.getPathLabel(resources.dirname(resource), this.contextService, this.environmentService); return { input, resource, label, description }; }).filter(p => !!p); @@ -1370,7 +1370,8 @@ export abstract class BaseSaveOneFileAction extends BaseSaveFileAction { if (this.resource) { source = this.resource; } else { - source = toResource(this.editorService.getActiveEditorInput(), { supportSideBySide: true, filter: ['file', 'untitled'] }); + // source = toResource(this.editorService.getActiveEditorInput(), { supportSideBySide: true, filter: ['file', 'untitled'] }); + source = toResource(this.editorService.getActiveEditorInput(), { supportSideBySide: true }); } if (source) { diff --git a/src/vs/workbench/parts/files/browser/fileCommands.ts b/src/vs/workbench/parts/files/browser/fileCommands.ts index fc05aad6d82..ffad1a5c8b5 100644 --- a/src/vs/workbench/parts/files/browser/fileCommands.ts +++ b/src/vs/workbench/parts/files/browser/fileCommands.ts @@ -22,7 +22,6 @@ import { FileStat, OpenEditor } from 'vs/workbench/parts/files/common/explorerMo import errors = require('vs/base/common/errors'); import { ITree } from 'vs/base/parts/tree/browser/tree'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; -import labels = require('vs/base/common/labels'); import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; import { IMessageService } from 'vs/platform/message/common/message'; @@ -44,7 +43,7 @@ export const copyPathCommand = (accessor: ServicesAccessor, resource?: URI) => { if (resource) { const clipboardService = accessor.get(IClipboardService); - clipboardService.writeText(labels.getPathLabel(resource)); + clipboardService.writeText(resource.scheme === 'file' ? resource.fsPath : resource.toString()); } else { const messageService = accessor.get(IMessageService); messageService.show(severity.Info, nls.localize('openFileToCopy', "Open a file first to copy its path")); diff --git a/src/vs/workbench/parts/files/browser/files.contribution.ts b/src/vs/workbench/parts/files/browser/files.contribution.ts index 6e1442f74e6..c7def3d064a 100644 --- a/src/vs/workbench/parts/files/browser/files.contribution.ts +++ b/src/vs/workbench/parts/files/browser/files.contribution.ts @@ -29,8 +29,8 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import * as platform from 'vs/base/common/platform'; -import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { DirtyFilesTracker } from 'vs/workbench/parts/files/common/dirtyFilesTracker'; +import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration'; // Viewlet Action export class OpenExplorerViewletAction extends ToggleViewletAction { @@ -113,7 +113,7 @@ interface ISerializedFileInput { class FileEditorInputFactory implements IEditorInputFactory { constructor( - @IWorkspaceConfigurationService private configurationService: IWorkspaceConfigurationService + @ITextResourceConfigurationService private configurationService: ITextResourceConfigurationService ) { } @@ -122,14 +122,10 @@ class FileEditorInputFactory implements IEditorInputFactory { const resource = fileEditorInput.getResource(); const fileInput: ISerializedFileInput = { resource: resource.toString(), // Keep for backwards compatibility - resourceJSON: resource.toJSON() + resourceJSON: resource.toJSON(), + encoding: fileEditorInput.getEncoding() }; - const encoding = fileEditorInput.getPreferredEncoding(); - if (encoding && encoding !== this.configurationService.lookup('files.encoding', { resource }).value) { - fileInput.encoding = encoding; - } - return JSON.stringify(fileInput); } diff --git a/src/vs/workbench/parts/files/browser/views/explorerView.ts b/src/vs/workbench/parts/files/browser/views/explorerView.ts index 1e144561a57..1a84751abf2 100644 --- a/src/vs/workbench/parts/files/browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/browser/views/explorerView.ts @@ -10,8 +10,8 @@ import { Builder, $ } from 'vs/base/browser/builder'; import URI from 'vs/base/common/uri'; import { ThrottledDelayer } from 'vs/base/common/async'; import errors = require('vs/base/common/errors'); -import labels = require('vs/base/common/labels'); import paths = require('vs/base/common/paths'); +import resources = require('vs/base/common/resources'); import glob = require('vs/base/common/glob'); import { Action, IAction } from 'vs/base/common/actions'; import { prepareActions } from 'vs/workbench/browser/actions'; @@ -32,7 +32,7 @@ import { FileStat, Model } from 'vs/workbench/parts/files/common/explorerModel'; import { IListService } from 'vs/platform/list/browser/listService'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IPartService } from 'vs/workbench/services/part/common/partService'; -import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -131,8 +131,7 @@ export class ExplorerView extends ViewsViewletPanel { const titleElement = container.querySelector('.title') as HTMLElement; const setHeader = () => { const workspace = this.contextService.getWorkspace(); - - const title = workspace.folders.map(folder => labels.getPathLabel(folder.uri.fsPath, void 0, this.environmentService)).join(); + const title = workspace.folders.map(folder => folder.name).join(); titleElement.textContent = this.name; titleElement.title = title; }; @@ -165,7 +164,7 @@ export class ExplorerView extends ViewsViewletPanel { }; this.disposables.push(this.themeService.onDidFileIconThemeChange(onFileIconThemeChange)); - this.disposables.push(this.contextService.onDidChangeWorkspaceFolders(() => this.refreshFromEvent())); + this.disposables.push(this.contextService.onDidChangeWorkspaceFolders(e => this.refreshFromEvent(e.added))); onFileIconThemeChange(this.themeService.getFileIconTheme()); } @@ -193,7 +192,11 @@ export class ExplorerView extends ViewsViewletPanel { this.onConfigurationUpdated(configuration); // Load and Fill Viewer - return this.doRefresh().then(() => { + let targetsToExpand = []; + if (this.settings[ExplorerView.MEMENTO_EXPANDED_FOLDER_RESOURCES]) { + targetsToExpand = this.settings[ExplorerView.MEMENTO_EXPANDED_FOLDER_RESOURCES].map((e: string) => URI.parse(e)); + } + return this.doRefresh(targetsToExpand).then(() => { // When the explorer viewer is loaded, listen to changes to the editor input this.disposables.push(this.editorGroupService.onEditorsChanged(() => this.onEditorsChanged())); @@ -366,7 +369,7 @@ export class ExplorerView extends ViewsViewletPanel { } // check for files - return toResource(input, { supportSideBySide: true, filter: 'file' }); + return toResource(input, { supportSideBySide: true }); } private get isCreated(): boolean { @@ -457,7 +460,7 @@ export class ExplorerView extends ViewsViewletPanel { // Add if (e.operation === FileOperation.CREATE || e.operation === FileOperation.IMPORT || e.operation === FileOperation.COPY) { const addedElement = e.target; - const parentResource = URI.file(paths.dirname(addedElement.resource.fsPath)); + const parentResource = resources.dirname(addedElement.resource); const parents = this.model.findAll(parentResource); if (parents.length) { @@ -492,8 +495,8 @@ export class ExplorerView extends ViewsViewletPanel { const oldResource = e.resource; const newElement = e.target; - const oldParentResource = URI.file(paths.dirname(oldResource.fsPath)); - const newParentResource = URI.file(paths.dirname(newElement.resource.fsPath)); + const oldParentResource = resources.dirname(oldResource); + const newParentResource = resources.dirname(newElement.resource); // Only update focus if renamed/moved element is selected let restoreFocus = false; @@ -677,11 +680,11 @@ export class ExplorerView extends ViewsViewletPanel { })); } - private refreshFromEvent(): void { + private refreshFromEvent(newRoots: IWorkspaceFolder[] = []): void { if (this.isVisible()) { this.explorerRefreshDelayer.trigger(() => { if (!this.explorerViewer.getHighlight()) { - return this.doRefresh(); + return this.doRefresh(newRoots.map(r => r.uri)); } return TPromise.as(null); @@ -723,20 +726,13 @@ export class ExplorerView extends ViewsViewletPanel { }); } - private doRefresh(): TPromise { + private doRefresh(targetsToExpand: URI[] = []): TPromise { const targetsToResolve: { root: FileStat, resource: URI, options: { resolveTo: URI[] } }[] = []; this.model.roots.forEach(root => { const rootAndTargets = { root, resource: root.resource, options: { resolveTo: [] } }; targetsToResolve.push(rootAndTargets); }); - let targetsToExpand: URI[] = []; - if (this.settings[ExplorerView.MEMENTO_EXPANDED_FOLDER_RESOURCES]) { - targetsToExpand = this.settings[ExplorerView.MEMENTO_EXPANDED_FOLDER_RESOURCES].map((e: string) => URI.parse(e)); - } else if (this.model.roots.length === 1) { - targetsToExpand = this.model.roots.map(root => root.resource); // always expand if there is just one root - } - // First time refresh: Receive target through active editor input or selection and also include settings from previous session if (!this.isCreated) { const activeFile = this.getActiveFile(); @@ -774,7 +770,7 @@ export class ExplorerView extends ViewsViewletPanel { return FileStat.create({ resource: targetsToResolve[index].resource, - name: paths.basename(targetsToResolve[index].resource.fsPath), + name: resources.basenameOrAuthority(targetsToResolve[index].resource), mtime: 0, etag: undefined, isDirectory: true, @@ -785,16 +781,11 @@ export class ExplorerView extends ViewsViewletPanel { modelStats.forEach((modelStat, index) => FileStat.mergeLocalWithDisk(modelStat, this.model.roots[index])); const input = this.contextService.getWorkbenchState() === WorkbenchState.FOLDER ? this.model.roots[0] : this.model; + const statsToExpand = this.explorerViewer.getExpandedElements().concat(targetsToExpand.map(target => this.model.findClosest(target))); if (input === this.explorerViewer.getInput()) { - return this.explorerViewer.refresh(); + return this.explorerViewer.refresh().then(() => this.explorerViewer.expandAll(statsToExpand)); } - // Preserve expanded elements if tree input changed. - // If it is a brand new tree just expand elements from memento - const expanded = this.explorerViewer.getExpandedElements(); - const statsToExpand = expanded.length ? [this.model.roots[0]].concat(expanded) : - targetsToExpand.map(expand => this.model.findClosest(expand)); - // Display roots only when multi folder workspace // Make sure to expand all folders that where expanded in the previous session return this.explorerViewer.setInput(input).then(() => this.explorerViewer.expandAll(statsToExpand)); @@ -815,7 +806,7 @@ export class ExplorerView extends ViewsViewletPanel { // Drop those path which are parents of the current one for (let i = resolvedDirectories.length - 1; i >= 0; i--) { const resource = resolvedDirectories[i]; - if (paths.isEqualOrParent(stat.resource.fsPath, resource.fsPath, !isLinux /* ignorecase */)) { + if (resources.isEqualOrParent(stat.resource, resource, !isLinux /* ignorecase */)) { resolvedDirectories.splice(i); } } diff --git a/src/vs/workbench/parts/files/browser/views/explorerViewer.ts b/src/vs/workbench/parts/files/browser/views/explorerViewer.ts index 960e7d8dd8a..a98fe094684 100644 --- a/src/vs/workbench/parts/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/parts/files/browser/views/explorerViewer.ts @@ -13,6 +13,7 @@ import URI from 'vs/base/common/uri'; import { MIME_BINARY } from 'vs/base/common/mime'; import { once } from 'vs/base/common/functional'; import paths = require('vs/base/common/paths'); +import resources = require('vs/base/common/resources'); import errors = require('vs/base/common/errors'); import { isString } from 'vs/base/common/types'; import { IAction, ActionRunner as BaseActionRunner, IActionRunner } from 'vs/base/common/actions'; @@ -350,9 +351,9 @@ export class FileRenderer implements IRenderer { }); const styler = attachInputBoxStyler(inputBox, this.themeService); - const parent = paths.dirname(stat.resource.fsPath); + const parent = resources.dirname(stat.resource); inputBox.onDidChange(value => { - label.setFile(URI.file(paths.join(parent, value)), labelOptions); // update label icon while typing! + label.setFile(parent.with({ path: paths.join(parent.path, value) }), labelOptions); // update label icon while typing! }); const value = stat.name || ''; @@ -752,7 +753,7 @@ export class FileDragAndDrop extends SimpleFileResourceDragAndDrop { } if (stat.isDirectory) { - return URI.from({ scheme: 'folder', path: stat.resource.fsPath }); // indicates that we are dragging a folder + return URI.from({ scheme: 'folder', path: stat.resource.path }); // indicates that we are dragging a folder } return stat.resource; @@ -836,11 +837,11 @@ export class FileDragAndDrop extends SimpleFileResourceDragAndDrop { return true; // Can not move anything onto itself } - if (!isCopy && paths.isEqual(paths.dirname(source.resource.fsPath), target.resource.fsPath)) { + if (!isCopy && resources.dirname(source.resource).toString() === target.resource.toString()) { return true; // Can not move a file to the same parent unless we copy } - if (paths.isEqualOrParent(target.resource.fsPath, source.resource.fsPath, !isLinux /* ignorecase */)) { + if (resources.isEqualOrParent(target.resource, source.resource, !isLinux /* ignorecase */)) { return true; // Can not move a parent folder into one of its children } @@ -928,9 +929,8 @@ export class FileDragAndDrop extends SimpleFileResourceDragAndDrop { // Handle dropped files (only support FileStat as target) else if (target instanceof FileStat) { const importAction = this.instantiationService.createInstance(ImportFileAction, tree, target, null); - return importAction.run({ - input: { paths: droppedResources.map(res => res.resource.fsPath) } - }); + + return importAction.run(droppedResources.map(res => res.resource)); } return void 0; @@ -962,18 +962,18 @@ export class FileDragAndDrop extends SimpleFileResourceDragAndDrop { }; // 1. check for dirty files that are being moved and backup to new target - const dirty = this.textFileService.getDirty().filter(d => paths.isEqualOrParent(d.fsPath, source.resource.fsPath, !isLinux /* ignorecase */)); + const dirty = this.textFileService.getDirty().filter(d => resources.isEqualOrParent(d, source.resource, !isLinux /* ignorecase */)); return TPromise.join(dirty.map(d => { let moved: URI; // If the dirty file itself got moved, just reparent it to the target folder - if (paths.isEqual(source.resource.fsPath, d.fsPath)) { - moved = URI.file(paths.join(target.resource.fsPath, source.name)); + if (source.resource.toString() === d.toString()) { + moved = target.resource.with({ path: paths.join(target.resource.path, source.name) }); } // Otherwise, a parent of the dirty resource got moved, so we have to reparent more complicated. Example: else { - moved = URI.file(paths.join(target.resource.fsPath, d.fsPath.substr(source.parent.resource.fsPath.length + 1))); + moved = target.resource.with({ path: paths.join(target.resource.path, d.path.substr(source.parent.resource.path.length + 1)) }); } dirtyMoved.push(moved); @@ -988,7 +988,7 @@ export class FileDragAndDrop extends SimpleFileResourceDragAndDrop { // 3.) run the move operation .then(() => { - const targetResource = URI.file(paths.join(target.resource.fsPath, source.name)); + const targetResource = target.resource.with({ path: paths.join(target.resource.path, source.name) }); let didHandleConflict = false; return this.fileService.moveFile(source.resource, targetResource).then(null, error => { @@ -1006,7 +1006,7 @@ export class FileDragAndDrop extends SimpleFileResourceDragAndDrop { // Move with overwrite if the user confirms if (this.messageService.confirm(confirm)) { - const targetDirty = this.textFileService.getDirty().filter(d => paths.isEqualOrParent(d.fsPath, targetResource.fsPath, !isLinux /* ignorecase */)); + const targetDirty = this.textFileService.getDirty().filter(d => resources.isEqualOrParent(d, targetResource, !isLinux /* ignorecase */)); // Make sure to revert all dirty in target first to be able to overwrite properly return this.textFileService.revertAll(targetDirty, { soft: true /* do not attempt to load content from disk */ }).then(() => { diff --git a/src/vs/workbench/parts/files/common/editors/fileEditorInput.ts b/src/vs/workbench/parts/files/common/editors/fileEditorInput.ts index 1393994840d..d3930c9fcff 100644 --- a/src/vs/workbench/parts/files/common/editors/fileEditorInput.ts +++ b/src/vs/workbench/parts/files/common/editors/fileEditorInput.ts @@ -6,7 +6,9 @@ import { localize } from 'vs/nls'; import { TPromise } from 'vs/base/common/winjs.base'; +import { memoize } from 'vs/base/common/decorators'; import paths = require('vs/base/common/paths'); +import resources = require('vs/base/common/resources'); import labels = require('vs/base/common/labels'); import URI from 'vs/base/common/uri'; import { EncodingMode, ConfirmResult, EditorInput, IFileEditorInput, ITextEditorModel } from 'vs/workbench/common/editor'; @@ -33,14 +35,6 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput { private name: string; - private shortDescription: string; - private mediumDescription: string; - private longDescription: string; - - private shortTitle: string; - private mediumTitle: string; - private longTitle: string; - private toUnbind: IDisposable[]; /** @@ -124,41 +118,72 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput { public getName(): string { if (!this.name) { - this.name = paths.basename(this.resource.fsPath); + this.name = resources.basenameOrAuthority(this.resource); } return this.decorateOrphanedFiles(this.name); } + @memoize + private get shortDescription(): string { + + return paths.basename(labels.getPathLabel(resources.dirname(this.resource), void 0, this.environmentService)); + } + + @memoize + private get mediumDescription(): string { + return labels.getPathLabel(resources.dirname(this.resource), this.contextService, this.environmentService); + } + + @memoize + private get longDescription(): string { + return labels.getPathLabel(resources.dirname(this.resource), void 0, this.environmentService); + } + public getDescription(verbosity: Verbosity = Verbosity.MEDIUM): string { let description: string; switch (verbosity) { case Verbosity.SHORT: - description = this.shortDescription ? this.shortDescription : (this.shortDescription = paths.basename(labels.getPathLabel(paths.dirname(this.resource.fsPath), void 0, this.environmentService))); + description = this.shortDescription; break; case Verbosity.LONG: - description = this.longDescription ? this.longDescription : (this.longDescription = labels.getPathLabel(paths.dirname(this.resource.fsPath), void 0, this.environmentService)); + description = this.longDescription; break; case Verbosity.MEDIUM: default: - description = this.mediumDescription ? this.mediumDescription : (this.mediumDescription = labels.getPathLabel(paths.dirname(this.resource.fsPath), this.contextService, this.environmentService)); + description = this.mediumDescription; break; } return description; } + @memoize + private get shortTitle(): string { + return this.getName(); + } + + @memoize + private get mediumTitle(): string { + return labels.getPathLabel(this.resource, this.contextService, this.environmentService); + } + + @memoize + private get longTitle(): string { + return labels.getPathLabel(this.resource, void 0, this.environmentService); + } + public getTitle(verbosity: Verbosity): string { let title: string; switch (verbosity) { case Verbosity.SHORT: - title = this.shortTitle ? this.shortTitle : (this.shortTitle = this.getName()); + title = this.shortTitle; break; case Verbosity.MEDIUM: - title = this.mediumTitle ? this.mediumTitle : (this.mediumTitle = labels.getPathLabel(this.resource, this.contextService, this.environmentService)); + title = this.mediumTitle; break; case Verbosity.LONG: - title = this.longTitle ? this.longTitle : (this.longTitle = labels.getPathLabel(this.resource, void 0, this.environmentService)); + title = this.longTitle; break; } diff --git a/src/vs/workbench/parts/files/common/editors/fileEditorTracker.ts b/src/vs/workbench/parts/files/common/editors/fileEditorTracker.ts index 52124b9fb7e..42afd62358c 100644 --- a/src/vs/workbench/parts/files/common/editors/fileEditorTracker.ts +++ b/src/vs/workbench/parts/files/common/editors/fileEditorTracker.ts @@ -24,6 +24,8 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { isLinux } from 'vs/base/common/platform'; import { ResourceQueue } from 'vs/base/common/async'; +import { ResourceMap } from 'vs/base/common/map'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; export class FileEditorTracker implements IWorkbenchContribution { @@ -32,6 +34,7 @@ export class FileEditorTracker implements IWorkbenchContribution { private stacks: IEditorStacksModel; private toUnbind: IDisposable[]; private modelLoadQueue: ResourceQueue; + private activeOutOfWorkspaceWatchers: ResourceMap; constructor( @IWorkbenchEditorService private editorService: IWorkbenchEditorService, @@ -40,11 +43,13 @@ export class FileEditorTracker implements IWorkbenchContribution { @IEditorGroupService private editorGroupService: IEditorGroupService, @IFileService private fileService: IFileService, @IEnvironmentService private environmentService: IEnvironmentService, - @IConfigurationService private configurationService: IConfigurationService + @IConfigurationService private configurationService: IConfigurationService, + @IWorkspaceContextService private contextService: IWorkspaceContextService, ) { this.toUnbind = []; this.stacks = editorGroupService.getStacksModel(); this.modelLoadQueue = new ResourceQueue(); + this.activeOutOfWorkspaceWatchers = new ResourceMap(); this.onConfigurationUpdated(configurationService.getConfiguration()); @@ -63,6 +68,9 @@ export class FileEditorTracker implements IWorkbenchContribution { // Update editors from disk changes this.toUnbind.push(this.fileService.onFileChanges(e => this.onFileChanges(e))); + // Editor changing + this.toUnbind.push(this.editorGroupService.onEditorsChanged(() => this.onEditorsChanged())); + // Lifecycle this.lifecycleService.onShutdown(this.dispose, this); @@ -205,8 +213,8 @@ export class FileEditorTracker implements IWorkbenchContribution { if (oldResource.toString() === resource.toString()) { reopenFileResource = newResource; // file got moved } else { - const index = indexOf(resource.fsPath, oldResource.fsPath, !isLinux /* ignorecase */); - reopenFileResource = URI.file(paths.join(newResource.fsPath, resource.fsPath.substr(index + oldResource.fsPath.length + 1))); // parent folder got moved + const index = indexOf(resource.path, oldResource.path, !isLinux /* ignorecase */); + reopenFileResource = newResource.with({ path: paths.join(newResource.path, resource.path.substr(index + oldResource.path.length + 1)) }); // parent folder got moved } // Reopen @@ -233,8 +241,8 @@ export class FileEditorTracker implements IWorkbenchContribution { for (let i = 0; i < editors.length; i++) { const editor = editors[i]; if (editor && editor.position === stacks.positionOfGroup(group)) { - const resource = toResource(editor.input, { filter: 'file' }); - if (resource && paths.isEqual(resource.fsPath, resource.fsPath)) { + const editorResource = toResource(editor.input); + if (editorResource && resource.toString() === editorResource.toString()) { const control = editor.getControl(); if (isCommonCodeEditor(control)) { return control.saveViewState(); @@ -290,7 +298,42 @@ export class FileEditorTracker implements IWorkbenchContribution { } } + private onEditorsChanged(): void { + this.handleOutOfWorkspaceWatchers(); + } + + private handleOutOfWorkspaceWatchers(): void { + const visibleOutOfWorkspacePaths = new ResourceMap(); + this.editorService.getVisibleEditors().map(editor => { + return toResource(editor.input, { supportSideBySide: true, filter: 'file' }); + }).filter(fileResource => { + return !!fileResource && !this.contextService.isInsideWorkspace(fileResource); + }).forEach(resource => { + visibleOutOfWorkspacePaths.set(resource, resource); + }); + + // Handle no longer visible out of workspace resources + this.activeOutOfWorkspaceWatchers.forEach(resource => { + if (!visibleOutOfWorkspacePaths.get(resource)) { + this.fileService.unwatchFileChanges(resource); + this.activeOutOfWorkspaceWatchers.delete(resource); + } + }); + + // Handle newly visible out of workspace resources + visibleOutOfWorkspacePaths.forEach(resource => { + if (!this.activeOutOfWorkspaceWatchers.get(resource)) { + this.fileService.watchFileChanges(resource); + this.activeOutOfWorkspaceWatchers.set(resource, resource); + } + }); + } + public dispose(): void { this.toUnbind = dispose(this.toUnbind); + + // Dispose watchers if any + this.activeOutOfWorkspaceWatchers.forEach(resource => this.fileService.unwatchFileChanges(resource)); + this.activeOutOfWorkspaceWatchers.clear(); } -} \ No newline at end of file +} diff --git a/src/vs/workbench/parts/files/common/explorerModel.ts b/src/vs/workbench/parts/files/common/explorerModel.ts index c29cd245f3d..0dfa64420eb 100644 --- a/src/vs/workbench/parts/files/common/explorerModel.ts +++ b/src/vs/workbench/parts/files/common/explorerModel.ts @@ -273,7 +273,8 @@ export class FileStat implements IFileStat { } private updateResource(recursive: boolean): void { - this.resource = URI.file(paths.join(this.parent.resource.fsPath, this.name)); + this.resource = this.parent.resource.with({ path: paths.join(this.parent.resource.path, this.name) }); + // this.resource = URI.file(paths.join(this.parent.resource.fsPath, this.name)); if (recursive) { if (this.isDirectory && this.hasChildren && this.children) { @@ -434,4 +435,4 @@ export class OpenEditor { public getResource(): URI { return toResource(this.editor, { supportSideBySide: true, filter: ['file', 'untitled'] }); } -} \ No newline at end of file +} diff --git a/src/vs/workbench/parts/html/browser/html.contribution.ts b/src/vs/workbench/parts/html/browser/html.contribution.ts index 0362b085b92..0e1fa1374bb 100644 --- a/src/vs/workbench/parts/html/browser/html.contribution.ts +++ b/src/vs/workbench/parts/html/browser/html.contribution.ts @@ -27,7 +27,7 @@ function getActivePreviewsForResource(accessor: ServicesAccessor, resource: URI return accessor.get(IWorkbenchEditorService).getVisibleEditors() .filter(c => c instanceof HtmlPreviewPart && c.model) .map(e => e as HtmlPreviewPart) - .filter(e => e.model.uri.scheme === uri.scheme && e.model.uri.fsPath === uri.fsPath); + .filter(e => e.model.uri.scheme === uri.scheme && e.model.uri.toString() === uri.toString()); } // --- Register Editor diff --git a/src/vs/workbench/parts/html/browser/webviewEditor.ts b/src/vs/workbench/parts/html/browser/webviewEditor.ts index e859ca71b30..60de417f8e7 100644 --- a/src/vs/workbench/parts/html/browser/webviewEditor.ts +++ b/src/vs/workbench/parts/html/browser/webviewEditor.ts @@ -23,7 +23,7 @@ export interface HtmlPreviewEditorViewState { } /** A context key that is set when a webview editor has focus. */ -export const KEYBINDING_CONTEXT_WEBVIEWEDITOR_FOCUS = new RawContextKey('webviewEditorFocus', undefined); +export const KEYBINDING_CONTEXT_WEBVIEWEDITOR_FOCUS = new RawContextKey('webviewEditorFocus', false); /** A context key that is set when a webview editor does not have focus. */ export const KEYBINDING_CONTEXT_WEBVIEWEDITOR_NOT_FOCUSED: ContextKeyExpr = KEYBINDING_CONTEXT_WEBVIEWEDITOR_FOCUS.toNegated(); /** A context key that is set when the find widget find input in webview editor webview is focused. */ @@ -31,6 +31,12 @@ export const KEYBINDING_CONTEXT_WEBVIEWEDITOR_FIND_WIDGET_INPUT_FOCUSED = new Ra /** A context key that is set when the find widget find input in webview editor webview is not focused. */ export const KEYBINDING_CONTEXT_WEBVIEWEDITOR_FIND_WIDGET_INPUT_NOT_FOCUSED: ContextKeyExpr = KEYBINDING_CONTEXT_WEBVIEWEDITOR_FIND_WIDGET_INPUT_FOCUSED.toNegated(); +/** A context key that is set when the find widget in a webview is visible. */ +export const KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE = new RawContextKey('webviewFindWidgetVisible', false); +/** A context key that is set when the find widget in a webview is not visible. */ +export const KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_NOT_VISIBLE: ContextKeyExpr = KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE.toNegated(); + + /** * This class is only intended to be subclassed and not instantiated. */ @@ -40,6 +46,7 @@ export abstract class WebviewEditor extends BaseWebviewEditor { protected _webview: WebView; protected content: HTMLElement; protected contextKey: IContextKey; + private findWidgetVisible: IContextKey; protected findInputFocusContextKey: IContextKey; constructor( @@ -53,16 +60,19 @@ export abstract class WebviewEditor extends BaseWebviewEditor { if (contextKeyService) { this.contextKey = KEYBINDING_CONTEXT_WEBVIEWEDITOR_FOCUS.bindTo(contextKeyService); this.findInputFocusContextKey = KEYBINDING_CONTEXT_WEBVIEWEDITOR_FIND_WIDGET_INPUT_FOCUSED.bindTo(contextKeyService); + this.findWidgetVisible = KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE.bindTo(contextKeyService); } } public showFind() { if (this._webview) { this._webview.showFind(); + this.findWidgetVisible.set(true); } } public hideFind() { + this.findWidgetVisible.reset(); if (this._webview) { this._webview.hideFind(); } @@ -137,7 +147,9 @@ class HideWebViewEditorFindCommand extends Command { } const hideCommand = new HideWebViewEditorFindCommand({ id: 'editor.action.webvieweditor.hideFind', - precondition: KEYBINDING_CONTEXT_WEBVIEWEDITOR_FOCUS, + precondition: ContextKeyExpr.and( + KEYBINDING_CONTEXT_WEBVIEWEDITOR_FOCUS, + KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE), kbOpts: { primary: KeyCode.Escape } diff --git a/src/vs/workbench/parts/performance/electron-browser/performance.contribution.ts b/src/vs/workbench/parts/performance/electron-browser/performance.contribution.ts index 8b04c59fd2f..fb56e3ed9b7 100644 --- a/src/vs/workbench/parts/performance/electron-browser/performance.contribution.ts +++ b/src/vs/workbench/parts/performance/electron-browser/performance.contribution.ts @@ -5,16 +5,12 @@ 'use strict'; -import product from 'vs/platform/node/product'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IExtensionService } from 'vs/platform/extensions/common/extensions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IMessageService } from 'vs/platform/message/common/message'; import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { ITimerService } from 'vs/workbench/services/timer/common/timerService'; import { IWindowsService } from 'vs/platform/windows/common/windows'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IWorkbenchContributionsRegistry, IWorkbenchContribution, Extensions } from 'vs/workbench/common/contributions'; import { Registry } from 'vs/platform/registry/common/platform'; import { ReportPerformanceIssueAction } from 'vs/workbench/electron-browser/actions'; @@ -22,125 +18,8 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { join } from 'path'; import { localize } from 'vs/nls'; import { toPromise, filterEvent } from 'vs/base/common/event'; -import { platform, Platform } from 'vs/base/common/platform'; import { readdir } from 'vs/base/node/pfs'; -import { release } from 'os'; import { stopProfiling } from 'vs/base/node/profiler'; -import { virtualMachineHint } from 'vs/base/node/id'; - -class ProfilingHint implements IWorkbenchContribution { - - // p95 to p95 by os&release - static readonly _percentiles: { [key: string]: [number, number] } = { - ['Windows_6.3.9600']: [35782, 35782], - ['Windows_6.1.7601']: [11160, 18366], - ['Windows_10.0.16199']: [10423, 17222], - ['Windows_10.0.16193']: [7503, 11033], - ['Windows_10.0.16188']: [8544, 8807], - ['Windows_10.0.15063']: [11085, 16837], - ['Windows_10.0.14393']: [12585, 32662], - ['Windows_10.0.10586']: [7047, 10944], - ['Windows_10.0.10240']: [16176, 16176], - ['Mac_16.7.0']: [2192, 4050], - ['Mac_16.6.0']: [8043, 10608], - ['Mac_16.5.0']: [4912, 11348], - ['Mac_16.4.0']: [3900, 4200], - ['Mac_16.3.0']: [7327, 7327], - ['Mac_16.1.0']: [6090, 6555], - ['Mac_16.0.0']: [32574, 32574], - ['Mac_15.6.0']: [16082, 17469], - ['Linux_4.9.0-3-amd64']: [2092, 2197], - ['Linux_4.9.0-2-amd64']: [9779, 9779], - ['Linux_4.8.0-52-generic']: [12803, 13257], - ['Linux_4.8.0-51-generic']: [2670, 2797], - ['Linux_4.8.0-040800-generic']: [3954, 3954], - ['Linux_4.4.0-78-generic']: [4218, 5891], - ['Linux_4.4.0-77-generic']: [6166, 6166], - ['Linux_4.11.2']: [1323, 1323], - ['Linux_4.10.15-200.fc25.x86_64']: [9270, 9480], - ['Linux_4.10.13-1-ARCH']: [7116, 8511], - ['Linux_4.10.11-100.fc24.x86_64']: [1845, 1845], - ['Linux_4.10.0-21-generic']: [14805, 16050], - ['Linux_3.19.0-84-generic']: [4840, 4840], - ['Linux_3.11.10-29-desktop']: [1637, 2891], - }; - - private static readonly _myPercentiles = ProfilingHint._percentiles[`${Platform[platform]}_${release()}`]; - - constructor( - @IWindowsService private readonly _windowsService: IWindowsService, - @ITimerService private readonly _timerService: ITimerService, - @IMessageService private readonly _messageService: IMessageService, - @IEnvironmentService private readonly _envService: IEnvironmentService, - @IStorageService private readonly _storageService: IStorageService, - @ITelemetryService private readonly _telemetryService: ITelemetryService, - ) { - - setTimeout(() => this._checkTimersAndSuggestToProfile(), 5000); - } - - getId(): string { - return 'performance.ProfilingHint'; - } - - private _checkTimersAndSuggestToProfile() { - - // Only initial startups, not when already profiling - if (!this._timerService.isInitialStartup || this._envService.args['prof-startup']) { - return; - } - - // Check that we have some data about this - // OS version to which we can compare this startup. - // Then only go for startups between the 90 and - // 95th percentile. - if (!Array.isArray(ProfilingHint._myPercentiles)) { - return; - } - const [p80, p90] = ProfilingHint._myPercentiles; - const { ellapsed } = this._timerService.startupMetrics; - if (ellapsed < p80 || ellapsed > p90) { - return; - } - - // Ignore virtual machines and only ask users - // to profile with a certain propability - if (virtualMachineHint.value() >= .5 || Math.ceil(Math.random() * 1000) !== 1) { - return; - } - - // Don't ask for the stable version, only - // ask once per version/build - if (this._envService.appQuality === 'stable') { - // don't ask in stable - return; - } - const mementoKey = `performance.didPromptToProfile.${product.commit}`; - const value = this._storageService.get(mementoKey, StorageScope.GLOBAL, undefined); - if (value !== undefined) { - // only ask once per version - return; - } - - const profile = this._messageService.confirm({ - type: 'info', - message: localize('slow', "Slow startup detected"), - detail: localize('slow.detail', "Sorry that you just had a slow startup. Please restart '{0}' with profiling enabled, share the profiles with us, and we will work hard to make startup great again.", this._envService.appNameLong), - primaryButton: 'Restart and profile' - }); - - this._telemetryService.publicLog('profileStartupInvite', { - acceptedInvite: profile - }); - - if (profile) { - this._storageService.store(mementoKey, 'didProfile', StorageScope.GLOBAL); - this._windowsService.relaunch({ addArgs: ['--prof-startup'] }); - } else { - this._storageService.store(mementoKey, 'didReject', StorageScope.GLOBAL); - } - } -} class StartupProfiler implements IWorkbenchContribution { @@ -214,5 +93,4 @@ class StartupProfiler implements IWorkbenchContribution { } const registry = Registry.as(Extensions.Workbench); -registry.registerWorkbenchContribution(ProfilingHint); registry.registerWorkbenchContribution(StartupProfiler); diff --git a/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts b/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts index acd16f4f9f3..895d2d310e4 100644 --- a/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts +++ b/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts @@ -251,6 +251,10 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor this.searchWidget.focus(); } + clearSearchResults(): void { + this.searchWidget.clear(); + } + showConflicts(keybindingEntry: IKeybindingItemEntry): TPromise { const value = `"${keybindingEntry.keybindingItem.keybinding.getAriaLabel()}"`; if (value !== this.searchWidget.getValue()) { diff --git a/src/vs/workbench/parts/preferences/browser/preferences.contribution.ts b/src/vs/workbench/parts/preferences/browser/preferences.contribution.ts index 5000a14e095..94b32bee5a8 100644 --- a/src/vs/workbench/parts/preferences/browser/preferences.contribution.ts +++ b/src/vs/workbench/parts/preferences/browser/preferences.contribution.ts @@ -20,7 +20,7 @@ import { KeybindingsEditor, KeybindingsEditorInput } from 'vs/workbench/parts/pr import { OpenGlobalSettingsAction, OpenGlobalKeybindingsAction, OpenGlobalKeybindingsFileAction, OpenWorkspaceSettingsAction, OpenFolderSettingsAction, ConfigureLanguageBasedSettingsAction } from 'vs/workbench/parts/preferences/browser/preferencesActions'; import { IPreferencesService, IKeybindingsEditor, CONTEXT_KEYBINDING_FOCUS, CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDINGS_SEARCH_FOCUS, KEYBINDINGS_EDITOR_COMMAND_DEFINE, KEYBINDINGS_EDITOR_COMMAND_REMOVE, KEYBINDINGS_EDITOR_COMMAND_SEARCH, - KEYBINDINGS_EDITOR_COMMAND_COPY, KEYBINDINGS_EDITOR_COMMAND_RESET, KEYBINDINGS_EDITOR_COMMAND_SHOW_CONFLICTS, KEYBINDINGS_EDITOR_COMMAND_FOCUS_KEYBINDINGS + KEYBINDINGS_EDITOR_COMMAND_COPY, KEYBINDINGS_EDITOR_COMMAND_RESET, KEYBINDINGS_EDITOR_COMMAND_SHOW_CONFLICTS, KEYBINDINGS_EDITOR_COMMAND_FOCUS_KEYBINDINGS, KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS } from 'vs/workbench/parts/preferences/common/preferences'; import { PreferencesService } from 'vs/workbench/parts/preferences/browser/preferencesService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -246,4 +246,15 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ } }); +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + when: ContextKeyExpr.and(CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDINGS_SEARCH_FOCUS), + primary: KeyCode.Escape, + handler: (accessor, args: any) => { + const editor = accessor.get(IWorkbenchEditorService).getActiveEditor() as IKeybindingsEditor; + editor.clearSearchResults(); + } +}); + Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(PreferencesContentProvider); \ No newline at end of file diff --git a/src/vs/workbench/parts/preferences/browser/preferencesActions.ts b/src/vs/workbench/parts/preferences/browser/preferencesActions.ts index 0417094813d..f120d63a291 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesActions.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesActions.ts @@ -11,9 +11,10 @@ import { Action } from 'vs/base/common/actions'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IQuickOpenService, IPickOpenEntry, IFilePickOpenEntry } from 'vs/platform/quickOpen/common/quickOpen'; -import { IPreferencesService, getSettingsTargetName } from 'vs/workbench/parts/preferences/common/preferences'; -import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; -import { ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; +import { IPreferencesService } from 'vs/workbench/parts/preferences/common/preferences'; +import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { PICK_WORKSPACE_FOLDER_COMMAND } from 'vs/workbench/browser/actions/workspaceActions'; export class OpenGlobalSettingsAction extends Action { @@ -114,7 +115,7 @@ export class OpenFolderSettingsAction extends Action { label: string, @IPreferencesService private preferencesService: IPreferencesService, @IWorkspaceContextService private workspaceContextService: IWorkspaceContextService, - @IQuickOpenService private quickOpenService: IQuickOpenService + @ICommandService private commandService: ICommandService ) { super(id, label); this.update(); @@ -127,21 +128,13 @@ export class OpenFolderSettingsAction extends Action { } public run(): TPromise { - const picks: IPickOpenEntry[] = this.workspaceContextService.getWorkspace().folders.map((folder, index) => { - return { - label: getSettingsTargetName(ConfigurationTarget.FOLDER, folder.uri, this.workspaceContextService), - id: `${index}` - }; - }); - - return this.quickOpenService.pick(picks, { placeHolder: nls.localize('pickFolder', "Select Folder") }) - .then(pick => { - if (pick) { - return this.preferencesService.openFolderSettings(this.workspaceContextService.getWorkspace().folders[parseInt(pick.id)].uri); + return this.commandService.executeCommand(PICK_WORKSPACE_FOLDER_COMMAND) + .then(workspaceFolder => { + if (workspaceFolder) { + return this.preferencesService.openFolderSettings(workspaceFolder.uri); } - return undefined; + return null; }); - } public dispose(): void { diff --git a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts index c80df1ab899..99baa629377 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts @@ -23,7 +23,7 @@ import { CodeEditor } from 'vs/editor/browser/codeEditor'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IPreferencesService, ISettingsGroup, ISetting, IFilterResult, - CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_SEARCH_FOCUS, SETTINGS_EDITOR_COMMAND_SEARCH, SETTINGS_EDITOR_COMMAND_FOCUS_FILE, ISettingsEditorModel + CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_SEARCH_FOCUS, SETTINGS_EDITOR_COMMAND_SEARCH, SETTINGS_EDITOR_COMMAND_FOCUS_FILE, ISettingsEditorModel, SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, SETTINGS_EDITOR_COMMAND_FOCUS_NEXT_SETTING, SETTINGS_EDITOR_COMMAND_FOCUS_PREVIOUS_SETTING } from 'vs/workbench/parts/preferences/common/preferences'; import { SettingsEditorModel, DefaultSettingsEditorModel } from 'vs/workbench/parts/preferences/common/preferencesModels'; import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions'; @@ -140,7 +140,6 @@ export class PreferencesEditor extends BaseEditor { focusKey: this.focusSettingsContextKey })); this._register(this.searchWidget.onDidChange(value => this.filterPreferences(value.trim()))); - this._register(this.searchWidget.onNavigate(shift => this.preferencesRenderers.focusNextPreference(!shift))); this._register(this.searchWidget.onFocus(() => this.lastFocusedWidget = this.searchWidget)); this.lastFocusedWidget = this.searchWidget; @@ -156,6 +155,24 @@ export class PreferencesEditor extends BaseEditor { this._register(this.workspaceContextService.onDidChangeWorkbenchState(() => this.onWorkbenchStateChanged())); } + public clearSearchResults(): void { + if (this.searchWidget) { + this.searchWidget.clear(); + } + } + + public focusNextResult(): void { + if (this.preferencesRenderers) { + this.preferencesRenderers.focusNextPreference(true); + } + } + + public focusPreviousResult(): void { + if (this.preferencesRenderers) { + this.preferencesRenderers.focusNextPreference(false); + } + } + public setInput(newInput: PreferencesEditorInput, options?: EditorOptions): TPromise { this.defaultSettingsEditorContextKey.set(true); const oldInput = this.input; @@ -221,12 +238,12 @@ export class PreferencesEditor extends BaseEditor { } private getSettingsConfigurationTarget(resource: URI): ConfigurationTarget { - if (this.preferencesService.userSettingsResource.fsPath === resource.fsPath) { + if (this.preferencesService.userSettingsResource.toString() === resource.toString()) { return ConfigurationTarget.USER; } const workspaceSettingsResource = this.preferencesService.workspaceSettingsResource; - if (workspaceSettingsResource && workspaceSettingsResource.fsPath === resource.fsPath) { + if (workspaceSettingsResource && workspaceSettingsResource.toString() === resource.toString()) { return ConfigurationTarget.WORKSPACE; } @@ -238,10 +255,10 @@ export class PreferencesEditor extends BaseEditor { } private getSettingsConfigurationTargetUri(resource: URI): URI { - if (this.preferencesService.userSettingsResource.fsPath === resource.fsPath) { + if (this.preferencesService.userSettingsResource.toString() === resource.toString()) { return resource; } - if (this.preferencesService.workspaceSettingsResource.fsPath === resource.fsPath) { + if (this.preferencesService.workspaceSettingsResource.toString() === resource.toString()) { return resource; } @@ -791,7 +808,7 @@ abstract class AbstractSettingsEditorContribution extends Disposable { private _hasAssociatedPreferencesModelChanged(associatedPreferencesModelUri: URI): TPromise { return this.preferencesRendererCreationPromise.then(preferencesRenderer => { - return !(preferencesRenderer && preferencesRenderer.associatedPreferencesModel && preferencesRenderer.associatedPreferencesModel.uri.fsPath === associatedPreferencesModelUri.fsPath); + return !(preferencesRenderer && preferencesRenderer.associatedPreferencesModel && preferencesRenderer.associatedPreferencesModel.uri.toString() === associatedPreferencesModelUri.toString()); }); } @@ -894,17 +911,17 @@ class SettingsEditorContribution extends AbstractSettingsEditorContribution impl return false; } - if (this.preferencesService.userSettingsResource && this.preferencesService.userSettingsResource.fsPath === model.uri.fsPath) { + if (this.preferencesService.userSettingsResource && this.preferencesService.userSettingsResource.toString() === model.uri.toString()) { return true; } - if (this.preferencesService.workspaceSettingsResource && this.preferencesService.workspaceSettingsResource.fsPath === model.uri.fsPath) { + if (this.preferencesService.workspaceSettingsResource && this.preferencesService.workspaceSettingsResource.toString() === model.uri.toString()) { return true; } for (const folder of this.workspaceContextService.getWorkspace().folders) { const folderSettingsResource = this.preferencesService.getFolderSettingsResource(folder.uri); - if (folderSettingsResource && folderSettingsResource.fsPath === model.uri.fsPath) { + if (folderSettingsResource && folderSettingsResource.toString() === model.uri.toString()) { return true; } } @@ -959,3 +976,54 @@ const focusSettingsFileEditorCommand = new FocusSettingsFileEditorCommand({ kbOpts: { primary: KeyCode.DownArrow } }); KeybindingsRegistry.registerCommandAndKeybindingRule(focusSettingsFileEditorCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib())); + +class ClearSearchResultsCommand extends SettingsCommand { + + public runCommand(accessor: ServicesAccessor, args: any): void { + const preferencesEditor = this.getPreferencesEditor(accessor); + if (preferencesEditor) { + preferencesEditor.clearSearchResults(); + } + } + +} +const clearSearchResultsCommand = new ClearSearchResultsCommand({ + id: SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, + precondition: CONTEXT_SETTINGS_SEARCH_FOCUS, + kbOpts: { primary: KeyCode.Escape } +}); +KeybindingsRegistry.registerCommandAndKeybindingRule(clearSearchResultsCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib())); + +class FocusNextSearchResultCommand extends SettingsCommand { + + public runCommand(accessor: ServicesAccessor, args: any): void { + const preferencesEditor = this.getPreferencesEditor(accessor); + if (preferencesEditor) { + preferencesEditor.focusNextResult(); + } + } + +} +const focusNextSearchResultCommand = new FocusNextSearchResultCommand({ + id: SETTINGS_EDITOR_COMMAND_FOCUS_NEXT_SETTING, + precondition: CONTEXT_SETTINGS_SEARCH_FOCUS, + kbOpts: { primary: KeyCode.Enter } +}); +KeybindingsRegistry.registerCommandAndKeybindingRule(focusNextSearchResultCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib())); + +class FocusPreviousSearchResultCommand extends SettingsCommand { + + public runCommand(accessor: ServicesAccessor, args: any): void { + const preferencesEditor = this.getPreferencesEditor(accessor); + if (preferencesEditor) { + preferencesEditor.focusPreviousResult(); + } + } + +} +const focusPreviousSearchResultCommand = new FocusPreviousSearchResultCommand({ + id: SETTINGS_EDITOR_COMMAND_FOCUS_PREVIOUS_SETTING, + precondition: CONTEXT_SETTINGS_SEARCH_FOCUS, + kbOpts: { primary: KeyMod.Shift | KeyCode.Enter } +}); +KeybindingsRegistry.registerCommandAndKeybindingRule(focusPreviousSearchResultCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib())); diff --git a/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts b/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts index 142d611efc6..afe4390953e 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts @@ -796,6 +796,10 @@ class EditSettingRenderer extends Disposable { let configurationNode = configurationMap[setting.key]; if (configurationNode) { if (this.isDefaultSettings()) { + if (setting.key === 'launch') { + // Do not show because of https://github.com/Microsoft/vscode/issues/32593 + return false; + } return true; } if (configurationNode.type === 'boolean' || configurationNode.enum) { diff --git a/src/vs/workbench/parts/preferences/browser/preferencesService.ts b/src/vs/workbench/parts/preferences/browser/preferencesService.ts index 49c69f7201f..c975dfe62f8 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesService.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesService.ts @@ -122,7 +122,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic resolveContent(uri: URI): TPromise { const workspaceSettingsUri = this.getEditableSettingsURI(ConfigurationTarget.WORKSPACE); - if (workspaceSettingsUri && workspaceSettingsUri.fsPath === uri.fsPath) { + if (workspaceSettingsUri && workspaceSettingsUri.toString() === uri.toString()) { return this.resolveSettingsContentFromWorkspaceConfiguration(); } return this.createPreferencesEditorModel(uri) @@ -135,7 +135,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic return promise; } - if (this.defaultSettingsResource.fsPath === uri.fsPath) { + if (this.defaultSettingsResource.toString() === uri.toString()) { promise = TPromise.join([this.extensionService.onReady(), this.fetchMostCommonlyUsedSettings()]) .then(result => { const mostCommonSettings = result[1]; @@ -146,7 +146,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic return promise; } - if (this.defaultResourceSettingsResource.fsPath === uri.fsPath) { + if (this.defaultResourceSettingsResource.toString() === uri.toString()) { promise = TPromise.join([this.extensionService.onReady(), this.fetchMostCommonlyUsedSettings()]) .then(result => { const mostCommonSettings = result[1]; @@ -157,25 +157,25 @@ export class PreferencesService extends Disposable implements IPreferencesServic return promise; } - if (this.defaultKeybindingsResource.fsPath === uri.fsPath) { + if (this.defaultKeybindingsResource.toString() === uri.toString()) { const model = this.instantiationService.createInstance(DefaultKeybindingsEditorModel, uri); promise = TPromise.wrap(model); this.defaultPreferencesEditorModels.set(uri, promise); return promise; } - if (this.workspaceConfigSettingsResource.fsPath === uri.fsPath) { + if (this.workspaceConfigSettingsResource.toString() === uri.toString()) { promise = this.createEditableSettingsEditorModel(ConfigurationTarget.WORKSPACE, uri); this.defaultPreferencesEditorModels.set(uri, promise); return promise; } - if (this.getEditableSettingsURI(ConfigurationTarget.USER).fsPath === uri.fsPath) { + if (this.getEditableSettingsURI(ConfigurationTarget.USER).toString() === uri.toString()) { return this.createEditableSettingsEditorModel(ConfigurationTarget.USER, uri); } const workspaceSettingsUri = this.getEditableSettingsURI(ConfigurationTarget.WORKSPACE); - if (workspaceSettingsUri && workspaceSettingsUri.fsPath === uri.fsPath) { + if (workspaceSettingsUri && workspaceSettingsUri.toString() === uri.toString()) { return this.createEditableSettingsEditorModel(ConfigurationTarget.WORKSPACE, workspaceSettingsUri); } @@ -288,7 +288,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic private createEditableSettingsEditorModel(configurationTarget: ConfigurationTarget, resource: URI): TPromise { const settingsUri = this.getEditableSettingsURI(configurationTarget, resource); if (settingsUri) { - if (settingsUri.fsPath === this.workspaceConfigSettingsResource.fsPath) { + if (settingsUri.toString() === this.workspaceConfigSettingsResource.toString()) { return TPromise.join([this.textModelResolverService.createModelReference(settingsUri), this.textModelResolverService.createModelReference(this.contextService.getWorkspace().configuration)]) .then(([reference, workspaceConfigReference]) => this.instantiationService.createInstance(WorkspaceConfigModel, reference, workspaceConfigReference, configurationTarget, this._onDispose.event)); } @@ -320,10 +320,10 @@ export class PreferencesService extends Disposable implements IPreferencesServic return null; } const workspace = this.contextService.getWorkspace(); - return workspace.configuration || this.contextService.toResource(paths.join('.vscode', 'settings.json'), workspace.folders[0]); + return workspace.configuration || workspace.folders[0].toResource(paths.join('.vscode', 'settings.json')); case ConfigurationTarget.FOLDER: const folder = this.contextService.getWorkspaceFolder(resource); - return folder ? this.contextService.toResource(paths.join('.vscode', 'settings.json'), folder) : null; + return folder ? folder.toResource(paths.join('.vscode', 'settings.json')) : null; } return null; } diff --git a/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts b/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts index 2e69c1dfc87..068499282ba 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts @@ -332,7 +332,7 @@ export class SettingsTargetsWidget extends Widget { actions.push({ id: 'userSettingsTarget', label: getSettingsTargetName(ConfigurationTarget.USER, userSettingsResource, this.workspaceContextService), - checked: this.uri.fsPath === userSettingsResource.fsPath, + checked: this.uri.toString() === userSettingsResource.toString(), enabled: true, run: () => this.onTargetClicked(userSettingsResource) }); @@ -342,7 +342,7 @@ export class SettingsTargetsWidget extends Widget { actions.push({ id: 'workspaceSettingsTarget', label: getSettingsTargetName(ConfigurationTarget.WORKSPACE, workspaceSettingsResource, this.workspaceContextService), - checked: this.uri.fsPath === workspaceSettingsResource.fsPath, + checked: this.uri.toString() === workspaceSettingsResource.toString(), enabled: true, run: () => this.onTargetClicked(workspaceSettingsResource) }); @@ -355,7 +355,7 @@ export class SettingsTargetsWidget extends Widget { return { id: 'folderSettingsTarget' + index, label: getSettingsTargetName(ConfigurationTarget.FOLDER, folder.uri, this.workspaceContextService), - checked: this.uri.fsPath === folder.uri.fsPath, + checked: this.uri.toString() === folder.uri.toString(), enabled: true, run: () => this.onTargetClicked(folder.uri) }; @@ -366,7 +366,7 @@ export class SettingsTargetsWidget extends Widget { } private onTargetClicked(target: URI): void { - if (this.uri.fsPath === target.fsPath) { + if (this.uri.toString() === target.toString()) { return; } this._onDidTargetChange.fire(target); @@ -399,9 +399,6 @@ export class SearchWidget extends Widget { private _onDidChange: Emitter = this._register(new Emitter()); public readonly onDidChange: Event = this._onDidChange.event; - private _onNavigate: Emitter = this._register(new Emitter()); - public readonly onNavigate: Event = this._onNavigate.event; - private _onFocus: Emitter = this._register(new Emitter()); public readonly onFocus: Event = this._onFocus.event; @@ -447,7 +444,6 @@ export class SearchWidget extends Widget { const searchInput = DOM.append(this.searchContainer, DOM.$('div.settings-search-input')); this.inputBox = this._register(this.createInputBox(searchInput)); this._register(this.inputBox.onDidChange(value => this._onDidChange.fire(value))); - this.onkeydown(this.inputBox.inputElement, (e) => this._onKeyDown(e)); } protected createInputBox(parent: HTMLElement): InputBox { @@ -504,24 +500,6 @@ export class SearchWidget extends Widget { return this.inputBox.value = value; } - private _onKeyDown(keyboardEvent: IKeyboardEvent): void { - let handled = false; - switch (keyboardEvent.keyCode) { - case KeyCode.Enter: - this._onNavigate.fire(keyboardEvent.shiftKey); - handled = true; - break; - case KeyCode.Escape: - this.clear(); - handled = true; - break; - } - if (handled) { - keyboardEvent.preventDefault(); - keyboardEvent.stopPropagation(); - } - } - public dispose(): void { if (this.options.focusKey) { this.options.focusKey.set(false); diff --git a/src/vs/workbench/parts/preferences/common/preferences.ts b/src/vs/workbench/parts/preferences/common/preferences.ts index 71e9d348b59..1b7200927c9 100644 --- a/src/vs/workbench/parts/preferences/common/preferences.ts +++ b/src/vs/workbench/parts/preferences/common/preferences.ts @@ -91,6 +91,7 @@ export interface IKeybindingsEditor extends IEditor { activeKeybindingEntry: IKeybindingItemEntry; search(filter: string): void; + clearSearchResults(): void; focusKeybindings(): void; defineKeybinding(keybindingEntry: IKeybindingItemEntry): TPromise; removeKeybinding(keybindingEntry: IKeybindingItemEntry): TPromise; @@ -118,8 +119,12 @@ export const CONTEXT_KEYBINDINGS_SEARCH_FOCUS = new RawContextKey('inKe export const CONTEXT_KEYBINDING_FOCUS = new RawContextKey('keybindingFocus', false); export const SETTINGS_EDITOR_COMMAND_SEARCH = 'settings.action.search'; +export const SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS = 'settings.action.clearSearchResults'; +export const SETTINGS_EDITOR_COMMAND_FOCUS_NEXT_SETTING = 'settings.action.focusNextSetting'; +export const SETTINGS_EDITOR_COMMAND_FOCUS_PREVIOUS_SETTING = 'settings.action.focusPreviousSetting'; export const SETTINGS_EDITOR_COMMAND_FOCUS_FILE = 'settings.action.focusSettingsFile'; export const KEYBINDINGS_EDITOR_COMMAND_SEARCH = 'keybindings.editor.searchKeybindings'; +export const KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS = 'keybindings.editor.clearSearchResults'; export const KEYBINDINGS_EDITOR_COMMAND_DEFINE = 'keybindings.editor.defineKeybinding'; export const KEYBINDINGS_EDITOR_COMMAND_REMOVE = 'keybindings.editor.removeKeybinding'; export const KEYBINDINGS_EDITOR_COMMAND_RESET = 'keybindings.editor.resetKeybinding'; diff --git a/src/vs/workbench/parts/preferences/common/preferencesContentProvider.ts b/src/vs/workbench/parts/preferences/common/preferencesContentProvider.ts index 4b58705c22b..33141c96052 100644 --- a/src/vs/workbench/parts/preferences/common/preferencesContentProvider.ts +++ b/src/vs/workbench/parts/preferences/common/preferencesContentProvider.ts @@ -14,6 +14,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { IPreferencesService } from 'vs/workbench/parts/preferences/common/preferences'; +import { dispose } from 'vs/base/common/lifecycle'; const schemaRegistry = Registry.as(JSONContributionRegistry.Extensions.JSONContribution); @@ -33,18 +34,16 @@ export class PreferencesContentProvider implements IWorkbenchContribution { } private start(): void { + this.textModelResolverService.registerTextModelContentProvider('vscode', { provideTextContent: (uri: URI): TPromise => { if (uri.scheme !== 'vscode') { return null; } if (uri.authority === 'schemas') { - let schemas = schemaRegistry.getSchemaContributions().schemas; - let schema = schemas[uri.toString()]; - if (schema) { - let modelContent = JSON.stringify(schema); - let mode = this.modeService.getOrCreateMode('json'); - return TPromise.as(this.modelService.createModel(modelContent, mode, uri)); + const schemaModel = this.getSchemaModel(uri); + if (schemaModel) { + return TPromise.as(schemaModel); } } return this.preferencesService.resolveContent(uri) @@ -59,4 +58,25 @@ export class PreferencesContentProvider implements IWorkbenchContribution { } }); } + + private getSchemaModel(uri: URI): IModel { + let schema = schemaRegistry.getSchemaContributions().schemas[uri.toString()]; + if (schema) { + const modelContent = JSON.stringify(schema); + const mode = this.modeService.getOrCreateMode('json'); + const model = this.modelService.createModel(modelContent, mode, uri); + + let disposables = []; + disposables.push(schemaRegistry.onDidChangeSchema(schemaUri => { + if (schemaUri === uri.toString()) { + schema = schemaRegistry.getSchemaContributions().schemas[uri.toString()]; + model.setValue(JSON.stringify(schema)); + } + })); + disposables.push(model.onWillDispose(() => dispose(disposables))); + + return model; + } + return null; + } } diff --git a/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.ts b/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.ts index e512ed141a0..e29d4a29225 100644 --- a/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.ts +++ b/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.ts @@ -9,11 +9,12 @@ import nls = require('vs/nls'); import 'vs/css!./media/dirtydiffDecorator'; import { ThrottledDelayer, always } from 'vs/base/common/async'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; import { TPromise } from 'vs/base/common/winjs.base'; +import { any as anyEvent, filterEvent } from 'vs/base/common/event'; import * as ext from 'vs/workbench/common/contributions'; import * as common from 'vs/editor/common/editorCommon'; -import * as widget from 'vs/editor/browser/codeEditor'; +import { CodeEditor } from 'vs/editor/browser/codeEditor'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IMessageService } from 'vs/platform/message/common/message'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; @@ -23,7 +24,7 @@ import { IModelService } from 'vs/editor/common/services/modelService'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; import URI from 'vs/base/common/uri'; import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; -import { ISCMService } from 'vs/workbench/services/scm/common/scm'; +import { ISCMService, ISCMRepository } from 'vs/workbench/services/scm/common/scm'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations'; import { registerThemingParticipant, ITheme, ICssStyleCollector, themeColorFromId } from 'vs/platform/theme/common/themeService'; import { registerColor } from 'vs/platform/theme/common/colorRegistry'; @@ -87,15 +88,17 @@ class DirtyDiffModelDecorator { } }); - private decorations: string[]; + private decorations: string[] = []; private baselineModel: common.IModel; private diffDelayer: ThrottledDelayer; private _originalURIPromise: TPromise; - private toDispose: IDisposable[]; + + private repositoryDisposables = new Set(); + private toDispose: IDisposable[] = []; constructor( private model: common.IModel, - private uri: URI, + // private editor: CodeEditor, @ISCMService private scmService: ISCMService, @IModelService private modelService: IModelService, @IEditorWorkerService private editorWorkerService: IEditorWorkerService, @@ -103,12 +106,28 @@ class DirtyDiffModelDecorator { @IWorkspaceContextService private contextService: IWorkspaceContextService, @ITextModelService private textModelResolverService: ITextModelService ) { - this.decorations = []; this.diffDelayer = new ThrottledDelayer(200); - this.toDispose = []; - this.triggerDiff(); + this.toDispose.push(model.onDidChangeContent(() => this.triggerDiff())); - this.toDispose.push(scmService.onDidChangeRepository(() => this.triggerDiff())); + scmService.onDidAddRepository(this.onDidAddRepository, this, this.toDispose); + scmService.repositories.forEach(r => this.onDidAddRepository(r)); + + this.triggerDiff(); + } + + private onDidAddRepository(repository: ISCMRepository): void { + const disposables: IDisposable[] = []; + + this.repositoryDisposables.add(disposables); + disposables.push(toDisposable(() => this.repositoryDisposables.delete(disposables))); + + const onDidChange = anyEvent(repository.provider.onDidChange, repository.provider.onDidChangeResources); + onDidChange(this.triggerDiff, this, disposables); + + const onDidRemoveThis = filterEvent(this.scmService.onDidRemoveRepository, r => r === repository); + onDidRemoveThis(() => dispose(disposables)); + + this.triggerDiff(); } private triggerDiff(): TPromise { @@ -174,7 +193,7 @@ class DirtyDiffModelDecorator { private async getOriginalResource(): TPromise { for (const repository of this.scmService.repositories) { - const result = repository.provider.getOriginalResource(this.uri); + const result = repository.provider.getOriginalResource(this.model.uri); if (result) { return result; @@ -237,6 +256,9 @@ class DirtyDiffModelDecorator { this.diffDelayer.cancel(); this.diffDelayer = null; } + + this.repositoryDisposables.forEach(d => dispose(d)); + this.repositoryDisposables.clear(); } } @@ -271,28 +293,25 @@ export class DirtyDiffDecorator implements ext.IWorkbenchContribution { .map(e => e.getControl()) // only interested in code editor widgets - .filter(c => c instanceof widget.CodeEditor) + .filter(c => c instanceof CodeEditor) // map to models - .map(e => (e).getModel()) + .map(editor => (editor as CodeEditor).getModel()) // remove nulls and duplicates - .filter((m, i, a) => !!m && !!m.uri && a.indexOf(m, i + 1) === -1) + .filter((m, i, a) => !!m && !!m.uri && a.indexOf(m, i + 1) === -1); - // get the associated resource - .map(m => ({ model: m, uri: m.uri })); + const newModels = models.filter(p => this.models.every(m => p !== m)); + const oldModels = this.models.filter(m => models.every(p => p !== m)); - const newModels = models.filter(p => this.models.every(m => p.model !== m)); - const oldModels = this.models.filter(m => models.every(p => p.model !== m)); - - newModels.forEach(({ model, uri }) => this.onModelVisible(model, uri)); + newModels.forEach(m => this.onModelVisible(m)); oldModels.forEach(m => this.onModelInvisible(m)); - this.models = models.map(p => p.model); + this.models = models; } - private onModelVisible(model: common.IModel, uri: URI): void { - this.decorators[model.id] = this.instantiationService.createInstance(DirtyDiffModelDecorator, model, uri); + private onModelVisible(model: common.IModel): void { + this.decorators[model.id] = this.instantiationService.createInstance(DirtyDiffModelDecorator, model); } private onModelInvisible(model: common.IModel): void { diff --git a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts index c1f91d67af1..dea5d1800d4 100644 --- a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts +++ b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts @@ -595,6 +595,11 @@ export class RepositoryPanel extends ViewletPanel { private pin(): void { const activeEditor = this.editorService.getActiveEditor(); const activeEditorInput = this.editorService.getActiveEditorInput(); + + if (!activeEditor) { + return; + } + this.editorGroupService.pinEditor(activeEditor.position, activeEditorInput); } diff --git a/src/vs/workbench/parts/search/browser/search.contribution.ts b/src/vs/workbench/parts/search/browser/search.contribution.ts index 76b10d2b040..ba0b901980a 100644 --- a/src/vs/workbench/parts/search/browser/search.contribution.ts +++ b/src/vs/workbench/parts/search/browser/search.contribution.ts @@ -190,7 +190,7 @@ class ExplorerViewerActionContributor extends ActionBarContributor { return false; } - return fileResource.isDirectory; + return fileResource.isDirectory && fileResource.resource.scheme === 'file'; } public getSecondaryActions(context: any): IAction[] { diff --git a/src/vs/workbench/parts/search/browser/searchActions.ts b/src/vs/workbench/parts/search/browser/searchActions.ts index 3b328409d7d..3c684f0cda2 100644 --- a/src/vs/workbench/parts/search/browser/searchActions.ts +++ b/src/vs/workbench/parts/search/browser/searchActions.ts @@ -7,6 +7,7 @@ import nls = require('vs/nls'); import DOM = require('vs/base/browser/dom'); import errors = require('vs/base/common/errors'); import paths = require('vs/base/common/paths'); +import resources = require('vs/base/common/resources'); import { TPromise } from 'vs/base/common/winjs.base'; import URI from 'vs/base/common/uri'; import { Action } from 'vs/base/common/actions'; @@ -376,7 +377,7 @@ export const findInFolderCommand = (accessor: ServicesAccessor, resource?: URI) if (focused) { const file = explorerItemToFileResource(focused); if (file) { - resource = file.isDirectory ? file.resource : URI.file(paths.dirname(file.resource.fsPath)); + resource = file.isDirectory ? file.resource : resources.dirname(file.resource); } } } diff --git a/src/vs/workbench/parts/search/browser/searchResultsView.ts b/src/vs/workbench/parts/search/browser/searchResultsView.ts index ec069ee2197..522ae4e54ef 100644 --- a/src/vs/workbench/parts/search/browser/searchResultsView.ts +++ b/src/vs/workbench/parts/search/browser/searchResultsView.ts @@ -324,12 +324,12 @@ export class SearchAccessibilityProvider implements IAccessibilityProvider { const match = element; const searchModel: SearchModel = (tree.getInput()).searchModel; const replace = searchModel.isReplaceActive() && !!searchModel.replaceString; - const preview = match.preview(); + const matchString = match.getMatchString(); const range = match.range(); if (replace) { - return nls.localize('replacePreviewResultAria', "Replace term {0} with {1} at column position {2} in line with text {3}", preview.inside, match.replaceString, range.startColumn + 1, match.text()); + return nls.localize('replacePreviewResultAria', "Replace term {0} with {1} at column position {2} in line with text {3}", matchString, match.replaceString, range.startColumn + 1, match.text()); } - return nls.localize('searchResultAria', "Found term {0} at column position {1} in line with text {2}", preview.inside, range.startColumn + 1, match.text()); + return nls.localize('searchResultAria', "Found term {0} at column position {1} in line with text {2}", matchString, range.startColumn + 1, match.text()); } return undefined; } diff --git a/src/vs/workbench/parts/search/common/queryBuilder.ts b/src/vs/workbench/parts/search/common/queryBuilder.ts index c91ced37871..014ad87db11 100644 --- a/src/vs/workbench/parts/search/common/queryBuilder.ts +++ b/src/vs/workbench/parts/search/common/queryBuilder.ts @@ -195,7 +195,7 @@ export class QueryBuilder { const pathPortions = this.expandAbsoluteSearchPaths(pathPortion); return pathPortions.map(searchPath => { return { - searchPath: uri.file(searchPath), + searchPath, pattern: globPortion }; }); @@ -207,14 +207,15 @@ export class QueryBuilder { /** * Takes a searchPath like `./a/foo` and expands it to absolute paths for all the workspaces it matches. */ - private expandAbsoluteSearchPaths(searchPath: string): string[] { + private expandAbsoluteSearchPaths(searchPath: string): uri[] { if (paths.isAbsolute(searchPath)) { - return [paths.normalize(searchPath)]; + // Currently only local resources can be searched for with absolute search paths + return [uri.file(paths.normalize(searchPath))]; } if (this.workspaceContextService.getWorkbenchState() === WorkbenchState.FOLDER) { // TODO: @Sandy Try checking workspace folders length instead. - return [paths.normalize( - paths.join(this.workspaceContextService.getWorkspace().folders[0].uri.fsPath, searchPath))]; + const workspaceUri = this.workspaceContextService.getWorkspace().folders[0].uri; + return [workspaceUri.with({ path: paths.normalize(paths.join(workspaceUri.path, searchPath)) })]; } else if (searchPath === './') { return []; // ./ or ./**/foo makes sense for single-folder but not multi-folder workspaces } else { @@ -225,8 +226,8 @@ export class QueryBuilder { if (matchingRoots.length) { return matchingRoots.map(root => { return relativeSearchPathMatch[2] ? - paths.normalize(paths.join(root.uri.fsPath, relativeSearchPathMatch[2])) : - root.uri.fsPath; + root.uri.with({ path: paths.normalize(paths.join(root.uri.path, relativeSearchPathMatch[2])) }) : + root.uri; }); } else { // No root folder with name diff --git a/src/vs/workbench/parts/search/common/searchModel.ts b/src/vs/workbench/parts/search/common/searchModel.ts index 3c3da515ec3..de3d6e7eee1 100644 --- a/src/vs/workbench/parts/search/common/searchModel.ts +++ b/src/vs/workbench/parts/search/common/searchModel.ts @@ -11,7 +11,7 @@ import { RunOnceScheduler } from 'vs/base/common/async'; import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; import { TPromise, PPromise } from 'vs/base/common/winjs.base'; import URI from 'vs/base/common/uri'; -import { values, ResourceMap, TrieMap } from 'vs/base/common/map'; +import { values, ResourceMap, StringTrieMap } from 'vs/base/common/map'; import Event, { Emitter, fromPromise, stopwatch, any } from 'vs/base/common/event'; import { ISearchService, ISearchProgressItem, ISearchComplete, ISearchQuery, IPatternInfo, IFileMatch } from 'vs/platform/search/common/search'; import { ReplacePattern } from 'vs/platform/search/common/replace'; @@ -496,7 +496,7 @@ export class SearchResult extends Disposable { public onChange: Event = this._onChange.event; private _folderMatches: FolderMatch[] = []; - private _folderMatchesMap: TrieMap = new TrieMap(); + private _folderMatchesMap: StringTrieMap = new StringTrieMap(); private _query: ISearchQuery = null; private _showHighlights: boolean; @@ -654,7 +654,7 @@ export class SearchResult extends Disposable { private disposeMatches(): void { this._folderMatches.forEach(folderMatch => folderMatch.dispose()); this._folderMatches = []; - this._folderMatchesMap = new TrieMap(); + this._folderMatchesMap = new StringTrieMap(); this._rangeHighlightDecorations.removeHighlightRange(); } diff --git a/src/vs/workbench/parts/tasks/browser/quickOpen.ts b/src/vs/workbench/parts/tasks/browser/quickOpen.ts index 4d44c449dc6..a842937eeca 100644 --- a/src/vs/workbench/parts/tasks/browser/quickOpen.ts +++ b/src/vs/workbench/parts/tasks/browser/quickOpen.ts @@ -30,7 +30,7 @@ export class TaskEntry extends Model.QuickOpenEntry { } public getDescription(): string { - if (!this.taskService.hasMultipleFolders()) { + if (!this.taskService.needsFolderQualification()) { return null; } let workspaceFolder = Task.getWorkspaceFolder(this.task); @@ -98,7 +98,12 @@ export abstract class QuickOpenHandler extends Quickopen.QuickOpenHandler { let configured: CustomTask[] = []; let detected: ContributedTask[] = []; let taskMap: IStringDictionary = Object.create(null); - tasks.forEach(task => taskMap[Task.getKey(task)] = task); + tasks.forEach(task => { + let key = Task.getRecentlyUsedKey(task); + if (key) { + taskMap[key] = task; + } + }); recentlyUsedTasks.keys().forEach(key => { let task = taskMap[key]; if (task) { @@ -106,7 +111,8 @@ export abstract class QuickOpenHandler extends Quickopen.QuickOpenHandler { } }); for (let task of tasks) { - if (!recentlyUsedTasks.has(Task.getKey(task))) { + let key = Task.getRecentlyUsedKey(task); + if (!key || !recentlyUsedTasks.has(key)) { if (CustomTask.is(task)) { configured.push(task); } else { diff --git a/src/vs/workbench/parts/tasks/common/problemCollectors.ts b/src/vs/workbench/parts/tasks/common/problemCollectors.ts index e886d8ef1a9..c40e58579b1 100644 --- a/src/vs/workbench/parts/tasks/common/problemCollectors.ts +++ b/src/vs/workbench/parts/tasks/common/problemCollectors.ts @@ -284,6 +284,11 @@ export class AbstractProblemCollector extends EventEmitter implements IDisposabl } return result; } + + protected cleanMarkerCaches(): void { + this.markers.clear(); + this.deliveredMarkers.clear(); + } } export enum ProblemHandlingStrategy { @@ -410,6 +415,7 @@ export class WatchingProblemCollector extends AbstractProblemCollector implement if (matches) { result = true; this.emit(ProblemCollectorEvents.WatchingBeginDetected, {}); + this.cleanMarkerCaches(); this.resetCurrentResource(); let owner = beginMatcher.problemMatcher.owner; let file = matches[beginMatcher.pattern.file]; @@ -435,6 +441,7 @@ export class WatchingProblemCollector extends AbstractProblemCollector implement let owner = endMatcher.problemMatcher.owner; this.resetCurrentResource(); this.cleanMarkers(owner); + this.cleanMarkerCaches(); } } return result; diff --git a/src/vs/workbench/parts/tasks/common/taskService.ts b/src/vs/workbench/parts/tasks/common/taskService.ts index 3710927f7c4..fa001c1537d 100644 --- a/src/vs/workbench/parts/tasks/common/taskService.ts +++ b/src/vs/workbench/parts/tasks/common/taskService.ts @@ -10,7 +10,7 @@ import { IEventEmitter } from 'vs/base/common/eventEmitter'; import { LinkedMap } from 'vs/base/common/map'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { Task, ContributedTask, CustomTask, TaskSet, TaskSorter } from 'vs/workbench/parts/tasks/common/tasks'; import { ITaskSummary, TaskEvent, TaskType, TaskTerminateResponse } from 'vs/workbench/parts/tasks/common/taskSystem'; @@ -57,12 +57,12 @@ export interface ITaskService extends IEventEmitter { /** * @param identifier The task's name, label or defined identifier. */ - getTask(workspaceFolder: WorkspaceFolder | string, identifier: string): TPromise; + getTask(workspaceFolder: IWorkspaceFolder | string, identifier: string): TPromise; getTasksForGroup(group: string): TPromise; getRecentlyUsedTasks(): LinkedMap; createSorter(): TaskSorter; - hasMultipleFolders(); + needsFolderQualification(); canCustomize(task: ContributedTask | CustomTask): boolean; customize(task: ContributedTask | CustomTask, properties?: {}, openConfig?: boolean): TPromise; openConfig(task: CustomTask): TPromise; diff --git a/src/vs/workbench/parts/tasks/common/taskSystem.ts b/src/vs/workbench/parts/tasks/common/taskSystem.ts index 11b78e506ef..50c34926ecf 100644 --- a/src/vs/workbench/parts/tasks/common/taskSystem.ts +++ b/src/vs/workbench/parts/tasks/common/taskSystem.ts @@ -9,7 +9,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { TerminateResponse } from 'vs/base/common/processes'; import { IEventEmitter } from 'vs/base/common/eventEmitter'; -import { WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { Task } from './tasks'; @@ -104,7 +104,7 @@ export interface TaskEvent { } export interface ITaskResolver { - resolve(workspaceFolder: WorkspaceFolder, identifier: string): Task; + resolve(workspaceFolder: IWorkspaceFolder, identifier: string): Task; } export interface TaskTerminateResponse extends TerminateResponse { diff --git a/src/vs/workbench/parts/tasks/common/tasks.ts b/src/vs/workbench/parts/tasks/common/tasks.ts index 898c6b9b5d3..6f4def9b10b 100644 --- a/src/vs/workbench/parts/tasks/common/tasks.ts +++ b/src/vs/workbench/parts/tasks/common/tasks.ts @@ -11,7 +11,7 @@ import * as Objects from 'vs/base/common/objects'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { ProblemMatcher } from 'vs/platform/markers/common/problemMatcher'; -import { WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; export interface ShellConfiguration { /** @@ -227,11 +227,11 @@ export enum TaskScope { export namespace TaskSourceKind { export const Workspace: 'workspace' = 'workspace'; export const Extension: 'extension' = 'extension'; - export const Composite: 'composite' = 'composite'; + export const InMemory: 'inMemory' = 'inMemory'; } export interface TaskSourceConfigElement { - workspaceFolder: WorkspaceFolder; + workspaceFolder: IWorkspaceFolder; file: string; index: number; element: any; @@ -249,19 +249,19 @@ export interface ExtensionTaskSource { readonly label: string; readonly extension: string; readonly scope: TaskScope; - readonly workspaceFolder: WorkspaceFolder | undefined; + readonly workspaceFolder: IWorkspaceFolder | undefined; } export interface ExtensionTaskSourceTransfer { __workspaceFolder: URI; } -export interface CompositeTaskSource { - readonly kind: 'composite'; +export interface InMemoryTaskSource { + readonly kind: 'inMemory'; readonly label: string; } -export type TaskSource = WorkspaceTaskSource | ExtensionTaskSource | CompositeTaskSource; +export type TaskSource = WorkspaceTaskSource | ExtensionTaskSource | InMemoryTaskSource; export interface TaskIdentifier { _key: string; @@ -269,7 +269,7 @@ export interface TaskIdentifier { } export interface TaskDependency { - workspaceFolder: WorkspaceFolder; + workspaceFolder: IWorkspaceFolder; task: string; } @@ -403,36 +403,61 @@ export namespace ContributedTask { } } -export interface CompositeTask extends CommonTask, ConfigurationProperties { +export interface InMemoryTask extends CommonTask, ConfigurationProperties { /** * Indicated the source of the task (e.g tasks.json or extension) */ - _source: CompositeTaskSource; + _source: InMemoryTaskSource; - type: 'composite'; + type: 'inMemory'; identifier: string; } -export namespace CompositeTask { - export function is(value: any): value is CompositeTask { - let candidate = value as CompositeTask; - return candidate && candidate._source && candidate._source.kind === TaskSourceKind.Composite; +export namespace InMemoryTask { + export function is(value: any): value is InMemoryTask { + let candidate = value as InMemoryTask; + return candidate && candidate._source && candidate._source.kind === TaskSourceKind.InMemory; } } -export type Task = CustomTask | ContributedTask | CompositeTask; +export type Task = CustomTask | ContributedTask | InMemoryTask; export namespace Task { - export function getKey(task: Task): string { - if (CustomTask.is(task) || CompositeTask.is(task)) { - return task.identifier; - } else { - return task.defines._key; + export function getRecentlyUsedKey(task: Task): string | undefined { + interface CustomKey { + type: string; + folder: string; + id: string; } + interface ContributedKey { + type: string; + scope: number; + folder?: string; + id: string; + } + if (InMemoryTask.is(task)) { + return undefined; + } + if (CustomTask.is(task)) { + let workspaceFolder = task._source.config.workspaceFolder; + if (!workspaceFolder) { + return undefined; + } + let key: CustomKey = { type: 'custom', folder: workspaceFolder.uri.toString(), id: task.identifier }; + return JSON.stringify(key); + } + if (ContributedTask.is(task)) { + let key: ContributedKey = { type: 'contributed', scope: task._source.scope, id: task.defines._key }; + if (task._source.scope === TaskScope.Folder && task._source.workspaceFolder) { + key.folder = task._source.workspaceFolder.uri.toString(); + } + return JSON.stringify(key); + } + return undefined; } - export function getWorkspaceFolder(task: Task): WorkspaceFolder | undefined { + export function getWorkspaceFolder(task: Task): IWorkspaceFolder | undefined { if (CustomTask.is(task)) { return task._source.config.workspaceFolder; } else if (ContributedTask.is(task)) { @@ -455,7 +480,7 @@ export namespace Task { } else { return 'workspace'; } - } else if (CompositeTask.is(task)) { + } else if (InMemoryTask.is(task)) { return 'composite'; } else { return 'unknown'; @@ -506,7 +531,7 @@ export class TaskSorter { private _order: Map = new Map(); - constructor(workspaceFolders: WorkspaceFolder[]) { + constructor(workspaceFolders: IWorkspaceFolder[]) { for (let i = 0; i < workspaceFolders.length; i++) { this._order.set(workspaceFolders[i].uri.toString(), i); } diff --git a/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts b/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts index b86ab02f64c..47f0d1de4be 100644 --- a/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts +++ b/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts @@ -30,14 +30,13 @@ import { OcticonLabel } from 'vs/base/browser/ui/octiconLabel/octiconLabel'; import { Registry } from 'vs/platform/registry/common/platform'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; -import { SyncActionDescriptor, MenuRegistry } from 'vs/platform/actions/common/actions'; +import { MenuRegistry } from 'vs/platform/actions/common/actions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { IEditor } from 'vs/platform/editor/common/editor'; import { IMessageService } from 'vs/platform/message/common/message'; import { IMarkerService, MarkerStatistics } from 'vs/platform/markers/common/markers'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IFileService } from 'vs/platform/files/common/files'; +import { IFileService, IFileStat } from 'vs/platform/files/common/files'; import { IExtensionService } from 'vs/platform/extensions/common/extensions'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; @@ -55,18 +54,17 @@ import { IModelService } from 'vs/editor/common/services/modelService'; import jsonContributionRegistry = require('vs/platform/jsonschemas/common/jsonContributionRegistry'); import { IJSONSchema } from 'vs/base/common/jsonSchema'; -import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions } from 'vs/workbench/common/actions'; import { IStatusbarItem, IStatusbarRegistry, Extensions as StatusbarExtensions, StatusbarItemDescriptor, StatusbarAlignment } from 'vs/workbench/browser/parts/statusbar/statusbar'; import { IQuickOpenRegistry, Extensions as QuickOpenExtensions, QuickOpenHandlerDescriptor } from 'vs/workbench/browser/quickopen'; -import { IQuickOpenService, IPickOpenEntry } from 'vs/platform/quickOpen/common/quickOpen'; +import { IQuickOpenService, IPickOpenEntry, IPickOpenAction, IPickOpenItem } from 'vs/platform/quickOpen/common/quickOpen'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import Constants from 'vs/workbench/parts/markers/common/constants'; import { IPartService } from 'vs/workbench/services/part/common/partService'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; import { IConfigurationEditingService, ConfigurationTarget, IConfigurationValue } from 'vs/workbench/services/configuration/common/configurationEditing'; -import { IWorkspaceContextService, WorkbenchState, WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IOutputService, IOutputChannelRegistry, Extensions as OutputExt, IOutputChannel } from 'vs/workbench/parts/output/common/output'; @@ -75,7 +73,7 @@ import { Scope, IActionBarRegistry, Extensions as ActionBarExtensions } from 'vs import { ITerminalService } from 'vs/workbench/parts/terminal/common/terminal'; import { ITaskSystem, ITaskResolver, ITaskSummary, ITaskExecuteResult, TaskExecuteKind, TaskError, TaskErrors, TaskSystemEvents, TaskTerminateResponse } from 'vs/workbench/parts/tasks/common/taskSystem'; -import { Task, CustomTask, ConfiguringTask, ContributedTask, CompositeTask, TaskSet, TaskGroup, ExecutionEngine, JsonSchemaVersion, TaskSourceKind, TaskIdentifier, TaskSorter } from 'vs/workbench/parts/tasks/common/tasks'; +import { Task, CustomTask, ConfiguringTask, ContributedTask, InMemoryTask, TaskSet, TaskGroup, ExecutionEngine, JsonSchemaVersion, TaskSourceKind, TaskIdentifier, TaskSorter } from 'vs/workbench/parts/tasks/common/tasks'; import { ITaskService, TaskServiceEvents, ITaskProvider, TaskEvent, RunOptions, CustomizationProperties } from 'vs/workbench/parts/tasks/common/taskService'; import { templates as taskTemplates } from 'vs/workbench/parts/tasks/common/taskTemplates'; @@ -95,152 +93,14 @@ import { ReloadWindowAction } from 'vs/workbench/electron-browser/actions'; let $ = Builder.$; let tasksCategory = nls.localize('tasksCategory', "Tasks"); -abstract class OpenTaskConfigurationAction extends Action { - - constructor(id: string, label: string, - private taskService: ITaskService, - private configurationService: IConfigurationService, - private editorService: IWorkbenchEditorService, private fileService: IFileService, - private contextService: IWorkspaceContextService, private outputService: IOutputService, - private messageService: IMessageService, private quickOpenService: IQuickOpenService, - private environmentService: IEnvironmentService, - private configurationResolverService: IConfigurationResolverService, - private extensionService: IExtensionService, - private telemetryService: ITelemetryService) { - - super(id, label); - } - - public run(event?: any): TPromise { - if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) { - this.messageService.show(Severity.Info, nls.localize('ConfigureTaskRunnerAction.noWorkspace', 'Tasks are only available on a workspace folder.')); - return TPromise.as(undefined); - } - let sideBySide = !!(event && (event.ctrlKey || event.metaKey)); - let configFileCreated = false; - return this.fileService.resolveFile(this.contextService.toResource('.vscode/tasks.json', this.contextService.getWorkspace().folders[0])).then((success) => { // TODO@Dirk (https://github.com/Microsoft/vscode/issues/29454) - - return success; - }, (err: any) => { - return this.quickOpenService.pick(taskTemplates, { placeHolder: nls.localize('ConfigureTaskRunnerAction.quickPick.template', 'Select a Task Runner') }).then(selection => { - if (!selection) { - return undefined; - } - let contentPromise: TPromise; - if (selection.autoDetect) { - const outputChannel = this.outputService.getChannel(TaskService.OutputChannelId); - outputChannel.show(true); - outputChannel.append(nls.localize('ConfigureTaskRunnerAction.autoDetecting', 'Auto detecting tasks for {0}', selection.id) + '\n'); - let detector = new ProcessRunnerDetector(this.fileService, this.contextService, this.configurationResolverService); - contentPromise = detector.detect(false, selection.id).then((value) => { - let config = value.config; - if (value.stderr && value.stderr.length > 0) { - value.stderr.forEach((line) => { - outputChannel.append(line + '\n'); - }); - if (config && (!config.tasks || config.tasks.length === 0)) { - this.messageService.show(Severity.Warning, nls.localize('ConfigureTaskRunnerAction.autoDetect', 'Auto detecting the task system failed. Using default template. Consult the task output for details.')); - return selection.content; - } else { - this.messageService.show(Severity.Warning, nls.localize('ConfigureTaskRunnerAction.autoDetectError', 'Auto detecting the task system produced errors. Consult the task output for details.')); - } - } - if (config) { - if (value.stdout && value.stdout.length > 0) { - value.stdout.forEach(line => outputChannel.append(line + '\n')); - } - let content = JSON.stringify(config, null, '\t'); - content = [ - '{', - '\t// See https://go.microsoft.com/fwlink/?LinkId=733558', - '\t// for the documentation about the tasks.json format', - ].join('\n') + content.substr(1); - return content; - } else { - return selection.content; - } - }); - } else { - contentPromise = TPromise.as(selection.content); - } - return contentPromise.then(content => { - let editorConfig = this.configurationService.getConfiguration(); - if (editorConfig.editor.insertSpaces) { - content = content.replace(/(\n)(\t+)/g, (_, s1, s2) => s1 + strings.repeat(' ', s2.length * editorConfig.editor.tabSize)); - } - configFileCreated = true; - return this.fileService.createFile(this.contextService.toResource('.vscode/tasks.json', this.contextService.getWorkspace().folders[0]), content).then((result) => { - this.telemetryService.publicLog(TaskService.TemplateTelemetryEventName, { - templateId: selection.id, - autoDetect: selection.autoDetect - }); - return result; - }); // TODO@Dirk (https://github.com/Microsoft/vscode/issues/29454) - }); - /* 2.0 version - let content = selection.content; - let editorConfig = this.configurationService.getConfiguration(); - if (editorConfig.editor.insertSpaces) { - content = content.replace(/(\n)(\t+)/g, (_, s1, s2) => s1 + strings.repeat(' ', s2.length * editorConfig.editor.tabSize)); - } - configFileCreated = true; - return this.fileService.createFile(this.contextService.toResource('.vscode/tasks.json'), content); - */ - }); - }).then((stat) => { - if (!stat) { - return undefined; - } - // // (2) Open editor with configuration file - return this.editorService.openEditor({ - resource: stat.resource, - options: { - forceOpen: true, - pinned: configFileCreated // pin only if config file is created #8727 - } - }, sideBySide); - }, (error) => { - throw new Error(nls.localize('ConfigureTaskRunnerAction.failed', "Unable to create the 'tasks.json' file inside the '.vscode' folder. Consult the task output for details.")); - }); - } +namespace ConfigureTaskAction { + export const ID = 'workbench.action.tasks.configureTaskRunner'; + export const TEXT = nls.localize('ConfigureTaskRunnerAction.label', "Configure Task"); } -class ConfigureTaskRunnerAction extends OpenTaskConfigurationAction { - public static ID = 'workbench.action.tasks.configureTaskRunner'; - public static TEXT = nls.localize('ConfigureTaskRunnerAction.label', "Configure Task Runner"); - - constructor(id: string, label: string, - @ITaskService taskService: ITaskService, @IConfigurationService configurationService: IConfigurationService, - @IWorkbenchEditorService editorService: IWorkbenchEditorService, @IFileService fileService: IFileService, - @IWorkspaceContextService contextService: IWorkspaceContextService, @IOutputService outputService: IOutputService, - @IMessageService messageService: IMessageService, @IQuickOpenService quickOpenService: IQuickOpenService, - @IEnvironmentService environmentService: IEnvironmentService, - @IConfigurationResolverService configurationResolverService: IConfigurationResolverService, - @IExtensionService extensionService: IExtensionService, - @ITelemetryService telemetryService: ITelemetryService) { - super(id, label, taskService, configurationService, editorService, fileService, contextService, - outputService, messageService, quickOpenService, environmentService, configurationResolverService, - extensionService, telemetryService); - } -} - -class ConfigureBuildTaskAction extends OpenTaskConfigurationAction { - public static ID = 'workbench.action.tasks.configureBuildTask'; - public static TEXT = nls.localize('ConfigureBuildTaskAction.label', "Configure Build Task"); - - constructor(id: string, label: string, - @ITaskService taskService: ITaskService, @IConfigurationService configurationService: IConfigurationService, - @IWorkbenchEditorService editorService: IWorkbenchEditorService, @IFileService fileService: IFileService, - @IWorkspaceContextService contextService: IWorkspaceContextService, @IOutputService outputService: IOutputService, - @IMessageService messageService: IMessageService, @IQuickOpenService quickOpenService: IQuickOpenService, - @IEnvironmentService environmentService: IEnvironmentService, - @IConfigurationResolverService configurationResolverService: IConfigurationResolverService, - @IExtensionService extensionService: IExtensionService, - @ITelemetryService telemetryService: ITelemetryService) { - super(id, label, taskService, configurationService, editorService, fileService, contextService, - outputService, messageService, quickOpenService, environmentService, configurationResolverService, - extensionService, telemetryService); - } +namespace ConfigureBuildTaskAction { + export const ID = 'workbench.action.tasks.configureBuildTask'; + export const TEXT = nls.localize('ConfigureBuildTaskAction.label', "Configure Build Task"); } class CloseMessageAction extends Action { @@ -618,11 +478,11 @@ interface WorkspaceTaskResult { } interface WorkspaceFolderTaskResult extends WorkspaceTaskResult { - workspaceFolder: WorkspaceFolder; + workspaceFolder: IWorkspaceFolder; } interface WorkspaceFolderConfigurationResult { - workspaceFolder: WorkspaceFolder; + workspaceFolder: IWorkspaceFolder; config: TaskConfig.ExternalTaskRunnerConfiguration; hasErrors: boolean; } @@ -641,7 +501,7 @@ class TaskMap { this._store.forEach(callback); } - public get(workspaceFolder: WorkspaceFolder | string): Task[] { + public get(workspaceFolder: IWorkspaceFolder | string): Task[] { let result: Task[] = Types.isString(workspaceFolder) ? this._store.get(workspaceFolder) : this._store.get(workspaceFolder.uri.toString()); if (!result) { result = []; @@ -650,11 +510,11 @@ class TaskMap { return result; } - public has(workspaceFolder: WorkspaceFolder): boolean { + public has(workspaceFolder: IWorkspaceFolder): boolean { return this._store.has(workspaceFolder.uri.toString()); } - public add(workspaceFolder: WorkspaceFolder | string, ...task: Task[]): void { + public add(workspaceFolder: IWorkspaceFolder | string, ...task: Task[]): void { let values = Types.isString(workspaceFolder) ? this._store.get(workspaceFolder) : this._store.get(workspaceFolder.uri.toString()); if (!values) { values = []; @@ -670,6 +530,10 @@ class TaskMap { } } +interface TaskQuickPickEntry extends IPickOpenEntry { + task: Task; +} + class TaskService extends EventEmitter implements ITaskService { // private static autoDetectTelemetryName: string = 'taskServer.autoDetect'; @@ -700,9 +564,9 @@ class TaskService extends EventEmitter implements ITaskService { private quickOpenService: IQuickOpenService; private _configHasErrors: boolean; - private _schemaVersion: JsonSchemaVersion; - private _executionEngine: ExecutionEngine; - private _workspaceFolders: WorkspaceFolder[]; + private __schemaVersion: JsonSchemaVersion; + private __executionEngine: ExecutionEngine; + private __workspaceFolders: IWorkspaceFolder[]; private _providers: Map; private _workspaceTasksPromise: TPromise>; @@ -757,8 +621,8 @@ class TaskService extends EventEmitter implements ITaskService { if (!this._taskSystem && !this._workspaceTasksPromise) { return; } - let folderSetup = this.computeWorkspaceFolders(); - if (this._executionEngine !== folderSetup[1] && this._taskSystem && this._taskSystem.getActiveTasks().length > 0) { + let folderSetup = this.computeWorkspaceFolderSetup(); + if (this.executionEngine !== folderSetup[1] && this._taskSystem && this._taskSystem.getActiveTasks().length > 0) { this.messageService.show( Severity.Info, { @@ -774,15 +638,9 @@ class TaskService extends EventEmitter implements ITaskService { ); return; } - this._workspaceFolders = folderSetup[0]; - this._executionEngine = folderSetup[1]; - this._schemaVersion = folderSetup[2]; + this.updateSetup(folderSetup); this.updateWorkspaceTasks(); }); - let folderSetup = this.computeWorkspaceFolders(); - this._workspaceFolders = folderSetup[0]; - this._executionEngine = folderSetup[1]; - this._schemaVersion = folderSetup[2]; lifecycleService.onWillShutdown(event => event.veto(this.beforeShutdown())); this.registerCommands(); } @@ -828,6 +686,10 @@ class TaskService extends EventEmitter implements ITaskService { this.runTestCommand(); }); + CommandsRegistry.registerCommand('workbench.action.tasks.configureTaskRunner', () => { + this.runConfigureTasks(); + }); + CommandsRegistry.registerCommand('workbench.action.tasks.configureDefaultBuildTask', () => { this.runConfigureDefaultBuildTask(); }); @@ -841,6 +703,36 @@ class TaskService extends EventEmitter implements ITaskService { }); } + private get workspaceFolders(): IWorkspaceFolder[] { + if (!this.__workspaceFolders) { + this.updateSetup(); + } + return this.__workspaceFolders; + } + + private get executionEngine(): ExecutionEngine { + if (this.__executionEngine === void 0) { + this.updateSetup(); + } + return this.__executionEngine; + } + + private get schemaVersion(): JsonSchemaVersion { + if (this.__schemaVersion === void 0) { + this.updateSetup(); + } + return this.__schemaVersion; + } + + private updateSetup(setup?: [IWorkspaceFolder[], ExecutionEngine, JsonSchemaVersion]): void { + if (!setup) { + setup = this.computeWorkspaceFolderSetup(); + } + this.__workspaceFolders = setup[0]; + this.__executionEngine = setup[1]; + this.__schemaVersion = setup[2]; + } + private showOutput(): void { this._outputChannel.show(true); } @@ -860,7 +752,7 @@ class TaskService extends EventEmitter implements ITaskService { return this._providers.delete(handle); } - public getTask(folder: WorkspaceFolder | string, alias: string): TPromise { + public getTask(folder: IWorkspaceFolder | string, alias: string): TPromise { return this.getGroupedTasks().then((map) => { let values = map.get(folder); if (!values) { @@ -937,7 +829,7 @@ class TaskService extends EventEmitter implements ITaskService { return this.getGroupedTasks().then((tasks) => { let runnable = this.createRunnableTask(tasks, TaskGroup.Build); if (!runnable || !runnable.task) { - if (this._schemaVersion === JsonSchemaVersion.V0_1_0) { + if (this.schemaVersion === JsonSchemaVersion.V0_1_0) { throw new TaskError(Severity.Info, nls.localize('TaskService.noBuildTask1', 'No build task defined. Mark a task with \'isBuildCommand\' in the tasks.json file.'), TaskErrors.NoBuildTask); } else { throw new TaskError(Severity.Info, nls.localize('TaskService.noBuildTask2', 'No build task defined. Mark a task with as a \'build\' group in the tasks.json file.'), TaskErrors.NoBuildTask); @@ -962,7 +854,7 @@ class TaskService extends EventEmitter implements ITaskService { return this.getGroupedTasks().then((tasks) => { let runnable = this.createRunnableTask(tasks, TaskGroup.Test); if (!runnable || !runnable.task) { - if (this._schemaVersion === JsonSchemaVersion.V0_1_0) { + if (this.schemaVersion === JsonSchemaVersion.V0_1_0) { throw new TaskError(Severity.Info, nls.localize('TaskService.noTestTask1', 'No test task defined. Mark a task with \'isTestCommand\' in the tasks.json file.'), TaskErrors.NoTestTask); } else { throw new TaskError(Severity.Info, nls.localize('TaskService.noTestTask2', 'No test task defined. Mark a task with as a \'test\' group in the tasks.json file.'), TaskErrors.NoTestTask); @@ -981,7 +873,7 @@ class TaskService extends EventEmitter implements ITaskService { throw new TaskError(Severity.Info, nls.localize('TaskServer.noTask', 'Requested task {0} to execute not found.', task.name), TaskErrors.TaskNotFound); } else { let resolver = this.createResolver(grouped); - if (options && options.attachProblemMatcher && this.shouldAttachProblemMatcher(task) && !CompositeTask.is(task)) { + if (options && options.attachProblemMatcher && this.shouldAttachProblemMatcher(task) && !InMemoryTask.is(task)) { return this.attachProblemMatcher(task).then((toExecute) => { if (toExecute) { return this.executeTask(toExecute, resolver); @@ -1042,7 +934,7 @@ class TaskService extends EventEmitter implements ITaskService { } if (entries.length > 0) { entries = entries.sort((a, b) => a.label.localeCompare(b.label)); - entries[0].separator = { border: true }; + entries[0].separator = { border: true, label: nls.localize('TaskService.associate', 'associate') }; entries.unshift( { label: nls.localize('TaskService.attachProblemMatcher.continueWithout', 'Continue without scanning the task output'), matcher: undefined }, { label: nls.localize('TaskService.attachProblemMatcher.never', 'Never scan the task output'), matcher: undefined, never: true }, @@ -1090,12 +982,12 @@ class TaskService extends EventEmitter implements ITaskService { }); } - public hasMultipleFolders(): boolean { - return this._workspaceFolders && this._workspaceFolders.length > 1; + public needsFolderQualification(): boolean { + return this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE; } public canCustomize(task: Task): boolean { - if (this._schemaVersion !== JsonSchemaVersion.V2_0_0) { + if (this.schemaVersion !== JsonSchemaVersion.V2_0_0) { return false; } if (CustomTask.is(task)) { @@ -1166,7 +1058,7 @@ class TaskService extends EventEmitter implements ITaskService { if (editorConfig.editor.insertSpaces) { content = content.replace(/(\n)(\t+)/g, (_, s1, s2) => s1 + strings.repeat(' ', s2.length * editorConfig.editor.tabSize)); } - promise = this.fileService.createFile(this.contextService.toResource('.vscode/tasks.json', workspaceFolder), content).then(() => { }); + promise = this.fileService.createFile(workspaceFolder.toResource('.vscode/tasks.json'), content).then(() => { }); } else { let value: IConfigurationValue = { key: undefined, value: undefined }; // We have a global task configuration @@ -1205,7 +1097,7 @@ class TaskService extends EventEmitter implements ITaskService { }; this.telemetryService.publicLog(TaskService.CustomizationTelemetryEventName, event); if (openConfig) { - let resource = this.contextService.toResource('.vscode/tasks.json', workspaceFolder); + let resource = workspaceFolder.toResource('.vscode/tasks.json'); this.editorService.openEditor({ resource: resource, options: { @@ -1217,7 +1109,7 @@ class TaskService extends EventEmitter implements ITaskService { }); } - private writeConfiguration(workspaceFolder: WorkspaceFolder, value: IConfigurationValue): TPromise { + private writeConfiguration(workspaceFolder: IWorkspaceFolder, value: IConfigurationValue): TPromise { if (this.contextService.getWorkbenchState() === WorkbenchState.FOLDER) { return this.configurationEditingService.writeConfiguration(ConfigurationTarget.WORKSPACE, value); } else if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) { @@ -1228,7 +1120,7 @@ class TaskService extends EventEmitter implements ITaskService { } public openConfig(task: CustomTask): TPromise { - let resource = this.contextService.toResource(task._source.config.file, Task.getWorkspaceFolder(task)); + let resource = Task.getWorkspaceFolder(task).toResource(task._source.config.file); return this.editorService.openEditor({ resource: resource, options: { @@ -1272,7 +1164,7 @@ class TaskService extends EventEmitter implements ITaskService { } }); let resolver: ITaskResolver = { - resolve: (workspaceFolder: WorkspaceFolder, alias: string) => { + resolve: (workspaceFolder: IWorkspaceFolder, alias: string) => { let data = resolverData.get(workspaceFolder.uri.toString()); if (!data) { return undefined; @@ -1296,11 +1188,11 @@ class TaskService extends EventEmitter implements ITaskService { return { task: extensionTasks[0], resolver }; } else { let id: string = UUID.generateUuid(); - let task: CompositeTask = { + let task: InMemoryTask = { _id: id, - _source: { kind: TaskSourceKind.Composite, label: 'composite' }, + _source: { kind: TaskSourceKind.InMemory, label: 'inMemory' }, _label: id, - type: 'composite', + type: 'inMemory', name: id, identifier: id, dependsOn: extensionTasks.map((task) => { return { workspaceFolder: Task.getWorkspaceFolder(task), task: task._id }; }) @@ -1328,7 +1220,7 @@ class TaskService extends EventEmitter implements ITaskService { } }); return { - resolve: (workspaceFolder: WorkspaceFolder, alias: string) => { + resolve: (workspaceFolder: IWorkspaceFolder, alias: string) => { let data = resolverData.get(workspaceFolder.uri.toString()); if (!data) { return undefined; @@ -1345,7 +1237,10 @@ class TaskService extends EventEmitter implements ITaskService { return ProblemMatcherRegistry.onReady().then(() => { return this.textFileService.saveAll().then((value) => { // make sure all dirty files are saved let executeResult = this.getTaskSystem().run(task, resolver); - this.getRecentlyUsedTasks().set(Task.getKey(task), Task.getKey(task), Touch.First); + let key = Task.getRecentlyUsedKey(task); + if (key) { + this.getRecentlyUsedTasks().set(key, key, Touch.First); + } if (executeResult.kind === TaskExecuteKind.Active) { let active = executeResult.active; if (active.same) { @@ -1398,7 +1293,7 @@ class TaskService extends EventEmitter implements ITaskService { if (this._taskSystem) { return this._taskSystem; } - if (this._executionEngine === ExecutionEngine.Terminal) { + if (this.executionEngine === ExecutionEngine.Terminal) { this._taskSystem = new TerminalTaskSystem( this.terminalService, this.outputService, this.markerService, this.modelService, this.configurationResolverService, this.telemetryService, @@ -1438,7 +1333,7 @@ class TaskService extends EventEmitter implements ITaskService { resolve(result); } }; - if (this._schemaVersion === JsonSchemaVersion.V2_0_0 && this._providers.size > 0) { + if (this.schemaVersion === JsonSchemaVersion.V2_0_0 && this._providers.size > 0) { this._providers.forEach((provider) => { counter++; provider.provideTasks().done(done, error); @@ -1568,7 +1463,7 @@ class TaskService extends EventEmitter implements ITaskService { private updateWorkspaceTasks(): void { this._workspaceTasksPromise = this.computeWorkspaceTasks().then(value => { - if (this._executionEngine === ExecutionEngine.Process && this._taskSystem instanceof ProcessTaskSystem) { + if (this.executionEngine === ExecutionEngine.Process && this._taskSystem instanceof ProcessTaskSystem) { // We can only have a process engine if we have one folder. value.forEach((value) => { this._configHasErrors = value.hasErrors; @@ -1580,11 +1475,11 @@ class TaskService extends EventEmitter implements ITaskService { } private computeWorkspaceTasks(): TPromise> { - if (this._workspaceFolders.length === 0) { + if (this.workspaceFolders.length === 0) { return TPromise.as(new Map()); } else { let promises: TPromise[] = []; - for (let folder of this._workspaceFolders) { + for (let folder of this.workspaceFolders) { promises.push(this.computeWorkspaceFolderTasks(folder).then((value) => value, () => undefined)); } return TPromise.join(promises).then((values) => { @@ -1599,8 +1494,8 @@ class TaskService extends EventEmitter implements ITaskService { } } - private computeWorkspaceFolderTasks(workspaceFolder: WorkspaceFolder): TPromise { - return (this._executionEngine === ExecutionEngine.Process + private computeWorkspaceFolderTasks(workspaceFolder: IWorkspaceFolder): TPromise { + return (this.executionEngine === ExecutionEngine.Process ? this.computeLegacyConfiguration(workspaceFolder) : this.computeConfiguration(workspaceFolder)). then((workspaceFolderConfiguration) => { @@ -1633,19 +1528,19 @@ class TaskService extends EventEmitter implements ITaskService { }); } - private computeConfiguration(workspaceFolder: WorkspaceFolder): TPromise { + private computeConfiguration(workspaceFolder: IWorkspaceFolder): TPromise { let { config, hasParseErrors } = this.getConfiguration(workspaceFolder); return TPromise.as({ workspaceFolder, config, hasErrors: hasParseErrors }); } - private computeLegacyConfiguration(workspaceFolder: WorkspaceFolder): TPromise { + private computeLegacyConfiguration(workspaceFolder: IWorkspaceFolder): TPromise { let { config, hasParseErrors } = this.getConfiguration(workspaceFolder); if (hasParseErrors) { return TPromise.as({ workspaceFolder: workspaceFolder, hasErrors: true, config: undefined }); } if (config) { if (this.hasDetectorSupport(config)) { - return new ProcessRunnerDetector(this.fileService, this.contextService, this.configurationResolverService, config).detect(true).then((value): WorkspaceFolderConfigurationResult => { + return new ProcessRunnerDetector(workspaceFolder, this.fileService, this.contextService, this.configurationResolverService, config).detect(true).then((value): WorkspaceFolderConfigurationResult => { let hasErrors = this.printStderr(value.stderr); let detectedConfig = value.config; if (!detectedConfig) { @@ -1671,20 +1566,20 @@ class TaskService extends EventEmitter implements ITaskService { return TPromise.as({ workspaceFolder, config, hasErrors: false }); } } else { - return new ProcessRunnerDetector(this.fileService, this.contextService, this.configurationResolverService).detect(true).then((value) => { + return new ProcessRunnerDetector(workspaceFolder, this.fileService, this.contextService, this.configurationResolverService).detect(true).then((value) => { let hasErrors = this.printStderr(value.stderr); return { workspaceFolder, config: value.config, hasErrors }; }); } } - private computeWorkspaceFolders(): [WorkspaceFolder[], ExecutionEngine, JsonSchemaVersion] { - let workspaceFolders: WorkspaceFolder[] = []; + private computeWorkspaceFolderSetup(): [IWorkspaceFolder[], ExecutionEngine, JsonSchemaVersion] { + let workspaceFolders: IWorkspaceFolder[] = []; let executionEngine = ExecutionEngine.Terminal; let schemaVersion = JsonSchemaVersion.V2_0_0; if (this.contextService.getWorkbenchState() === WorkbenchState.FOLDER) { - let workspaceFolder: WorkspaceFolder = this.contextService.getWorkspace().folders[0]; + let workspaceFolder: IWorkspaceFolder = this.contextService.getWorkspace().folders[0]; workspaceFolders.push(workspaceFolder); executionEngine = this.computeExecutionEngine(workspaceFolder); schemaVersion = this.computeJsonSchemaVersion(workspaceFolder); @@ -1695,15 +1590,16 @@ class TaskService extends EventEmitter implements ITaskService { } else { this._outputChannel.append(nls.localize( 'taskService.ignoreingFolder', - 'Ignoring task configurations for workspace folder {0}. Multi root folder support requires that all folders use task version 2.0.', + 'Ignoring task configurations for workspace folder {0}. Multi root folder support requires that all folders use task version 2.0.0\n', workspaceFolder.uri.fsPath)); + this._outputChannel.show(true); } } } return [workspaceFolders, executionEngine, schemaVersion]; } - private computeExecutionEngine(workspaceFolder: WorkspaceFolder): ExecutionEngine { + private computeExecutionEngine(workspaceFolder: IWorkspaceFolder): ExecutionEngine { let { config } = this.getConfiguration(workspaceFolder); if (!config) { return ExecutionEngine._default; @@ -1711,7 +1607,7 @@ class TaskService extends EventEmitter implements ITaskService { return TaskConfig.ExecutionEngine.from(config); } - private computeJsonSchemaVersion(workspaceFolder: WorkspaceFolder): JsonSchemaVersion { + private computeJsonSchemaVersion(workspaceFolder: IWorkspaceFolder): JsonSchemaVersion { let { config } = this.getConfiguration(workspaceFolder); if (!config) { return JsonSchemaVersion.V2_0_0; @@ -1719,7 +1615,7 @@ class TaskService extends EventEmitter implements ITaskService { return TaskConfig.JsonSchemaVersion.from(config); } - private getConfiguration(workspaceFolder: WorkspaceFolder): { config: TaskConfig.ExternalTaskRunnerConfiguration; hasParseErrors: boolean } { + private getConfiguration(workspaceFolder: IWorkspaceFolder): { config: TaskConfig.ExternalTaskRunnerConfiguration; hasParseErrors: boolean } { let result = this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? this.configurationService.getConfiguration('tasks', { resource: workspaceFolder.uri }) : undefined; @@ -1760,7 +1656,7 @@ class TaskService extends EventEmitter implements ITaskService { if (this._taskSystem) { return this._taskSystem instanceof TerminalTaskSystem; } - return this._executionEngine === ExecutionEngine.Terminal; + return this.executionEngine === ExecutionEngine.Terminal; } private hasDetectorSupport(config: TaskConfig.ExternalTaskRunnerConfiguration): boolean { @@ -1771,17 +1667,21 @@ class TaskService extends EventEmitter implements ITaskService { } public configureAction(): Action { - return new ConfigureTaskRunnerAction(ConfigureTaskRunnerAction.ID, ConfigureTaskRunnerAction.TEXT, this, - this.configurationService, this.editorService, this.fileService, this.contextService, - this.outputService, this.messageService, this.quickOpenService, this.environmentService, this.configurationResolverService, - this.extensionService, this.telemetryService); + let run = () => { this.runConfigureTasks(); return TPromise.as(undefined); }; + return new class extends Action { + constructor() { + super(ConfigureTaskAction.ID, ConfigureTaskAction.TEXT, undefined, true, run); + } + }; } private configureBuildTask(): Action { - return new ConfigureBuildTaskAction(ConfigureBuildTaskAction.ID, ConfigureBuildTaskAction.TEXT, this, - this.configurationService, this.editorService, this.fileService, this.contextService, - this.outputService, this.messageService, this.quickOpenService, this.environmentService, this.configurationResolverService, - this.extensionService, this.telemetryService); + let run = () => { this.runConfigureTasks(); return TPromise.as(undefined); }; + return new class extends Action { + constructor() { + super(ConfigureTaskAction.ID, ConfigureTaskAction.TEXT, undefined, true, run); + } + }; } public beforeShutdown(): boolean | TPromise { @@ -1882,48 +1782,66 @@ class TaskService extends EventEmitter implements ITaskService { return true; } - private showQuickPick(tasks: Task[], placeHolder: string, group: boolean = false, sort: boolean = false): TPromise { + private createTaskQuickPickEntries(tasks: Task[], group: boolean = false, sort: boolean = false): TaskQuickPickEntry[] { if (tasks === void 0 || tasks === null || tasks.length === 0) { - return TPromise.as(undefined); + return []; } - interface TaskQickPickEntry extends IPickOpenEntry { - task: Task; - } - const TaskQickPickEntry = (task: Task): TaskQickPickEntry => { + const TaskQuickPickEntry = (task: Task): TaskQuickPickEntry => { let description: string; - if (this.hasMultipleFolders) { + if (this.needsFolderQualification()) { let workspaceFolder = Task.getWorkspaceFolder(task); if (workspaceFolder) { - description = `(${workspaceFolder.name})`; + description = workspaceFolder.name; } } return { label: task._label, description, task }; }; - function fillEntries(entries: TaskQickPickEntry[], tasks: Task[], groupLabel: string, withBorder: boolean = false): void { + let taskService = this; + let action = new class extends Action implements IPickOpenAction { + constructor() { + super('configureAction', 'Configure Task', 'quick-open-task-configure', true); + } + public run(item: IPickOpenItem): TPromise { + let task: Task = item.getPayload(); + taskService.quickOpenService.close(); + if (ContributedTask.is(task)) { + taskService.customize(task, undefined, true); + } else if (CustomTask.is(task)) { + taskService.openConfig(task); + } + return TPromise.as(false); + } + }; + function fillEntries(entries: TaskQuickPickEntry[], tasks: Task[], groupLabel: string, withBorder: boolean = false): void { let first = true; for (let task of tasks) { + let entry: TaskQuickPickEntry = TaskQuickPickEntry(task); if (first) { first = false; - let entry = TaskQickPickEntry(task); entry.separator = { label: groupLabel, border: withBorder }; - entries.push(entry); - } else { - entries.push(TaskQickPickEntry(task)); } + entry.action = action; + entry.payload = task; + entries.push(entry); } } - let entries: TaskQickPickEntry[]; + let entries: TaskQuickPickEntry[]; if (group) { entries = []; if (tasks.length === 1) { - entries.push(TaskQickPickEntry(tasks[0])); + entries.push(TaskQuickPickEntry(tasks[0])); } else { let recentlyUsedTasks = this.getRecentlyUsedTasks(); let recent: Task[] = []; let configured: Task[] = []; let detected: Task[] = []; let taskMap: IStringDictionary = Object.create(null); - tasks.forEach(task => taskMap[Task.getKey(task)] = task); + tasks.forEach(task => { + let key = Task.getRecentlyUsedKey(task); + if (key) { + taskMap[key] = task; + } + }); recentlyUsedTasks.keys().forEach(key => { let task = taskMap[key]; if (task) { @@ -1931,7 +1849,8 @@ class TaskService extends EventEmitter implements ITaskService { } }); for (let task of tasks) { - if (!recentlyUsedTasks.has(Task.getKey(task))) { + let key = Task.getRecentlyUsedKey(task); + if (!key || !recentlyUsedTasks.has(key)) { if (task._source.kind === TaskSourceKind.Workspace) { configured.push(task); } else { @@ -1953,9 +1872,25 @@ class TaskService extends EventEmitter implements ITaskService { const sorter = this.createSorter(); tasks = tasks.sort((a, b) => sorter.compare(a, b)); } - entries = tasks.map(task => TaskQickPickEntry(task)); + entries = tasks.map(task => TaskQuickPickEntry(task)); } - return this.quickOpenService.pick(entries, { placeHolder, autoFocus: { autoFocusFirstEntry: true } }).then(entry => entry ? entry.task : undefined); + return entries; + } + + private showQuickPick(tasks: TPromise | Task[], placeHolder: string, defaultEntry?: TaskQuickPickEntry, group: boolean = false, sort: boolean = false): TPromise { + let _createEntries = (): TPromise => { + if (Array.isArray(tasks)) { + return TPromise.as(this.createTaskQuickPickEntries(tasks, group, sort)); + } else { + return tasks.then((tasks) => this.createTaskQuickPickEntries(tasks, group, sort)); + } + }; + return this.quickOpenService.pick(_createEntries().then((entries) => { + if (entries.length === 0 && defaultEntry) { + entries.push(defaultEntry); + } + return entries; + }), { placeHolder, autoFocus: { autoFocusFirstEntry: true } }).then(entry => entry ? entry.task : undefined); } private runTaskCommand(accessor: ServicesAccessor, arg: any): void { @@ -1973,20 +1908,39 @@ class TaskService extends EventEmitter implements ITaskService { return; } } - this.quickOpenService.show('task '); + this.doRunTaskCommand(grouped.all()); }, () => { - this.quickOpenService.show('task '); + this.doRunTaskCommand(); }); } else { - this.quickOpenService.show('task '); + this.doRunTaskCommand(); } } + private doRunTaskCommand(tasks?: Task[]): void { + this.showQuickPick(tasks ? tasks : this.tasks(), + nls.localize('TaskService.pickRunTask', 'Select the task to run'), + { + label: nls.localize('TaslService.noEntryToRun', 'No task to run found. Configure Tasks...'), + task: null + }, + true).then((task) => { + if (task === void 0) { + return; + } + if (task === null) { + this.runConfigureTasks(); + } else { + this.run(task, { attachProblemMatcher: true }); + } + }); + } + private runBuildCommand(): void { if (!this.canRunCommand()) { return; } - if (this._schemaVersion === JsonSchemaVersion.V0_1_0) { + if (this.schemaVersion === JsonSchemaVersion.V0_1_0) { this.build(); return; } @@ -1995,32 +1949,35 @@ class TaskService extends EventEmitter implements ITaskService { title: nls.localize('TaskService.fetchingBuildTasks', 'Fetching build tasks...') }; let promise = this.getTasksForGroup(TaskGroup.Build).then((tasks) => { - if (tasks.length === 0) { - this.messageService.show( - Severity.Info, - { - message: nls.localize('TaskService.noBuildTaskTerminal', 'No Build Task found. Press \'Configure Build Task\' to define one.'), - actions: [this.configureBuildTask(), new CloseMessageAction()] + if (tasks.length > 0) { + let primaries: Task[] = []; + for (let task of tasks) { + // We only have build tasks here + if (task.isDefaultGroupEntry) { + primaries.push(task); } - ); - return; - } - let primaries: Task[] = []; - for (let task of tasks) { - // We only have build tasks here - if (task.isDefaultGroupEntry) { - primaries.push(task); + } + if (primaries.length === 1) { + this.run(primaries[0]); + return; } } - if (primaries.length === 1) { - this.run(primaries[0]); - return; - } - this.showQuickPick(tasks, nls.localize('TaskService.pickBuildTask', 'Select the build task to run'), true).then((task) => { - if (task) { + this.showQuickPick(tasks, + nls.localize('TaskService.pickBuildTask', 'Select the build task to run'), + { + label: nls.localize('TaskService.noBuildTask', 'No build task to run found. Configure Tasks...'), + task: null + }, + true).then((task) => { + if (task === void 0) { + return; + } + if (task === null) { + this.runConfigureTasks(); + return; + } this.run(task, { attachProblemMatcher: true }); - } - }); + }); }); this.progressService.withProgress(options, () => promise); } @@ -2029,7 +1986,7 @@ class TaskService extends EventEmitter implements ITaskService { if (!this.canRunCommand()) { return; } - if (this._schemaVersion === JsonSchemaVersion.V0_1_0) { + if (this.schemaVersion === JsonSchemaVersion.V0_1_0) { this.runTest(); return; } @@ -2038,31 +1995,34 @@ class TaskService extends EventEmitter implements ITaskService { title: nls.localize('TaskService.fetchingTestTasks', 'Fetching test tasks...') }; let promise = this.getTasksForGroup(TaskGroup.Test).then((tasks) => { - if (tasks.length === 0) { - this.messageService.show( - Severity.Info, - { - message: nls.localize('TaskService.noTestTaskTerminal', 'No Test Task found. Press \'Configure Task Runner\' to define one.'), - actions: [this.configureAction(), new CloseMessageAction()] + if (tasks.length > 0) { + let primaries: Task[] = []; + for (let task of tasks) { + // We only have test task here. + if (task.isDefaultGroupEntry) { + primaries.push(task); } - ); - return; - } - let primaries: Task[] = []; - for (let task of tasks) { - // We only have test task here. - if (task.isDefaultGroupEntry) { - primaries.push(task); + } + if (primaries.length === 1) { + this.run(primaries[0]); + return; } } - if (primaries.length === 1) { - this.run(primaries[0]); - return; - } - this.showQuickPick(tasks, nls.localize('TaskService.pickTestTask', 'Select the test task to run'), true).then((task) => { - if (task) { - this.run(task); + this.showQuickPick(tasks, + nls.localize('TaskService.pickTestTask', 'Select the test task to run'), + { + label: nls.localize('TaskService.noTestTaskTerminal', 'No test task to run found. Configure Tasks...'), + task: null + }, true + ).then((task) => { + if (task === void 0) { + return; } + if (task === null) { + this.runConfigureTasks(); + return; + } + this.run(task); }); }); this.progressService.withProgress(options, () => promise); @@ -2073,16 +2033,18 @@ class TaskService extends EventEmitter implements ITaskService { return; } if (this.inTerminal()) { - this.getActiveTasks().then((activeTasks) => { - if (activeTasks.length === 0) { - this.messageService.show(Severity.Info, nls.localize('TaskService.noTaskRunning', 'No task is currently running.')); + this.showQuickPick(this.getActiveTasks(), + nls.localize('TaskService.tastToTerminate', 'Select task to terminate'), + { + label: nls.localize('TaskService.noTaskRunning', 'No task is currently running'), + task: null + }, + false, true + ).then(task => { + if (task === void 0 || task === null) { return; } - this.showQuickPick(activeTasks, nls.localize('TaskService.tastToTerminate', 'Select task to terminate'), false, true).then(task => { - if (task) { - this.terminate(task); - } - }); + this.terminate(task); }); } else { this.isActive().then((active) => { @@ -2109,16 +2071,18 @@ class TaskService extends EventEmitter implements ITaskService { return; } if (this.inTerminal()) { - this.getActiveTasks().then((activeTasks) => { - if (activeTasks.length === 0) { - this.messageService.show(Severity.Info, nls.localize('TaskService.noTaskToRestart', 'No task to restart.')); + this.showQuickPick(this.getActiveTasks(), + nls.localize('TaskService.tastToRestart', 'Select the task to restart'), + { + label: nls.localize('TaskService.noTaskToRestart', 'No task to restart'), + task: null + }, + false, true + ).then(task => { + if (task === void 0 || task === null) { return; } - this.showQuickPick(activeTasks, nls.localize('TaskService.tastToRestart', 'Select the task to restart'), false, true).then(task => { - if (task) { - this.restart(task); - } - }); + this.restart(task); }); } else { this.getActiveTasks().then((activeTasks) => { @@ -2131,17 +2095,139 @@ class TaskService extends EventEmitter implements ITaskService { } } + private runConfigureTasks(): void { + if (!this.canRunCommand()) { + return undefined; + } + let taskPromise: TPromise; + if (this.schemaVersion === JsonSchemaVersion.V2_0_0) { + taskPromise = this.getGroupedTasks(); + } else { + taskPromise = TPromise.as(new TaskMap()); + } + + let openTaskFile = (workspaceFolder: IWorkspaceFolder): void => { + let resource = workspaceFolder.toResource('.vscode/tasks.json'); + let configFileCreated = false; + this.fileService.resolveFile(resource).then((stat) => stat, () => undefined).then((stat) => { + if (stat) { + return stat.resource; + } + return this.quickOpenService.pick(taskTemplates, { placeHolder: nls.localize('TaskService.template', 'Select a Task Template') }).then((selection) => { + if (!selection) { + return undefined; + } + let content = selection.content; + let editorConfig = this.configurationService.getConfiguration(); + if (editorConfig.editor.insertSpaces) { + content = content.replace(/(\n)(\t+)/g, (_, s1, s2) => s1 + strings.repeat(' ', s2.length * editorConfig.editor.tabSize)); + } + configFileCreated = true; + return this.fileService.createFile(resource, content).then((result): URI => { + this.telemetryService.publicLog(TaskService.TemplateTelemetryEventName, { + templateId: selection.id, + autoDetect: selection.autoDetect + }); + return result.resource; + }); + }); + }).then((resource) => { + if (!resource) { + return; + } + this.editorService.openEditor({ + resource: resource, + options: { + forceOpen: true, + pinned: configFileCreated // pin only if config file is created #8727 + } + }, false); + }); + }; + + let configureTask = (task: Task): void => { + if (ContributedTask.is(task)) { + this.customize(task, undefined, true); + } else if (CustomTask.is(task)) { + this.openConfig(task); + } + }; + + function isTaskEntry(value: IPickOpenEntry): value is IPickOpenEntry & { task: Task } { + let candidate: IPickOpenEntry & { task: Task } = value as any; + return candidate && !!candidate.task; + } + + let stats = this.contextService.getWorkspace().folders.map>((folder) => { + return this.fileService.resolveFile(folder.toResource('.vscode/tasks.json')).then(stat => stat, () => undefined); + }); + + let createLabel = nls.localize('TaskService.createJsonFile', 'Create tasks.json file from template'); + let openLabel = nls.localize('TaskService.openJsonFile', 'Open tasks.json file'); + let entries = TPromise.join(stats).then((stats) => { + return taskPromise.then((taskMap) => { + type EntryType = (IPickOpenEntry & { task: Task; }) | (IPickOpenEntry & { folder: IWorkspaceFolder; }); + let entries: EntryType[] = []; + if (this.contextService.getWorkbenchState() === WorkbenchState.FOLDER) { + let tasks = taskMap.all(); + if (tasks.length > 0) { + tasks = tasks.sort((a, b) => a._label.localeCompare(b._label)); + entries = tasks.map(task => { return { label: task._label, task }; }); + } else { + let label = stats[0] !== void 0 ? openLabel : createLabel; + entries.push({ label, folder: this.contextService.getWorkspace().folders[0] }); + } + } else { + let folders = this.contextService.getWorkspace().folders; + let index = 0; + for (let folder of folders) { + let tasks = taskMap.get(folder); + if (tasks.length > 0) { + tasks = tasks.slice().sort((a, b) => a._label.localeCompare(b._label)); + for (let i = 0; i < tasks.length; i++) { + let entry: EntryType = { label: tasks[i]._label, task: tasks[i], description: folder.name }; + if (i === 0) { + entry.separator = { label: folder.name, border: index > 0 }; + } + entries.push(entry); + } + } else { + let label = stats[index] !== void 0 ? openLabel : createLabel; + let entry: EntryType = { label, folder: folder }; + entry.separator = { label: folder.name, border: index > 0 }; + entries.push(entry); + } + index++; + } + } + return entries; + }); + }); + + this.quickOpenService.pick(entries, { placeHolder: nls.localize('TaskService.pickTask', 'Select a task to configure'), autoFocus: { autoFocusFirstEntry: true } }).then((selection) => { + if (!selection) { + return; + } + if (isTaskEntry(selection)) { + configureTask(selection.task); + } else { + openTaskFile(selection.folder); + } + }); + } + private runConfigureDefaultBuildTask(): void { if (!this.canRunCommand()) { return; } - if (this._schemaVersion === JsonSchemaVersion.V2_0_0) { + if (this.schemaVersion === JsonSchemaVersion.V2_0_0) { this.tasks().then((tasks => { if (tasks.length === 0) { - this.configureBuildTask().run(); + this.runConfigureTasks(); return; } let defaultTask: Task; + let defaultEntry: TaskQuickPickEntry; for (let task of tasks) { if (task.group === TaskGroup.Build && task.isDefaultGroupEntry) { defaultTask = task; @@ -2149,20 +2235,27 @@ class TaskService extends EventEmitter implements ITaskService { } } if (defaultTask) { - this.messageService.show(Severity.Info, nls.localize('TaskService.defaultBuildTaskExists', '{0} is already marked as the default build task.', Task.getQualifiedLabel(defaultTask))); - return; + tasks = []; + defaultEntry = { + label: nls.localize('TaskService.defaultBuildTaskExists', '{0} is already marked as the default build task', Task.getQualifiedLabel(defaultTask)), + task: defaultTask + }; } - this.showQuickPick(tasks, nls.localize('TaskService.pickDefaultBuildTask', 'Select the task to be used as the default build task'), true).then((task) => { - if (!task) { - return; - } - if (!CompositeTask.is(task)) { - this.customize(task, { group: { kind: 'build', isDefault: true } }, true); - } - }); + this.showQuickPick(tasks, + nls.localize('TaskService.pickDefaultBuildTask', 'Select the task to be used as the default build task'), defaultEntry, true).then((task) => { + if (task === void 0) { + return; + } + if (task === defaultTask && CustomTask.is(task)) { + this.openConfig(task); + } + if (!InMemoryTask.is(task)) { + this.customize(task, { group: { kind: 'build', isDefault: true } }, true); + } + }); })); } else { - this.configureBuildTask().run(); + this.runConfigureTasks(); } } @@ -2170,10 +2263,10 @@ class TaskService extends EventEmitter implements ITaskService { if (!this.canRunCommand()) { return; } - if (this._schemaVersion === JsonSchemaVersion.V2_0_0) { + if (this.schemaVersion === JsonSchemaVersion.V2_0_0) { this.tasks().then((tasks => { if (tasks.length === 0) { - this.configureAction().run(); + this.runConfigureTasks(); } let defaultTask: Task; for (let task of tasks) { @@ -2186,17 +2279,17 @@ class TaskService extends EventEmitter implements ITaskService { this.messageService.show(Severity.Info, nls.localize('TaskService.defaultTestTaskExists', '{0} is already marked as the default test task.', Task.getQualifiedLabel(defaultTask))); return; } - this.showQuickPick(tasks, nls.localize('TaskService.pickDefaultTestTask', 'Select the task to be used as the default test task'), true).then((task) => { + this.showQuickPick(tasks, nls.localize('TaskService.pickDefaultTestTask', 'Select the task to be used as the default test task'), undefined, true).then((task) => { if (!task) { return; } - if (!CompositeTask.is(task)) { + if (!InMemoryTask.is(task)) { this.customize(task, { group: { kind: 'test', isDefault: true } }, true); } }); })); } else { - this.configureAction().run(); + this.runConfigureTasks(); } } @@ -2204,33 +2297,23 @@ class TaskService extends EventEmitter implements ITaskService { if (!this.canRunCommand()) { return; } - if (!this._taskSystem) { - this.messageService.show(Severity.Info, nls.localize('TaskService.noTaskIsRunning', 'No task is running.')); - return; - } - this.getActiveTasks().then((tasks) => { - if (tasks.length === 0) { - this.messageService.show(Severity.Info, nls.localize('TaskService.noTaskIsRunning', 'No task is running.')); - } else if (tasks.length === 1) { - if (this._taskSystem) { - this._taskSystem.revealTask(tasks[0]); - } - } else { - this.showQuickPick(tasks, nls.localize('TaskService.pickShowTask', 'Select the task to show its output'), false, true).then((task) => { - if (!task || !this._taskSystem) { - return; - } - this._taskSystem.revealTask(task); - }); + this.showQuickPick(this.getActiveTasks(), + nls.localize('TaskService.pickShowTask', 'Select the task to show its output'), + { + label: nls.localize('TaskService.noTaskIsRunning', 'No task is running'), + task: null + }, + false, true + ).then((task) => { + if (task === void 0 || task === null) { + return; } + this._taskSystem.revealTask(task); }); } } - -let workbenchActionsRegistry = Registry.as(WorkbenchActionExtensions.WorkbenchActions); -workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ConfigureTaskRunnerAction, ConfigureTaskRunnerAction.ID, ConfigureTaskRunnerAction.TEXT), 'Tasks: Configure Task Runner', tasksCategory); - +MenuRegistry.addCommand({ id: ConfigureTaskAction.ID, title: { value: ConfigureTaskAction.TEXT, original: 'Configure Task' }, category: { value: tasksCategory, original: 'Tasks' } }); MenuRegistry.addCommand({ id: 'workbench.action.tasks.showLog', title: { value: nls.localize('ShowLogAction.label', "Show Task Log"), original: 'Show Task Log' }, category: { value: tasksCategory, original: 'Tasks' } }); MenuRegistry.addCommand({ id: 'workbench.action.tasks.runTask', title: { value: nls.localize('RunTaskAction.label', "Run Task"), original: 'Run Task' }, category: { value: tasksCategory, original: 'Tasks' } }); MenuRegistry.addCommand({ id: 'workbench.action.tasks.restartTask', title: { value: nls.localize('RestartTaskAction.label', "Restart Running Task"), original: 'Restart Running Task' }, category: { value: tasksCategory, original: 'Tasks' } }); diff --git a/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts b/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts index 1176ca1c7af..39b6e37ba07 100644 --- a/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts +++ b/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts @@ -22,7 +22,7 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import * as TPath from 'vs/base/common/paths'; import { IMarkerService } from 'vs/platform/markers/common/markers'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IModelService } from 'vs/editor/common/services/modelService'; import { ProblemMatcher, ProblemMatcherRegistry /*, ProblemPattern, getResource */ } from 'vs/platform/markers/common/problemMatcher'; @@ -440,6 +440,8 @@ export class TerminalTaskSystem extends EventEmitter implements ITaskSystem { }; let shellLaunchConfig: IShellLaunchConfig = undefined; let isShellCommand = task.command.runtime === RuntimeType.Shell; + let workspaceFolder = Task.getWorkspaceFolder(task); + let needsFolderQualification = workspaceFolder && this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE; if (isShellCommand) { if (Platform.isWindows && ((options.cwd && TPath.isUNC(options.cwd)) || (!options.cwd && TPath.isUNC(process.cwd())))) { throw new TaskError(Severity.Error, nls.localize('TerminalTaskSystem', 'Can\'t execute a shell command on an UNC drive.'), TaskErrors.UnknownError); @@ -492,7 +494,11 @@ export class TerminalTaskSystem extends EventEmitter implements ITaskSystem { shellArgs.push(commandLine); shellLaunchConfig.args = windowsShellArgs ? shellArgs.join(' ') : shellArgs; if (task.command.presentation.echo) { - shellLaunchConfig.initialText = `\x1b[1m> Executing task: ${commandLine} <\x1b[0m\n`; + if (needsFolderQualification) { + shellLaunchConfig.initialText = `\x1b[1m> Executing task in folder ${workspaceFolder.name}: ${commandLine} <\x1b[0m\n`; + } else { + shellLaunchConfig.initialText = `\x1b[1m> Executing task: ${commandLine} <\x1b[0m\n`; + } } } else { let cwd = options && options.cwd ? options.cwd : process.cwd(); @@ -515,7 +521,11 @@ export class TerminalTaskSystem extends EventEmitter implements ITaskSystem { } return args.join(' '); }; - shellLaunchConfig.initialText = `\x1b[1m> Executing task: ${shellLaunchConfig.executable} ${getArgsToEcho(shellLaunchConfig.args)} <\x1b[0m\n`; + if (needsFolderQualification) { + shellLaunchConfig.initialText = `\x1b[1m> Executing task in folder ${workspaceFolder.name}: ${shellLaunchConfig.executable} ${getArgsToEcho(shellLaunchConfig.args)} <\x1b[0m\n`; + } else { + shellLaunchConfig.initialText = `\x1b[1m> Executing task: ${shellLaunchConfig.executable} ${getArgsToEcho(shellLaunchConfig.args)} <\x1b[0m\n`; + } } } if (options.cwd) { diff --git a/src/vs/workbench/parts/tasks/node/processRunnerDetector.ts b/src/vs/workbench/parts/tasks/node/processRunnerDetector.ts index 6a8926fec1d..8d54a4baca4 100644 --- a/src/vs/workbench/parts/tasks/node/processRunnerDetector.ts +++ b/src/vs/workbench/parts/tasks/node/processRunnerDetector.ts @@ -18,7 +18,7 @@ import { IFileService } from 'vs/platform/files/common/files'; import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; -import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import * as Tasks from '../common/tasks'; import * as TaskConfig from './taskConfiguration'; @@ -145,18 +145,20 @@ export class ProcessRunnerDetector { private contextService: IWorkspaceContextService; private configurationResolverService: IConfigurationResolverService; private taskConfiguration: TaskConfig.ExternalTaskRunnerConfiguration; + private _workspaceRoot: IWorkspaceFolder; private _stderr: string[]; private _stdout: string[]; private _cwd: string; - constructor(fileService: IFileService, contextService: IWorkspaceContextService, configurationResolverService: IConfigurationResolverService, config: TaskConfig.ExternalTaskRunnerConfiguration = null) { + constructor(workspaceFolder: IWorkspaceFolder, fileService: IFileService, contextService: IWorkspaceContextService, configurationResolverService: IConfigurationResolverService, config: TaskConfig.ExternalTaskRunnerConfiguration = null) { this.fileService = fileService; this.contextService = contextService; this.configurationResolverService = configurationResolverService; this.taskConfiguration = config; + this._workspaceRoot = workspaceFolder; this._stderr = []; this._stdout = []; - this._cwd = this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? Paths.normalize(this.contextService.getWorkspace().folders[0].uri.fsPath, true) : ''; + this._cwd = this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? Paths.normalize(this._workspaceRoot.uri.fsPath, true) : ''; } public get stderr(): string[] { @@ -171,21 +173,20 @@ export class ProcessRunnerDetector { if (this.taskConfiguration && this.taskConfiguration.command && ProcessRunnerDetector.supports(this.taskConfiguration.command)) { let config = ProcessRunnerDetector.detectorConfig(this.taskConfiguration.command); let args = (this.taskConfiguration.args || []).concat(config.arg); - let options: CommandOptions = this.taskConfiguration.options ? this.resolveCommandOptions(this.taskConfiguration.options) : { cwd: this._cwd }; + let options: CommandOptions = this.taskConfiguration.options ? this.resolveCommandOptions(this._workspaceRoot, this.taskConfiguration.options) : { cwd: this._cwd }; let isShellCommand = !!this.taskConfiguration.isShellCommand; - // TODO@Dirk adopt new configuration resolver service https://github.com/Microsoft/vscode/issues/31365 return this.runDetection( - new LineProcess(this.taskConfiguration.command, this.configurationResolverService.resolve(this.contextService.getWorkspace().folders[0], args), isShellCommand, options), + new LineProcess(this.taskConfiguration.command, this.configurationResolverService.resolve(this._workspaceRoot, args), isShellCommand, options), this.taskConfiguration.command, isShellCommand, config.matcher, ProcessRunnerDetector.DefaultProblemMatchers, list); } else { if (detectSpecific) { let detectorPromise: TPromise; if ('gulp' === detectSpecific) { - detectorPromise = this.tryDetectGulp(list); + detectorPromise = this.tryDetectGulp(this._workspaceRoot, list); } else if ('jake' === detectSpecific) { - detectorPromise = this.tryDetectJake(list); + detectorPromise = this.tryDetectJake(this._workspaceRoot, list); } else if ('grunt' === detectSpecific) { - detectorPromise = this.tryDetectGrunt(list); + detectorPromise = this.tryDetectGrunt(this._workspaceRoot, list); } return detectorPromise.then((value) => { if (value) { @@ -195,15 +196,15 @@ export class ProcessRunnerDetector { } }); } else { - return this.tryDetectGulp(list).then((value) => { + return this.tryDetectGulp(this._workspaceRoot, list).then((value) => { if (value) { return value; } - return this.tryDetectJake(list).then((value) => { + return this.tryDetectJake(this._workspaceRoot, list).then((value) => { if (value) { return value; } - return this.tryDetectGrunt(list).then((value) => { + return this.tryDetectGrunt(this._workspaceRoot, list).then((value) => { if (value) { return value; } @@ -215,20 +216,20 @@ export class ProcessRunnerDetector { } } - private resolveCommandOptions(options: CommandOptions): CommandOptions { + private resolveCommandOptions(workspaceFolder: IWorkspaceFolder, options: CommandOptions): CommandOptions { // TODO@Dirk adopt new configuration resolver service https://github.com/Microsoft/vscode/issues/31365 let result = Objects.clone(options); if (result.cwd) { - result.cwd = this.configurationResolverService.resolve(this.contextService.getWorkspace().folders[0], result.cwd); + result.cwd = this.configurationResolverService.resolve(workspaceFolder, result.cwd); } if (result.env) { - result.env = this.configurationResolverService.resolve(this.contextService.getWorkspace().folders[0], result.env); + result.env = this.configurationResolverService.resolve(workspaceFolder, result.env); } return result; } - private tryDetectGulp(list: boolean): TPromise { - return this.fileService.resolveFile(this.contextService.toResource('gulpfile.js', this.contextService.getWorkspace().folders[0])).then((stat) => { // TODO@Dirk (https://github.com/Microsoft/vscode/issues/29454) + private tryDetectGulp(workspaceFolder: IWorkspaceFolder, list: boolean): TPromise { + return this.fileService.resolveFile(workspaceFolder.toResource('gulpfile.js')).then((stat) => { // TODO@Dirk (https://github.com/Microsoft/vscode/issues/29454) let config = ProcessRunnerDetector.detectorConfig('gulp'); let process = new LineProcess('gulp', [config.arg, '--no-color'], true, { cwd: this._cwd }); return this.runDetection(process, 'gulp', true, config.matcher, ProcessRunnerDetector.DefaultProblemMatchers, list); @@ -237,8 +238,8 @@ export class ProcessRunnerDetector { }); } - private tryDetectGrunt(list: boolean): TPromise { - return this.fileService.resolveFile(this.contextService.toResource('Gruntfile.js', this.contextService.getWorkspace().folders[0])).then((stat) => { // TODO@Dirk (https://github.com/Microsoft/vscode/issues/29454) + private tryDetectGrunt(workspaceFolder: IWorkspaceFolder, list: boolean): TPromise { + return this.fileService.resolveFile(workspaceFolder.toResource('Gruntfile.js')).then((stat) => { // TODO@Dirk (https://github.com/Microsoft/vscode/issues/29454) let config = ProcessRunnerDetector.detectorConfig('grunt'); let process = new LineProcess('grunt', [config.arg, '--no-color'], true, { cwd: this._cwd }); return this.runDetection(process, 'grunt', true, config.matcher, ProcessRunnerDetector.DefaultProblemMatchers, list); @@ -247,16 +248,16 @@ export class ProcessRunnerDetector { }); } - private tryDetectJake(list: boolean): TPromise { + private tryDetectJake(workspaceFolder: IWorkspaceFolder, list: boolean): TPromise { let run = () => { let config = ProcessRunnerDetector.detectorConfig('jake'); let process = new LineProcess('jake', [config.arg], true, { cwd: this._cwd }); return this.runDetection(process, 'jake', true, config.matcher, ProcessRunnerDetector.DefaultProblemMatchers, list); }; - return this.fileService.resolveFile(this.contextService.toResource('Jakefile', this.contextService.getWorkspace().folders[0])).then((stat) => { // TODO@Dirk (https://github.com/Microsoft/vscode/issues/29454) + return this.fileService.resolveFile(workspaceFolder.toResource('Jakefile')).then((stat) => { // TODO@Dirk (https://github.com/Microsoft/vscode/issues/29454) return run(); }, (err: any) => { - return this.fileService.resolveFile(this.contextService.toResource('Jakefile.js', this.contextService.getWorkspace().folders[0])).then((stat) => { // TODO@Dirk (https://github.com/Microsoft/vscode/issues/29454) + return this.fileService.resolveFile(workspaceFolder.toResource('Jakefile.js')).then((stat) => { // TODO@Dirk (https://github.com/Microsoft/vscode/issues/29454) return run(); }, (err: any) => { return null; diff --git a/src/vs/workbench/parts/tasks/node/processTaskSystem.ts b/src/vs/workbench/parts/tasks/node/processTaskSystem.ts index e0006516a31..6074ce58b46 100644 --- a/src/vs/workbench/parts/tasks/node/processTaskSystem.ts +++ b/src/vs/workbench/parts/tasks/node/processTaskSystem.ts @@ -181,9 +181,9 @@ export class ProcessTaskSystem extends EventEmitter implements ITaskSystem { } let args: string[] = commandConfig.args ? commandConfig.args.slice() : []; - args = this.resolveVariables(args); - let command: string = this.resolveVariable(commandConfig.name); - this.childProcess = new LineProcess(command, args, commandConfig.runtime === RuntimeType.Shell, this.resolveOptions(commandConfig.options)); + args = this.resolveVariables(task, args); + let command: string = this.resolveVariable(task, commandConfig.name); + this.childProcess = new LineProcess(command, args, commandConfig.runtime === RuntimeType.Shell, this.resolveOptions(task, commandConfig.options)); telemetryEvent.command = this.childProcess.getSanitizedCommand(); // we have no problem matchers defined. So show the output log let reveal = task.command.presentation.reveal; @@ -196,7 +196,7 @@ export class ProcessTaskSystem extends EventEmitter implements ITaskSystem { this.log(`running command${prompt} ${command} ${args.join(' ')}`); } if (task.isBackground) { - let watchingProblemMatcher = new WatchingProblemCollector(this.resolveMatchers(task.problemMatchers), this.markerService, this.modelService); + let watchingProblemMatcher = new WatchingProblemCollector(this.resolveMatchers(task, task.problemMatchers), this.markerService, this.modelService); let toUnbind: IDisposable[] = []; let event: TaskEvent = { taskId: task._id, taskName: task.name, type: TaskType.Watching, group: task.group }; let eventCounter: number = 0; @@ -259,7 +259,7 @@ export class ProcessTaskSystem extends EventEmitter implements ITaskSystem { } else { let event: TaskEvent = { taskId: task._id, taskName: task.name, type: TaskType.SingleRun, group: task.group }; this.emit(TaskSystemEvents.Active, event); - let startStopProblemMatcher = new StartStopProblemCollector(this.resolveMatchers(task.problemMatchers), this.markerService, this.modelService); + let startStopProblemMatcher = new StartStopProblemCollector(this.resolveMatchers(task, task.problemMatchers), this.markerService, this.modelService); this.activeTask = task; this.activeTaskPromise = this.childProcess.start().then((success): ITaskSummary => { this.childProcessEnded(); @@ -329,14 +329,14 @@ export class ProcessTaskSystem extends EventEmitter implements ITaskSystem { return false; } - private resolveOptions(options: CommandOptions): CommandOptions { - let result: CommandOptions = { cwd: this.resolveVariable(options.cwd) }; + private resolveOptions(task: CustomTask, options: CommandOptions): CommandOptions { + let result: CommandOptions = { cwd: this.resolveVariable(task, options.cwd) }; if (options.env) { result.env = Object.create(null); Object.keys(options.env).forEach((key) => { let value: any = options.env[key]; if (Types.isString(value)) { - result.env[key] = this.resolveVariable(value); + result.env[key] = this.resolveVariable(task, value); } else { result.env[key] = value.toString(); } @@ -345,11 +345,11 @@ export class ProcessTaskSystem extends EventEmitter implements ITaskSystem { return result; } - private resolveVariables(value: string[]): string[] { - return value.map(s => this.resolveVariable(s)); + private resolveVariables(task: CustomTask, value: string[]): string[] { + return value.map(s => this.resolveVariable(task, s)); } - private resolveMatchers(values: (string | ProblemMatcher)[]): ProblemMatcher[] { + private resolveMatchers(task: CustomTask, values: (string | ProblemMatcher)[]): ProblemMatcher[] { if (values === void 0 || values === null || values.length === 0) { return []; } @@ -373,16 +373,15 @@ export class ProcessTaskSystem extends EventEmitter implements ITaskSystem { result.push(matcher); } else { let copy = Objects.clone(matcher); - copy.filePrefix = this.resolveVariable(copy.filePrefix); + copy.filePrefix = this.resolveVariable(task, copy.filePrefix); result.push(copy); } }); return result; } - private resolveVariable(value: string): string { - // TODO@Dirk adopt new configuration resolver service https://github.com/Microsoft/vscode/issues/31365 - return this.configurationResolverService.resolve(this.contextService.getWorkspace().folders[0], value); + private resolveVariable(task: CustomTask, value: string): string { + return this.configurationResolverService.resolve(Task.getWorkspaceFolder(task), value); } public log(value: string): void { diff --git a/src/vs/workbench/parts/tasks/node/taskConfiguration.ts b/src/vs/workbench/parts/tasks/node/taskConfiguration.ts index 5720bd9573e..b6cd314e6e8 100644 --- a/src/vs/workbench/parts/tasks/node/taskConfiguration.ts +++ b/src/vs/workbench/parts/tasks/node/taskConfiguration.ts @@ -20,7 +20,7 @@ import { isNamedProblemMatcher, ProblemMatcherRegistry } from 'vs/platform/markers/common/problemMatcher'; -import { WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import * as Tasks from '../common/tasks'; import { TaskDefinitionRegistry } from '../common/taskDefinitionRegistry'; @@ -563,7 +563,7 @@ function _freeze(this: void, target: T, properties: MetaData[]): Read } interface ParseContext { - workspaceFolder: WorkspaceFolder; + workspaceFolder: IWorkspaceFolder; problemReporter: IProblemReporter; namedProblemMatchers: IStringDictionary; uuidMap: UUIDMap; @@ -1154,12 +1154,42 @@ namespace ConfiguringTask { identifier = { type }; - Object.keys(typeDeclaration.properties).forEach((property) => { + let properties = typeDeclaration.properties; + let required: Set = new Set(); + if (Array.isArray(typeDeclaration.required)) { + typeDeclaration.required.forEach(element => Types.isString(element) ? required.add(element) : required); + } + for (let property of Object.keys(properties)) { let value = external[property]; if (value !== void 0 && value !== null) { identifier[property] = value; + } else if (required.has(property)) { + let schema = properties[property]; + if (schema.default !== void 0) { + identifier[property] = Objects.deepClone(schema.default); + } else { + switch (schema.type) { + case 'boolean': + identifier[property] = false; + break; + case 'number': + case 'integer': + identifier[property] = 0; + break; + case 'string': + identifier[property] = ''; + break; + default: + let message = nls.localize( + 'ConfigurationParser.missingRequiredProperty', + 'Error: the task configuration \'{0}\' missed the required property \'{1}\'. The task configuration will be ignored.', JSON.stringify(external, undefined, 0), property + ); + context.problemReporter.error(message); + return undefined; + } + } } - }); + } } let taskIdentifier = TaskIdentifier.from(identifier); let configElement: Tasks.TaskSourceConfigElement = { @@ -1682,11 +1712,11 @@ class UUIDMap { class ConfigurationParser { - private workspaceFolder: WorkspaceFolder; + private workspaceFolder: IWorkspaceFolder; private problemReporter: IProblemReporter; private uuidMap: UUIDMap; - constructor(workspaceFolder: WorkspaceFolder, problemReporter: IProblemReporter, uuidMap: UUIDMap) { + constructor(workspaceFolder: IWorkspaceFolder, problemReporter: IProblemReporter, uuidMap: UUIDMap) { this.workspaceFolder = workspaceFolder; this.problemReporter = problemReporter; this.uuidMap = uuidMap; @@ -1791,7 +1821,7 @@ class ConfigurationParser { } let uuidMaps: Map = new Map(); -export function parse(workspaceFolder: WorkspaceFolder, configuration: ExternalTaskRunnerConfiguration, logger: IProblemReporter): ParseResult { +export function parse(workspaceFolder: IWorkspaceFolder, configuration: ExternalTaskRunnerConfiguration, logger: IProblemReporter): ParseResult { let uuidMap = uuidMaps.get(workspaceFolder.uri.toString()); if (!uuidMap) { uuidMap = new UUIDMap(); diff --git a/src/vs/workbench/parts/tasks/test/electron-browser/configuration.test.ts b/src/vs/workbench/parts/tasks/test/electron-browser/configuration.test.ts index 715c3e1c11a..4d212d7fd89 100644 --- a/src/vs/workbench/parts/tasks/test/electron-browser/configuration.test.ts +++ b/src/vs/workbench/parts/tasks/test/electron-browser/configuration.test.ts @@ -12,17 +12,16 @@ import * as UUID from 'vs/base/common/uuid'; import * as Platform from 'vs/base/common/platform'; import { ValidationStatus } from 'vs/base/common/parsers'; import { ProblemMatcher, FileLocationKind, ProblemPattern, ApplyToKind } from 'vs/platform/markers/common/problemMatcher'; -import { WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceFolder, WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import * as Tasks from 'vs/workbench/parts/tasks/common/tasks'; import { parse, ParseResult, IProblemReporter, ExternalTaskRunnerConfiguration, CustomTask } from 'vs/workbench/parts/tasks/node/taskConfiguration'; -const workspaceFolder: WorkspaceFolder = { +const workspaceFolder: IWorkspaceFolder = new WorkspaceFolder({ uri: URI.file('/workspace/folderOne'), name: 'folderOne', - index: 0, - raw: { path: '../folderOne', name: 'folderOne' } -}; + index: 0 +}); class ProblemReporter implements IProblemReporter { @@ -459,7 +458,7 @@ function assertConfiguration(result: ParseResult, expected: Tasks.Task[]): void function assertTask(actual: Tasks.Task, expected: Tasks.Task) { assert.ok(actual._id); assert.strictEqual(actual.name, expected.name, 'name'); - if (!Tasks.CompositeTask.is(actual) && !Tasks.CompositeTask.is(expected)) { + if (!Tasks.InMemoryTask.is(actual) && !Tasks.InMemoryTask.is(expected)) { assertCommandConfiguration(actual.command, expected.command); } assert.strictEqual(actual.isBackground, expected.isBackground, 'isBackground'); diff --git a/src/vs/workbench/parts/terminal/electron-browser/media/terminal.css b/src/vs/workbench/parts/terminal/electron-browser/media/terminal.css index 1d9961ab042..e032054ea47 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/media/terminal.css +++ b/src/vs/workbench/parts/terminal/electron-browser/media/terminal.css @@ -41,7 +41,10 @@ height: 100%; } -.monaco-workbench .panel.integrated-terminal .xterm-viewport, +.monaco-workbench .panel.integrated-terminal .xterm-viewport { + margin-right: -20px; +} + .monaco-workbench .panel.integrated-terminal canvas { /* Align the viewport and canvases to the bottom of the panel */ position: absolute; @@ -71,8 +74,9 @@ } .monaco-workbench .panel.integrated-terminal .xterm { - position: relative; - height: 100%; + position: absolute; + bottom: 0; + left: 0; user-select: none; } diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts b/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts index 9b937f11ea5..a3c434dacd3 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts @@ -343,7 +343,7 @@ actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusTerminalFin }, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Focus Find Widget', category); actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(HideTerminalFindWidgetAction, HideTerminalFindWidgetAction.ID, HideTerminalFindWidgetAction.LABEL, { primary: KeyCode.Escape, - secondary: [KeyCode.Shift | KeyCode.Escape] + secondary: [KeyMod.Shift | KeyCode.Escape] }, ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE)), 'Terminal: Hide Find Widget', category); actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ShowNextFindTermTerminalFindWidgetAction, ShowNextFindTermTerminalFindWidgetAction.ID, ShowNextFindTermTerminalFindWidgetAction.LABEL, { primary: KeyMod.Alt | KeyCode.DownArrow diff --git a/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts b/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts index c57f74e828a..90336d7e2ff 100644 --- a/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts +++ b/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts @@ -365,7 +365,7 @@ class WelcomePage { this.messageService.show(Severity.Info, strings.alreadyInstalled.replace('{0}', extensionSuggestion.name)); return; } - const foundAndInstalled = installedExtension ? TPromise.as(true) : this.extensionGalleryService.query({ names: [extensionSuggestion.id] }) + const foundAndInstalled = installedExtension ? TPromise.as(true) : this.extensionGalleryService.query({ names: [extensionSuggestion.id], source: telemetryFrom }) .then(result => { const [extension] = result.firstPage; if (!extension) { diff --git a/src/vs/workbench/services/configuration/node/configuration.ts b/src/vs/workbench/services/configuration/node/configuration.ts index fd6f77db55d..8fed9ed5247 100644 --- a/src/vs/workbench/services/configuration/node/configuration.ts +++ b/src/vs/workbench/services/configuration/node/configuration.ts @@ -9,7 +9,6 @@ import * as paths from 'vs/base/common/paths'; import { TPromise } from 'vs/base/common/winjs.base'; import Event, { Emitter } from 'vs/base/common/event'; import { StrictResourceMap } from 'vs/base/common/map'; -import { equals } from 'vs/base/common/arrays'; import * as objects from 'vs/base/common/objects'; import * as errors from 'vs/base/common/errors'; import * as collections from 'vs/base/common/collections'; @@ -18,7 +17,7 @@ import { RunOnceScheduler } from 'vs/base/common/async'; import { readFile, stat, writeFile } from 'vs/base/node/pfs'; import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import * as extfs from 'vs/base/node/extfs'; -import { IWorkspaceContextService, IWorkspace, Workspace, WorkbenchState, WorkspaceFolder, toWorkspaceFolders } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, Workspace, WorkbenchState, IWorkspaceFolder, toWorkspaceFolders, IWorkspaceFoldersChangeEvent } from 'vs/platform/workspace/common/workspace'; import { FileChangeType, FileChangesEvent } from 'vs/platform/files/common/files'; import { isLinux } from 'vs/base/common/platform'; import { ConfigWatcher } from 'vs/base/node/config'; @@ -31,9 +30,9 @@ import { ConfigurationService as GlobalConfigurationService } from 'vs/platform/ import * as nls from 'vs/nls'; import { Registry } from 'vs/platform/registry/common/platform'; import { ExtensionsRegistry, ExtensionMessageCollector } from 'vs/platform/extensions/common/extensionsRegistry'; -import { IConfigurationNode, IConfigurationRegistry, Extensions, editorConfigurationSchemaId, IDefaultConfigurationExtension, validateProperty, ConfigurationScope, schemaId } from 'vs/platform/configuration/common/configurationRegistry'; +import { IConfigurationNode, IConfigurationRegistry, Extensions, editorConfigurationSchemaId, IDefaultConfigurationExtension, validateProperty, ConfigurationScope, settingsSchema, resourceSettingsSchema } from 'vs/platform/configuration/common/configurationRegistry'; import { createHash } from 'crypto'; -import { getWorkspaceLabel, IWorkspacesService, IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { getWorkspaceLabel, IWorkspacesService, IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, IStoredWorkspace } from 'vs/platform/workspaces/common/workspaces'; import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; import { IExtensionService } from 'vs/platform/extensions/common/extensions'; @@ -41,6 +40,11 @@ import { ICommandService } from 'vs/platform/commands/common/commands'; import product from 'vs/platform/node/product'; import pkg from 'vs/platform/node/package'; +const defaultSettingsSchemaId = 'vscode://schemas/settings/default'; +const userSettingsSchemaId = 'vscode://schemas/settings/user'; +const workspaceSettingsSchemaId = 'vscode://schemas/settings/workspace'; +const folderSettingsSchemaId = 'vscode://schemas/settings/folder'; + interface IStat { resource: URI; isDirectory?: boolean; @@ -178,6 +182,7 @@ function validateProperties(configuration: IConfigurationNode, collector: Extens const message = validateProperty(key); const propertyConfiguration = configuration.properties[key]; propertyConfiguration.scope = propertyConfiguration.scope && propertyConfiguration.scope.toString() === 'resource' ? ConfigurationScope.RESOURCE : ConfigurationScope.WINDOW; + propertyConfiguration.isFromExtensions = true; if (message) { collector.warn(message); delete properties[key]; @@ -207,29 +212,44 @@ contributionRegistry.registerSchema('vscode://schemas/workspaceConfig', { required: ['folders'], properties: { 'folders': { - minItems: 1, + minItems: 0, uniqueItems: true, - description: nls.localize('workspaceConfig.folders.description', "List of folders to be loaded in the workspace. Must be a file path. e.g. `/root/folderA` or `./folderA` for a relative path that will be resolved against the location of the workspace file."), + description: nls.localize('workspaceConfig.folders.description', "List of folders to be loaded in the workspace."), items: { type: 'object', default: { path: '' }, - properties: { - path: { - type: 'string', - description: nls.localize('workspaceConfig.folder.description', "A file path. e.g. `/root/folderA` or `./folderA` for a relative path that will be resolved against the location of the workspace file.") + oneOf: [{ + properties: { + path: { + type: 'string', + description: nls.localize('workspaceConfig.path.description', "A file path. e.g. `/root/folderA` or `./folderA` for a relative path that will be resolved against the location of the workspace file.") + }, + name: { + type: 'string', + description: nls.localize('workspaceConfig.name.description', "An optional name for the folder. ") + } }, - name: { - type: 'string', - description: nls.localize('workspaceConfig.name.description', "An optional name for the folder. ") - } - } + required: ['path'] + }, { + properties: { + uri: { + type: 'string', + description: nls.localize('workspaceConfig.uri.description', "URI of the folder") + }, + name: { + type: 'string', + description: nls.localize('workspaceConfig.name.description', "An optional name for the folder. ") + } + }, + required: ['uri'] + }] } }, 'settings': { type: 'object', default: {}, description: nls.localize('workspaceConfig.settings.description', "Workspace settings"), - $ref: schemaId + $ref: workspaceSettingsSchemaId }, 'extensions': { type: 'object', @@ -253,8 +273,8 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat protected readonly _onDidUpdateConfiguration: Emitter = this._register(new Emitter()); public readonly onDidUpdateConfiguration: Event = this._onDidUpdateConfiguration.event; - protected readonly _onDidChangeWorkspaceFolders: Emitter = this._register(new Emitter()); - public readonly onDidChangeWorkspaceFolders: Event = this._onDidChangeWorkspaceFolders.event; + protected readonly _onDidChangeWorkspaceFolders: Emitter = this._register(new Emitter()); + public readonly onDidChangeWorkspaceFolders: Event = this._onDidChangeWorkspaceFolders.event; protected readonly _onDidChangeWorkspaceName: Emitter = this._register(new Emitter()); public readonly onDidChangeWorkspaceName: Event = this._onDidChangeWorkspaceName.event; @@ -270,9 +290,10 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat this.baseConfigurationService = this._register(new GlobalConfigurationService(environmentService)); this._register(this.baseConfigurationService.onDidUpdateConfiguration(e => this.onBaseConfigurationChanged(e))); + this._register(configurationRegistry.onDidRegisterConfiguration(e => this.registerConfigurationSchemas())); } - public getWorkspace(): IWorkspace { + public getWorkspace(): Workspace { return this.workspace; } @@ -291,7 +312,7 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat return WorkbenchState.EMPTY; } - public getWorkspaceFolder(resource: URI): WorkspaceFolder { + public getWorkspaceFolder(resource: URI): IWorkspaceFolder { return this.workspace.getFolder(resource); } @@ -309,10 +330,6 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat return false; } - public toResource(workspaceRelativePath: string, workspaceFolder: WorkspaceFolder): URI { - return URI.file(paths.join(workspaceFolder.uri.fsPath, workspaceRelativePath)); - } - public getConfigurationData(): IConfigurationData { return this._configuration.toData(); } @@ -391,9 +408,6 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat .then(() => { const workspaceConfigurationModel = this.workspaceConfiguration.workspaceConfigurationModel; const workspaceFolders = toWorkspaceFolders(workspaceConfigurationModel.folders, URI.file(paths.dirname(workspaceConfigPath.fsPath))); - if (!workspaceFolders.length) { - return TPromise.wrapError(new Error('Invalid workspace configuraton file ' + workspaceConfigPath)); - } const workspaceId = workspaceIdentifier.id; const workspaceName = getWorkspaceLabel({ id: workspaceId, configPath: workspaceConfigPath.fsPath }, this.environmentService); return new Workspace(workspaceId, workspaceName, workspaceFolders, workspaceConfigPath); @@ -438,12 +452,27 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat this._onDidChangeWorkspaceName.fire(); } - if (!equals(this.workspace.folders, currentFolders, (folder1, folder2) => folder1.uri.fsPath === folder2.uri.fsPath)) { - this._onDidChangeWorkspaceFolders.fire(); + const changes = this.compareFolders(currentFolders, this.workspace.folders); + if (changes.added.length || changes.removed.length || changes.changed.length) { + this._onDidChangeWorkspaceFolders.fire(changes); } } + private compareFolders(currentFolders: IWorkspaceFolder[], newFolders: IWorkspaceFolder[]): IWorkspaceFoldersChangeEvent { + const result = { added: [], removed: [], changed: [] }; + + result.added = newFolders.filter(newFolder => !currentFolders.some(currentFolder => newFolder.uri.toString() === currentFolder.uri.toString())); + result.removed = currentFolders.filter(currentFolder => !newFolders.some(newFolder => currentFolder.uri.toString() === newFolder.uri.toString())); + + if (result.added.length === 0 && result.removed.length === 0) { + result.changed = currentFolders.filter((currentFolder, index) => newFolders[index].uri.toString() !== currentFolder.uri.toString()); + } + + return result; + } + private initializeConfiguration(trigger: boolean = true): TPromise { + this.registerConfigurationSchemas(); this.resetCaches(); return this.updateConfiguration() .then(() => { @@ -459,20 +488,36 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat this.initCachesForFolders(this.workspace.folders); } - private initCachesForFolders(folders: WorkspaceFolder[]): void { + private initCachesForFolders(folders: IWorkspaceFolder[]): void { for (const folder of folders) { this.cachedFolderConfigs.set(folder.uri, this._register(new FolderConfiguration(folder.uri, this.workspaceSettingsRootFolder, this.getWorkbenchState() === WorkbenchState.WORKSPACE ? ConfigurationScope.RESOURCE : ConfigurationScope.WINDOW))); this.updateFolderConfiguration(folder, new FolderConfigurationModel(new FolderSettingsModel(null), [], ConfigurationScope.RESOURCE), false); } } - private updateConfiguration(folders: WorkspaceFolder[] = this.workspace.folders): TPromise { + private updateConfiguration(folders: IWorkspaceFolder[] = this.workspace.folders): TPromise { return TPromise.join([...folders.map(folder => this.cachedFolderConfigs.get(folder.uri).loadConfiguration() .then(configuration => this.updateFolderConfiguration(folder, configuration, true)))]) .then(changed => changed.reduce((result, value) => result || value, false)) .then(changed => this.updateWorkspaceConfiguration(true) || changed); } + private registerConfigurationSchemas(): void { + if (this.workspace) { + + contributionRegistry.registerSchema(defaultSettingsSchemaId, settingsSchema); + contributionRegistry.registerSchema(userSettingsSchemaId, settingsSchema); + + if (WorkbenchState.WORKSPACE === this.getWorkbenchState()) { + contributionRegistry.registerSchema(workspaceSettingsSchemaId, settingsSchema); + contributionRegistry.registerSchema(folderSettingsSchemaId, resourceSettingsSchema); + } else { + contributionRegistry.registerSchema(workspaceSettingsSchemaId, settingsSchema); + contributionRegistry.registerSchema(folderSettingsSchemaId, settingsSchema); + } + } + } + private onBaseConfigurationChanged({ source, sourceConfig }: IConfigurationServiceEvent): void { if (this.workspace) { if (source === ConfigurationSource.Default) { @@ -487,15 +532,15 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat private onWorkspaceConfigurationChanged(): void { if (this.workspace && this.workspace.configuration) { let configuredFolders = toWorkspaceFolders(this.workspaceConfiguration.workspaceConfigurationModel.folders, URI.file(paths.dirname(this.workspace.configuration.fsPath))); - const foldersChanged = !equals(this.workspace.folders, configuredFolders, (folder1, folder2) => folder1.uri.fsPath === folder2.uri.fsPath); - if (foldersChanged) { // TODO@Sandeep be smarter here about detecting changes + const changes = this.compareFolders(this.workspace.folders, configuredFolders); + if (changes.added.length || changes.removed.length || changes.changed.length) { // TODO@Sandeep be smarter here about detecting changes this.workspace.folders = configuredFolders; this.onFoldersChanged() .then(configurationChanged => { - this._onDidChangeWorkspaceFolders.fire(); if (configurationChanged) { this.triggerConfigurationChange(); } + this._onDidChangeWorkspaceFolders.fire(changes); }); } else { const configurationChanged = this.updateWorkspaceConfiguration(true); @@ -532,7 +577,7 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat return TPromise.as(false); } - private updateFolderConfiguration(folder: WorkspaceFolder, folderConfiguration: FolderConfigurationModel, compare: boolean): boolean { + private updateFolderConfiguration(folder: IWorkspaceFolder, folderConfiguration: FolderConfigurationModel, compare: boolean): boolean { let configurationChanged = this._configuration.updateFolderConfiguration(folder.uri, folderConfiguration, compare); if (this.getWorkbenchState() === WorkbenchState.FOLDER) { // Workspace configuration changed @@ -589,7 +634,10 @@ class WorkspaceConfiguration extends Disposable { this._workspaceConfigurationWatcherDisposables = dispose(this._workspaceConfigurationWatcherDisposables); return new TPromise((c, e) => { this._workspaceConfigurationWatcher = new ConfigWatcher(this._workspaceConfigPath.fsPath, { - changeBufferDelay: 300, onError: error => errors.onUnexpectedError(error), defaultConfig: new WorkspaceConfigurationModel(null, this._workspaceConfigPath.fsPath), parse: (content: string, parseErrors: any[]) => { + changeBufferDelay: 300, + onError: error => errors.onUnexpectedError(error), + defaultConfig: new WorkspaceConfigurationModel(JSON.stringify({ folders: [] } as IStoredWorkspace, null, '\t'), this._workspaceConfigPath.fsPath), + parse: (content: string, parseErrors: any[]) => { const workspaceConfigurationModel = new WorkspaceConfigurationModel(content, this._workspaceConfigPath.fsPath); parseErrors = [...workspaceConfigurationModel.errors]; return workspaceConfigurationModel; @@ -841,7 +889,7 @@ export class Configuration extends BaseConfiguration { } deleteFolderConfiguration(folder: URI): boolean { - if (this._workspace && this._workspace.folders.length > 0 && this._workspace.folders[0].uri.fsPath === folder.fsPath) { + if (this._workspace && this._workspace.folders.length > 0 && this._workspace.folders[0].uri.toString() === folder.toString()) { // Do not remove workspace configuration return false; } diff --git a/src/vs/workbench/services/configuration/node/configurationEditingService.ts b/src/vs/workbench/services/configuration/node/configurationEditingService.ts index 409fbff7b9f..fa81d5be3cf 100644 --- a/src/vs/workbench/services/configuration/node/configurationEditingService.ts +++ b/src/vs/workbench/services/configuration/node/configurationEditingService.ts @@ -338,7 +338,7 @@ export class ConfigurationEditingService implements IConfigurationEditingService return workspace.configuration; } if (workbenchState === WorkbenchState.FOLDER) { - return this.contextService.toResource(relativePath, workspace.folders[0]); + return workspace.folders[0].toResource(relativePath); } } @@ -346,7 +346,7 @@ export class ConfigurationEditingService implements IConfigurationEditingService if (resource) { const folder = this.contextService.getWorkspaceFolder(resource); if (folder) { - return this.contextService.toResource(relativePath, folder); + return folder.toResource(relativePath); } } } diff --git a/src/vs/workbench/services/configuration/test/node/configuration.test.ts b/src/vs/workbench/services/configuration/test/node/configuration.test.ts index 32fc370158e..8073bb82a98 100644 --- a/src/vs/workbench/services/configuration/test/node/configuration.test.ts +++ b/src/vs/workbench/services/configuration/test/node/configuration.test.ts @@ -100,7 +100,6 @@ suite('WorkspaceContextService - Folder', () => { assert.equal(actual.folders[0].uri.fsPath, URI.file(workspaceResource).fsPath); assert.equal(actual.folders[0].name, workspaceName); assert.equal(actual.folders[0].index, 0); - assert.equal(actual.folders[0].raw.path.toLowerCase(), workspaceResource.toLowerCase()); assert.ok(!actual.configuration); }); @@ -529,4 +528,4 @@ suite('WorkspaceConfigurationService - Node', () => { }); }); }); -}); \ No newline at end of file +}); diff --git a/src/vs/workbench/services/configurationResolver/common/configurationResolver.ts b/src/vs/workbench/services/configurationResolver/common/configurationResolver.ts index 9e191e760ec..e2dc0febf79 100644 --- a/src/vs/workbench/services/configurationResolver/common/configurationResolver.ts +++ b/src/vs/workbench/services/configurationResolver/common/configurationResolver.ts @@ -6,7 +6,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { IStringDictionary } from 'vs/base/common/collections'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; export const IConfigurationResolverService = createDecorator('configurationResolverService'); @@ -14,9 +14,9 @@ export interface IConfigurationResolverService { _serviceBrand: any; // TODO@Isidor improve this API - resolve(root: WorkspaceFolder, value: string): string; - resolve(root: WorkspaceFolder, value: string[]): string[]; - resolve(root: WorkspaceFolder, value: IStringDictionary): IStringDictionary; - resolveAny(root: WorkspaceFolder, value: T): T; + resolve(root: IWorkspaceFolder, value: string): string; + resolve(root: IWorkspaceFolder, value: string[]): string[]; + resolve(root: IWorkspaceFolder, value: IStringDictionary): IStringDictionary; + resolveAny(root: IWorkspaceFolder, value: T): T; resolveInteractiveVariables(configuration: any, interactiveVariablesMap: { [key: string]: string }): TPromise; } diff --git a/src/vs/workbench/services/configurationResolver/node/configurationResolverService.ts b/src/vs/workbench/services/configurationResolver/node/configurationResolverService.ts index 183fa02bf13..d5bb08cfbb4 100644 --- a/src/vs/workbench/services/configurationResolver/node/configurationResolverService.ts +++ b/src/vs/workbench/services/configurationResolver/node/configurationResolverService.ts @@ -13,14 +13,14 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { ICommonCodeEditor } from 'vs/editor/common/editorCommon'; -import { WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { toResource } from 'vs/workbench/common/editor'; export class ConfigurationResolverService implements IConfigurationResolverService { _serviceBrand: any; private _execPath: string; - private _lastWorkspaceFolder: WorkspaceFolder; + private _lastWorkspaceFolder: IWorkspaceFolder; constructor( envVariables: { [key: string]: string }, @@ -109,10 +109,10 @@ export class ConfigurationResolverService implements IConfigurationResolverServi return paths.normalize(fileResource.fsPath, true); } - public resolve(root: WorkspaceFolder, value: string): string; - public resolve(root: WorkspaceFolder, value: string[]): string[]; - public resolve(root: WorkspaceFolder, value: IStringDictionary): IStringDictionary; - public resolve(root: WorkspaceFolder, value: any): any { + public resolve(root: IWorkspaceFolder, value: string): string; + public resolve(root: IWorkspaceFolder, value: string[]): string[]; + public resolve(root: IWorkspaceFolder, value: IStringDictionary): IStringDictionary; + public resolve(root: IWorkspaceFolder, value: any): any { try { this._lastWorkspaceFolder = root; if (types.isString(value)) { @@ -128,8 +128,8 @@ export class ConfigurationResolverService implements IConfigurationResolverServi } } - public resolveAny(root: WorkspaceFolder, value: T): T; - public resolveAny(root: WorkspaceFolder, value: any): any { + public resolveAny(root: IWorkspaceFolder, value: T): T; + public resolveAny(root: IWorkspaceFolder, value: any): any { try { this._lastWorkspaceFolder = root; if (types.isString(value)) { @@ -145,7 +145,7 @@ export class ConfigurationResolverService implements IConfigurationResolverServi } } - private resolveString(root: WorkspaceFolder, value: string): string { + private resolveString(root: IWorkspaceFolder, value: string): string { let regexp = /\$\{(.*?)\}/g; const originalValue = value; const resolvedString = value.replace(regexp, (match: string, name: string) => { @@ -160,7 +160,7 @@ export class ConfigurationResolverService implements IConfigurationResolverServi return this.resolveConfigVariable(root, resolvedString, originalValue); } - private resolveConfigVariable(root: WorkspaceFolder, value: string, originalValue: string): string { + private resolveConfigVariable(root: IWorkspaceFolder, value: string, originalValue: string): string { const replacer = (match: string, name: string) => { let config = this.configurationService.getConfiguration(); let newValue: any; @@ -191,7 +191,7 @@ export class ConfigurationResolverService implements IConfigurationResolverServi return value.replace(/\$\{config:(.+?)\}/g, replacer); } - private resolveLiteral(root: WorkspaceFolder, values: IStringDictionary | string[]>): IStringDictionary | string[]> { + private resolveLiteral(root: IWorkspaceFolder, values: IStringDictionary | string[]>): IStringDictionary | string[]> { let result: IStringDictionary | string[]> = Object.create(null); Object.keys(values).forEach(key => { let value = values[key]; @@ -200,8 +200,8 @@ export class ConfigurationResolverService implements IConfigurationResolverServi return result; } - private resolveAnyLiteral(root: WorkspaceFolder, values: T): T; - private resolveAnyLiteral(root: WorkspaceFolder, values: any): any { + private resolveAnyLiteral(root: IWorkspaceFolder, values: T): T; + private resolveAnyLiteral(root: IWorkspaceFolder, values: any): any { let result: IStringDictionary | string[]> = Object.create(null); Object.keys(values).forEach(key => { let value = values[key]; @@ -210,12 +210,12 @@ export class ConfigurationResolverService implements IConfigurationResolverServi return result; } - private resolveArray(root: WorkspaceFolder, value: string[]): string[] { + private resolveArray(root: IWorkspaceFolder, value: string[]): string[] { return value.map(s => this.resolveString(root, s)); } - private resolveAnyArray(root: WorkspaceFolder, value: T[]): T[]; - private resolveAnyArray(root: WorkspaceFolder, value: any[]): any[] { + private resolveAnyArray(root: IWorkspaceFolder, value: T[]): T[]; + private resolveAnyArray(root: IWorkspaceFolder, value: any[]): any[] { return value.map(s => this.resolveAny(root, s)); } diff --git a/src/vs/workbench/services/configurationResolver/test/node/configurationResolverService.test.ts b/src/vs/workbench/services/configurationResolver/test/node/configurationResolverService.test.ts index 139e1c758b6..7841abf9606 100644 --- a/src/vs/workbench/services/configurationResolver/test/node/configurationResolverService.test.ts +++ b/src/vs/workbench/services/configurationResolver/test/node/configurationResolverService.test.ts @@ -11,7 +11,7 @@ import { IConfigurationService, getConfigurationValue, IConfigurationOverrides, import { ICommandService } from 'vs/platform/commands/common/commands'; import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; import { ConfigurationResolverService } from 'vs/workbench/services/configurationResolver/node/configurationResolverService'; -import { WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { TestEnvironmentService, TestEditorService } from 'vs/workbench/test/workbenchTestServices'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; @@ -20,7 +20,7 @@ suite('Configuration Resolver Service', () => { let envVariables: { [key: string]: string } = { key1: 'Value for Key1', key2: 'Value for Key2' }; let mockCommandService: MockCommandService; let editorService: TestEditorService; - let workspace: WorkspaceFolder; + let workspace: IWorkspaceFolder; setup(() => { @@ -30,7 +30,7 @@ suite('Configuration Resolver Service', () => { uri: uri.parse('file:///VSCode/workspaceLocation'), name: 'hey', index: 0, - raw: undefined + toResource: () => null }; configurationResolverService = new ConfigurationResolverService(envVariables, editorService, TestEnvironmentService, new TestConfigurationService(), mockCommandService); }); diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index f2ac5bbf146..9a2f95831b5 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -23,6 +23,7 @@ import { getPathLabel } from 'vs/base/common/labels'; import { ResourceMap } from 'vs/base/common/map'; import { once } from 'vs/base/common/event'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IFileService } from 'vs/platform/files/common/files'; export interface IEditorPart { openEditor(input?: IEditorInput, options?: IEditorOptions | ITextEditorOptions, sideBySide?: boolean): TPromise; @@ -53,7 +54,8 @@ export class WorkbenchEditorService implements IWorkbenchEditorService { @IUntitledEditorService private untitledEditorService: IUntitledEditorService, @IWorkspaceContextService private workspaceContextService: IWorkspaceContextService, @IInstantiationService private instantiationService: IInstantiationService, - @IEnvironmentService private environmentService: IEnvironmentService + @IEnvironmentService private environmentService: IEnvironmentService, + @IFileService private fileService: IFileService ) { this.editorPart = editorPart; this.fileInputFactory = Registry.as(Extensions.Editors).getFileInputFactory(); @@ -273,7 +275,7 @@ export class WorkbenchEditorService implements IWorkbenchEditorService { } let input: ICachedEditorInput; - if (resource.scheme === network.Schemas.file) { + if (resource.scheme === network.Schemas.file || this.fileService.canHandleResource && this.fileService.canHandleResource(resource)) { input = this.fileInputFactory.createFileInput(resource, encoding, instantiationService); } else { input = instantiationService.createInstance(ResourceEditorInput, label, description, resource); @@ -319,14 +321,16 @@ export class DelegatingWorkbenchEditorService extends WorkbenchEditorService { @IInstantiationService instantiationService: IInstantiationService, @IWorkspaceContextService workspaceContextService: IWorkspaceContextService, @IWorkbenchEditorService editorService: IWorkbenchEditorService, - @IEnvironmentService environmentService: IEnvironmentService + @IEnvironmentService environmentService: IEnvironmentService, + @IFileService fileService: IFileService ) { super( editorService, untitledEditorService, workspaceContextService, instantiationService, - environmentService + environmentService, + fileService ); } @@ -359,4 +363,4 @@ export class DelegatingWorkbenchEditorService extends WorkbenchEditorService { return super.doCloseEditor(position, input); }); } -} \ No newline at end of file +} diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts index cee692ce0e6..d4b17a7fbc3 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts @@ -33,8 +33,9 @@ import { IWorkspaceConfigurationService } from 'vs/workbench/services/configurat import { ICrashReporterService } from 'vs/workbench/services/crashReporter/common/crashReporterService'; import { IBroadcastService, IBroadcast } from 'vs/platform/broadcast/electron-browser/broadcastService'; import { isEqual } from 'vs/base/common/paths'; -import { EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL, EXTENSION_RELOAD_BROADCAST_CHANNEL, ILogEntry, EXTENSION_ATTACH_BROADCAST_CHANNEL, EXTENSION_LOG_BROADCAST_CHANNEL, EXTENSION_TERMINATE_BROADCAST_CHANNEL } from 'vs/platform/extensions/common/extensionHost'; +import { EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL, EXTENSION_RELOAD_BROADCAST_CHANNEL, EXTENSION_ATTACH_BROADCAST_CHANNEL, EXTENSION_LOG_BROADCAST_CHANNEL, EXTENSION_TERMINATE_BROADCAST_CHANNEL } from 'vs/platform/extensions/common/extensionHost'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IRemoteConsoleLog, log, parse } from 'vs/base/node/console'; export class ExtensionHostProcessWorker { @@ -142,7 +143,8 @@ export class ExtensionHostProcessWorker { VSCODE_WINDOW_ID: String(this._windowService.getCurrentWindowId()), VSCODE_IPC_HOOK_EXTHOST: pipeName, VSCODE_HANDLES_UNCAUGHT_ERRORS: true, - ELECTRON_NO_ASAR: '1' + ELECTRON_NO_ASAR: '1', + VSCODE_LOG_STACK: !this._isExtensionDevTestFromCli && (this._isExtensionDevHost || !this._environmentService.isBuilt || product.quality !== 'stable' || this._environmentService.verbose) }), // We only detach the extension host on windows. Linux and Mac orphan by default // and detach under Linux and Mac create another process group. @@ -195,8 +197,8 @@ export class ExtensionHostProcessWorker { // Support logging from extension host this._extensionHostProcess.on('message', msg => { - if (msg && (msg).type === '__$console') { - this._logExtensionHostMessage(msg); + if (msg && (msg).type === '__$console') { + this._logExtensionHostMessage(msg); } }); @@ -365,33 +367,16 @@ export class ExtensionHostProcessWorker { }); } - private _logExtensionHostMessage(logEntry: ILogEntry) { - let args = []; - try { - let parsed = JSON.parse(logEntry.arguments); - args.push(...Object.getOwnPropertyNames(parsed).map(o => parsed[o])); - } catch (error) { - args.push(logEntry.arguments); - } - - // If the first argument is a string, check for % which indicates that the message - // uses substitution for variables. In this case, we cannot just inject our colored - // [Extension Host] to the front because it breaks substitution. - let consoleArgs = []; - if (typeof args[0] === 'string' && args[0].indexOf('%') >= 0) { - consoleArgs = [`%c[Extension Host]%c ${args[0]}`, 'color: blue', 'color: black', ...args.slice(1)]; - } else { - consoleArgs = ['%c[Extension Host]', 'color: blue', ...args]; - } + private _logExtensionHostMessage(entry: IRemoteConsoleLog) { // Send to local console unless we run tests from cli if (!this._isExtensionDevTestFromCli) { - console[logEntry.severity].apply(console, consoleArgs); + log(entry, 'Extension Host'); } // Log on main side if running tests from cli if (this._isExtensionDevTestFromCli) { - this._windowsService.log(logEntry.severity, ...args); + this._windowsService.log(entry.severity, ...parse(entry).args); } // Broadcast to other windows if we are in development mode @@ -399,7 +384,7 @@ export class ExtensionHostProcessWorker { this._broadcastService.broadcast({ channel: EXTENSION_LOG_BROADCAST_CHANNEL, payload: { - logEntry, + logEntry: entry, debugId: this._environmentService.debugExtensionHost.debugId } }); diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index cb3e2266cf6..e600bf32f97 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -32,6 +32,7 @@ import { ExtHostCustomersRegistry } from 'vs/workbench/api/electron-browser/extH import { IWindowService } from 'vs/platform/windows/common/windows'; import { Action } from 'vs/base/common/actions'; import { IDisposable } from 'vs/base/common/lifecycle'; +import { startTimer } from 'vs/base/node/startupTimers'; const SystemExtensionsRoot = path.normalize(path.join(URI.parse(require.toUrl('')).fsPath, '..', 'extensions')); @@ -318,7 +319,12 @@ export class ExtensionService implements IExtensionService { let messageHandler = (msg: IMessage) => this._handleExtensionPointMessage(msg); for (let i = 0, len = extensionPoints.length; i < len; i++) { - ExtensionService._handleExtensionPoint(extensionPoints[i], availableExtensions, messageHandler); + const clock = startTimer(`handleExtensionPoint:${extensionPoints[i].name}`); + try { + ExtensionService._handleExtensionPoint(extensionPoints[i], availableExtensions, messageHandler); + } finally { + clock.stop(); + } } this._barrier.open(); diff --git a/src/vs/workbench/services/files/electron-browser/fileService.ts b/src/vs/workbench/services/files/electron-browser/fileService.ts index 23644861e3e..d2c61424a26 100644 --- a/src/vs/workbench/services/files/electron-browser/fileService.ts +++ b/src/vs/workbench/services/files/electron-browser/fileService.ts @@ -11,17 +11,13 @@ import paths = require('vs/base/common/paths'); import encoding = require('vs/base/node/encoding'); import errors = require('vs/base/common/errors'); import uri from 'vs/base/common/uri'; -import { toResource } from 'vs/workbench/common/editor'; import { FileOperation, FileOperationEvent, IFileService, IFilesConfiguration, IResolveFileOptions, IFileStat, IResolveFileResult, IContent, IStreamContent, IImportResult, IResolveContentOptions, IUpdateContentOptions, FileChangesEvent, ICreateFileOptions } from 'vs/platform/files/common/files'; import { FileService as NodeFileService, IFileServiceOptions, IEncodingOverride } from 'vs/workbench/services/files/node/fileService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { Action } from 'vs/base/common/actions'; -import { ResourceMap } from 'vs/base/common/map'; -import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IMessageService, IMessageWithAction, Severity, CloseAction } from 'vs/platform/message/common/message'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import Event, { Emitter } from 'vs/base/common/event'; @@ -40,24 +36,20 @@ export class FileService implements IFileService { private raw: IFileService; private toUnbind: IDisposable[]; - private activeOutOfWorkspaceWatchers: ResourceMap; protected _onFileChanges: Emitter; - private _onAfterOperation: Emitter; + protected _onAfterOperation: Emitter; constructor( @IConfigurationService private configurationService: IConfigurationService, @IWorkspaceContextService private contextService: IWorkspaceContextService, - @IWorkbenchEditorService private editorService: IWorkbenchEditorService, @IEnvironmentService private environmentService: IEnvironmentService, - @IEditorGroupService private editorGroupService: IEditorGroupService, @ILifecycleService private lifecycleService: ILifecycleService, @IMessageService private messageService: IMessageService, @IStorageService private storageService: IStorageService, @ITextResourceConfigurationService textResourceConfigurationService: ITextResourceConfigurationService ) { this.toUnbind = []; - this.activeOutOfWorkspaceWatchers = new ResourceMap(); this._onFileChanges = new Emitter(); this.toUnbind.push(this._onFileChanges); @@ -129,9 +121,6 @@ export class FileService implements IFileService { // Config changes this.toUnbind.push(this.configurationService.onDidUpdateConfiguration(e => this.onConfigurationChange(this.configurationService.getConfiguration()))); - // Editor changing - this.toUnbind.push(this.editorGroupService.onEditorsChanged(() => this.onEditorsChanged())); - // Root changes this.toUnbind.push(this.contextService.onDidChangeWorkspaceFolders(() => this.onDidChangeWorkspaceFolders())); @@ -153,37 +142,6 @@ export class FileService implements IFileService { return encodingOverride; } - private onEditorsChanged(): void { - this.handleOutOfWorkspaceWatchers(); - } - - private handleOutOfWorkspaceWatchers(): void { - const visibleOutOfWorkspacePaths = new ResourceMap(); - this.editorService.getVisibleEditors().map(editor => { - return toResource(editor.input, { supportSideBySide: true, filter: 'file' }); - }).filter(fileResource => { - return !!fileResource && !this.contextService.isInsideWorkspace(fileResource); - }).forEach(resource => { - visibleOutOfWorkspacePaths.set(resource, resource); - }); - - // Handle no longer visible out of workspace resources - this.activeOutOfWorkspaceWatchers.forEach(resource => { - if (!visibleOutOfWorkspacePaths.get(resource)) { - this.unwatchFileChanges(resource); - this.activeOutOfWorkspaceWatchers.delete(resource); - } - }); - - // Handle newly visible out of workspace resources - visibleOutOfWorkspacePaths.forEach(resource => { - if (!this.activeOutOfWorkspaceWatchers.get(resource)) { - this.watchFileChanges(resource); - this.activeOutOfWorkspaceWatchers.set(resource, resource); - } - }); - } - private onConfigurationChange(configuration: IFilesConfiguration): void { this.updateOptions(configuration.files); } @@ -297,10 +255,6 @@ export class FileService implements IFileService { public dispose(): void { this.toUnbind = dispose(this.toUnbind); - // Dispose watchers if any - this.activeOutOfWorkspaceWatchers.forEach(resource => this.unwatchFileChanges(resource)); - this.activeOutOfWorkspaceWatchers.clear(); - // Dispose service this.raw.dispose(); } diff --git a/src/vs/workbench/services/files/electron-browser/remoteFileService.ts b/src/vs/workbench/services/files/electron-browser/remoteFileService.ts index 990dfba275b..cfcfe0e3913 100644 --- a/src/vs/workbench/services/files/electron-browser/remoteFileService.ts +++ b/src/vs/workbench/services/files/electron-browser/remoteFileService.ts @@ -6,32 +6,118 @@ import URI from 'vs/base/common/uri'; import { FileService } from 'vs/workbench/services/files/electron-browser/fileService'; -import { IContent, IStreamContent, IFileStat, IResolveContentOptions, IUpdateContentOptions, FileChangesEvent, FileChangeType } from 'vs/platform/files/common/files'; +import { IContent, IStreamContent, IFileStat, IResolveContentOptions, IUpdateContentOptions, IResolveFileOptions, IResolveFileResult, FileOperationEvent, FileOperation, IFileSystemProvider, IStat, FileType, IImportResult, FileChangesEvent, ICreateFileOptions, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files'; import { TPromise } from 'vs/base/common/winjs.base'; -import Event from 'vs/base/common/event'; -import { EventEmitter } from 'events'; -import { basename } from 'path'; +import { basename, join } from 'path'; import { IDisposable } from 'vs/base/common/lifecycle'; +import { groupBy, isFalsyOrEmpty, distinct } from 'vs/base/common/arrays'; +import { compare } from 'vs/base/common/strings'; +import { Schemas } from 'vs/base/common/network'; +import { Progress } from 'vs/platform/progress/common/progress'; +import { decodeStream, encode, UTF8, UTF8_with_bom } from 'vs/base/node/encoding'; +import { StringTrieMap } from 'vs/base/common/map'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { IMessageService } from 'vs/platform/message/common/message'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration'; +import { IExtensionService } from 'vs/platform/extensions/common/extensions'; +import { maxBufferLen, detectMimeAndEncodingFromBuffer } from 'vs/base/node/mime'; +import { MIME_BINARY } from 'vs/base/common/mime'; +import { localize } from 'vs/nls'; -export interface IRemoteFileSystemProvider { - onDidChange: Event; - resolve(resource: URI): TPromise; - update(resource: URI, content: string): TPromise; +function toIFileStat(provider: IFileSystemProvider, tuple: [URI, IStat], recurse?: (tuple: [URI, IStat]) => boolean): TPromise { + const [resource, stat] = tuple; + const fileStat: IFileStat = { + isDirectory: false, + hasChildren: false, + resource: resource, + name: basename(resource.path), + mtime: stat.mtime, + size: stat.size, + etag: stat.mtime.toString(29) + stat.size.toString(31), + }; + + if (stat.type === FileType.File) { + // done + return TPromise.as(fileStat); + + } else { + // dir -> resolve + return provider.readdir(resource).then(entries => { + fileStat.isDirectory = true; + fileStat.hasChildren = entries.length > 0; + + if (recurse && recurse([resource, stat])) { + // resolve children if requested + return TPromise.join(entries.map(stat => toIFileStat(provider, stat, recurse))).then(children => { + fileStat.children = children; + return fileStat; + }); + } else { + return fileStat; + } + }); + } +} + +export function toDeepIFileStat(provider: IFileSystemProvider, tuple: [URI, IStat], to: URI[]): TPromise { + + const trie = new StringTrieMap(); + trie.insert(tuple[0].toString(), true); + + if (!isFalsyOrEmpty(to)) { + to.forEach(uri => trie.insert(uri.toString(), true)); + } + + return toIFileStat(provider, tuple, candidate => { + const sub = trie.findSuperstr(candidate[0].toString()); + return !!sub; + }); } export class RemoteFileService extends FileService { - private readonly _provider = new Map(); + private readonly _provider = new Map(); + private _supportedSchemes: string[]; - registerProvider(authority: string, provider: IRemoteFileSystemProvider): IDisposable { + constructor( + @IExtensionService private readonly _extensionService: IExtensionService, + @IStorageService private readonly _storageService: IStorageService, + @IConfigurationService configurationService: IConfigurationService, + @IWorkspaceContextService contextService: IWorkspaceContextService, + @IEnvironmentService environmentService: IEnvironmentService, + @ILifecycleService lifecycleService: ILifecycleService, + @IMessageService messageService: IMessageService, + @ITextResourceConfigurationService textResourceConfigurationService: ITextResourceConfigurationService, + ) { + super( + configurationService, + contextService, + environmentService, + lifecycleService, + messageService, + _storageService, + textResourceConfigurationService, + ); + + this._supportedSchemes = JSON.parse(this._storageService.get('remote_schemes', undefined, '[]')); + } + + registerProvider(authority: string, provider: IFileSystemProvider): IDisposable { if (this._provider.has(authority)) { throw new Error(); } + this._supportedSchemes.push(authority); + this._storageService.store('remote_schemes', JSON.stringify(distinct(this._supportedSchemes))); + this._provider.set(authority, provider); - const reg = provider.onDidChange(e => { + const reg = provider.onDidChange(changes => { // forward change events - this._onFileChanges.fire(new FileChangesEvent([{ resource: e, type: FileChangeType.UPDATED }])); + this._onFileChanges.fire(new FileChangesEvent(changes)); }); return { dispose: () => { @@ -41,70 +127,378 @@ export class RemoteFileService extends FileService { }; } + canHandleResource(resource: URI): boolean { + return resource.scheme === Schemas.file + || this._provider.has(resource.scheme) + // TODO@remote + || this._supportedSchemes.indexOf(resource.scheme) >= 0; + } + + // --- stat + + private _withProvider(resource: URI): TPromise { + return this._extensionService.activateByEvent('onFileSystemAccess:' + resource.scheme).then(() => { + const provider = this._provider.get(resource.scheme); + if (!provider) { + const err = new Error(); + err.name = 'ENOPRO'; + err.message = `no provider for ${resource.toString()}`; + throw err; + } + return provider; + }); + } + + existsFile(resource: URI): TPromise { + if (resource.scheme === Schemas.file) { + return super.existsFile(resource); + } else { + return this.resolveFile(resource).then(data => true, err => false); + } + } + + resolveFile(resource: URI, options?: IResolveFileOptions): TPromise { + if (resource.scheme === Schemas.file) { + return super.resolveFile(resource, options); + } else { + return this._doResolveFiles([{ resource, options }]).then(data => { + if (data.length !== 1 || !data[0].success) { + throw new Error(`ENOENT, ${resource}`); + } else { + return data[0].stat; + } + }); + } + } + + resolveFiles(toResolve: { resource: URI; options?: IResolveFileOptions; }[]): TPromise { + const groups = groupBy(toResolve, (a, b) => compare(a.resource.scheme, b.resource.scheme)); + const promises: TPromise[] = []; + for (const group of groups) { + if (group[0].resource.scheme === Schemas.file) { + promises.push(super.resolveFiles(group)); + } else { + promises.push(this._doResolveFiles(group)); + } + } + return TPromise.join(promises).then(data => { + return [].concat(...data); + }); + } + + private _doResolveFiles(toResolve: { resource: URI; options?: IResolveFileOptions; }[]): TPromise { + return this._withProvider(toResolve[0].resource).then(provider => { + let result: IResolveFileResult[] = []; + let promises = toResolve.map((item, idx) => { + return provider.stat(item.resource).then(stat => { + return toDeepIFileStat(provider, [item.resource, stat], item.options && item.options.resolveTo).then(fileStat => { + result[idx] = { stat: fileStat, success: true }; + }); + }, err => { + result[idx] = { stat: undefined, success: false }; + }); + }); + return TPromise.join(promises).then(() => result); + }); + } + // --- resolve resolveContent(resource: URI, options?: IResolveContentOptions): TPromise { - if (this._provider.has(resource.authority)) { - return this._doResolveContent(resource); + if (resource.scheme === Schemas.file) { + return super.resolveContent(resource, options); + } else { + return this._doResolveContent(resource, options).then(RemoteFileService._asContent); } - - return super.resolveContent(resource, options); } resolveStreamContent(resource: URI, options?: IResolveContentOptions): TPromise { - if (this._provider.has(resource.authority)) { - return this._doResolveContent(resource).then(RemoteFileService._asStreamContent); + if (resource.scheme === Schemas.file) { + return super.resolveStreamContent(resource, options); + } else { + return this._doResolveContent(resource, options); } - - return super.resolveStreamContent(resource, options); } - private async _doResolveContent(resource: URI): TPromise { + private _doResolveContent(resource: URI, options: IResolveContentOptions = Object.create(null)): TPromise { + return this._withProvider(resource).then(provider => { - const stat = RemoteFileService._createFakeStat(resource); - const value = await this._provider.get(resource.authority).resolve(resource); - return { ...stat, value }; + return this.resolveFile(resource).then(fileStat => { + const guessEncoding = options.autoGuessEncoding; + const count = maxBufferLen(options); + const chunks: Buffer[] = []; + + return provider.read( + resource, + 0, count, + new Progress(chunk => chunks.push(chunk)) + ).then(bytesRead => { + // send to bla + return detectMimeAndEncodingFromBuffer({ bytesRead, buffer: Buffer.concat(chunks) }, guessEncoding); + + }).then(detected => { + if (options.acceptTextOnly && detected.mimes.indexOf(MIME_BINARY) >= 0) { + return TPromise.wrapError(new FileOperationError( + localize('fileBinaryError', "File seems to be binary and cannot be opened as text"), + FileOperationResult.FILE_IS_BINARY + )); + } + + let preferredEncoding: string; + if (options && options.encoding) { + if (detected.encoding === UTF8 && options.encoding === UTF8) { + preferredEncoding = UTF8_with_bom; // indicate the file has BOM if we are to resolve with UTF 8 + } else { + preferredEncoding = options.encoding; // give passed in encoding highest priority + } + } else if (detected.encoding) { + if (detected.encoding === UTF8) { + preferredEncoding = UTF8_with_bom; // if we detected UTF-8, it can only be because of a BOM + } else { + preferredEncoding = detected.encoding; + } + // todo@remote - encoding logic should not be kept + // hostage inside the node file service + // } else if (super.configuredEncoding(resource) === UTF8_with_bom) { + } else { + preferredEncoding = UTF8; // if we did not detect UTF 8 BOM before, this can only be UTF 8 then + } + + // const encoding = this.getEncoding(resource); + const stream = decodeStream(preferredEncoding); + + // start with what we have already read + // and have a new stream to read the rest + let offset = 0; + for (const chunk of chunks) { + stream.write(chunk); + offset += chunk.length; + } + provider.read(resource, offset, Number.MAX_VALUE, new Progress(chunk => stream.write(chunk))).then(() => { + stream.end(); + }, err => { + stream.emit('error', err); + stream.end(); + }); + + return { + encoding: preferredEncoding, + value: stream, + resource: fileStat.resource, + name: fileStat.name, + etag: fileStat.etag, + mtime: fileStat.mtime, + }; + }); + }); + }); } // --- saving + createFile(resource: URI, content?: string, options?: ICreateFileOptions): TPromise { + if (resource.scheme === Schemas.file) { + return super.createFile(resource, content, options); + } else { + return this._withProvider(resource).then(provider => { + let prepare = options && !options.overwrite + ? this.existsFile(resource) + : TPromise.as(false); + + + return prepare.then(exists => { + if (exists && options && !options.overwrite) { + return TPromise.wrapError(new FileOperationError('EEXIST', FileOperationResult.FILE_MODIFIED_SINCE)); + } + return this._doUpdateContent(provider, resource, content || '', {}); + }).then(fileStat => { + this._onAfterOperation.fire(new FileOperationEvent(resource, FileOperation.CREATE, fileStat)); + return fileStat; + }); + }); + } + } + updateContent(resource: URI, value: string, options?: IUpdateContentOptions): TPromise { - if (this._provider.has(resource.authority)) { - return this._doUpdateContent(resource, value).then(RemoteFileService._createFakeStat); + if (resource.scheme === Schemas.file) { + return super.updateContent(resource, value, options); + } else { + return this._withProvider(resource).then(provider => { + return this._doUpdateContent(provider, resource, value, options || {}); + }); + } + } + + private _doUpdateContent(provider: IFileSystemProvider, resource: URI, content: string, options: IUpdateContentOptions): TPromise { + const encoding = this.getEncoding(resource, options.encoding); + return provider.write(resource, encode(content, encoding)).then(() => { + return this.resolveFile(resource); + }); + } + + private static _asContent(content: IStreamContent): TPromise { + return new TPromise((resolve, reject) => { + let result: IContent = { + value: '', + encoding: content.encoding, + etag: content.etag, + mtime: content.mtime, + name: content.name, + resource: content.resource + }; + content.value.on('data', chunk => result.value += chunk); + content.value.on('error', reject); + content.value.on('end', () => resolve(result)); + }); + } + + // --- delete + + del(resource: URI, useTrash?: boolean): TPromise { + if (resource.scheme === Schemas.file) { + return super.del(resource, useTrash); + } else { + return this._withProvider(resource).then(provider => { + return provider.stat(resource).then(stat => { + return stat.type === FileType.Dir ? provider.rmdir(resource) : provider.unlink(resource); + }).then(() => { + this._onAfterOperation.fire(new FileOperationEvent(resource, FileOperation.DELETE)); + }); + }); + } + } + + createFolder(resource: URI): TPromise { + if (resource.scheme === Schemas.file) { + return super.createFolder(resource); + } else { + return this._withProvider(resource).then(provider => { + return provider.mkdir(resource).then(stat => { + return toIFileStat(provider, [resource, stat]); + }); + }).then(fileStat => { + this._onAfterOperation.fire(new FileOperationEvent(resource, FileOperation.CREATE, fileStat)); + return fileStat; + }); + } + } + + rename(resource: URI, newName: string): TPromise { + if (resource.scheme === Schemas.file) { + return super.rename(resource, newName); + } else { + const target = resource.with({ path: join(resource.path, '..', newName) }); + return this._doMoveWithInScheme(resource, target, false); + } + } + + moveFile(source: URI, target: URI, overwrite?: boolean): TPromise { + if (source.scheme !== target.scheme) { + return this._doMoveAcrossScheme(source, target); + } else if (source.scheme === Schemas.file) { + return super.moveFile(source, target, overwrite); + } else { + return this._doMoveWithInScheme(source, target, overwrite); + } + } + + private _doMoveWithInScheme(source: URI, target: URI, overwrite?: boolean): TPromise { + + const prepare = overwrite + ? this.del(target).then(undefined, err => { /*ignore*/ }) + : TPromise.as(null); + + return prepare.then(() => this._withProvider(source)).then(provider => { + return provider.move(source, target).then(stat => { + return toIFileStat(provider, [target, stat]); + }).then(fileStat => { + this._onAfterOperation.fire(new FileOperationEvent(source, FileOperation.MOVE, fileStat)); + return fileStat; + }); + }); + } + + private _doMoveAcrossScheme(source: URI, target: URI, overwrite?: boolean): TPromise { + return this.copyFile(source, target, overwrite).then(() => { + return this.del(source); + }).then(() => { + return this.resolveFile(target); + }).then(fileStat => { + this._onAfterOperation.fire(new FileOperationEvent(source, FileOperation.MOVE, fileStat)); + return fileStat; + }); + } + + importFile(source: URI, targetFolder: URI): TPromise { + if (source.scheme === targetFolder.scheme && source.scheme === Schemas.file) { + return super.importFile(source, targetFolder); + } else { + const target = targetFolder.with({ path: join(targetFolder.path, basename(source.path)) }); + return this.copyFile(source, target, false).then(stat => ({ stat, isNew: false })); + } + } + + copyFile(source: URI, target: URI, overwrite?: boolean): TPromise { + if (source.scheme === target.scheme && source.scheme === Schemas.file) { + return super.copyFile(source, target, overwrite); } - return super.updateContent(resource, value, options); + const prepare = overwrite + ? this.del(target).then(undefined, err => { /*ignore*/ }) + : TPromise.as(null); + + return prepare.then(() => { + // TODO@Joh This does only work for textfiles + // because the content turns things into a string + // and all binary data will be broken + return this.resolveContent(source).then(content => { + return this._withProvider(target).then(provider => { + return this._doUpdateContent(provider, target, content.value, { encoding: content.encoding }).then(fileStat => { + this._onAfterOperation.fire(new FileOperationEvent(source, FileOperation.COPY, fileStat)); + return fileStat; + }); + }, err => { + if (err instanceof Error && err.name === 'ENOPRO') { + // file scheme + return super.updateContent(target, content.value, { encoding: content.encoding }); + } else { + return TPromise.wrapError(err); + } + }); + }); + }); + } - private async _doUpdateContent(resource: URI, content: string): TPromise { - await this._provider.get(resource.authority).update(resource, content); - return resource; + touchFile(resource: URI): TPromise { + if (resource.scheme === Schemas.file) { + return super.touchFile(resource); + } else { + return this._doTouchFile(resource); + } } - // --- util - - private static _createFakeStat(resource: URI): IFileStat { - - return { - resource, - name: basename(resource.path), - encoding: 'utf8', - mtime: Date.now(), - etag: Date.now().toString(16), - isDirectory: false, - hasChildren: false - }; + private _doTouchFile(resource: URI): TPromise { + return this._withProvider(resource).then(provider => { + return provider.stat(resource).then(() => { + return provider.utimes(resource, Date.now(), Date.now()); + }, err => { + return provider.write(resource, new Uint8Array(0)); + }).then(() => { + return this.resolveFile(resource); + }); + }); } - private static _asStreamContent(content: IContent): IStreamContent { - const emitter = new EventEmitter(); - const { value } = content; - const result = content; - result.value = emitter; - setTimeout(() => { - emitter.emit('data', value); - emitter.emit('end'); - }, 0); - return result; + // TODO@Joh - file watching on demand! + public watchFileChanges(resource: URI): void { + if (resource.scheme === Schemas.file) { + super.watchFileChanges(resource); + } + } + public unwatchFileChanges(resource: URI): void { + if (resource.scheme === Schemas.file) { + super.unwatchFileChanges(resource); + } } } diff --git a/src/vs/workbench/services/files/test/node/fileService.test.ts b/src/vs/workbench/services/files/test/node/fileService.test.ts index d08e9e93cc4..6559d99dc1e 100644 --- a/src/vs/workbench/services/files/test/node/fileService.test.ts +++ b/src/vs/workbench/services/files/test/node/fileService.test.ts @@ -20,7 +20,7 @@ import encodingLib = require('vs/base/node/encoding'); import utils = require('vs/workbench/services/files/test/node/utils'); import { onError } from 'vs/base/test/common/utils'; import { TestContextService, TestTextResourceConfigurationService } from 'vs/workbench/test/workbenchTestServices'; -import { Workspace, WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { Workspace, toWorkspaceFolders } from 'vs/platform/workspace/common/workspace'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; suite('FileService', () => { @@ -38,7 +38,7 @@ suite('FileService', () => { return onError(error, done); } - service = new FileService(new TestContextService(new Workspace(testDir, testDir, [{ uri: uri.file(testDir), raw: { path: testDir }, index: 0, name: '' }])), new TestTextResourceConfigurationService(), new TestConfigurationService(), { disableWatcher: true }); + service = new FileService(new TestContextService(new Workspace(testDir, testDir, toWorkspaceFolders([{ path: testDir }]))), new TestTextResourceConfigurationService(), new TestConfigurationService(), { disableWatcher: true }); done(); }); }); @@ -784,7 +784,7 @@ suite('FileService', () => { const textResourceConfigurationService = new TestTextResourceConfigurationService(configurationService); - const _service = new FileService(new TestContextService(new Workspace(_testDir, _testDir, [aWorkspaceFolder(_testDir, 0)])), textResourceConfigurationService, configurationService, { + const _service = new FileService(new TestContextService(new Workspace(_testDir, _testDir, toWorkspaceFolders([{ path: _testDir }]))), textResourceConfigurationService, configurationService, { encodingOverride, disableWatcher: true }); @@ -811,7 +811,7 @@ suite('FileService', () => { const _sourceDir = require.toUrl('./fixtures/service'); const resource = uri.file(path.join(testDir, 'index.html')); - const _service = new FileService(new TestContextService(new Workspace(_testDir, _testDir, [aWorkspaceFolder(_testDir, 0)])), new TestTextResourceConfigurationService(), new TestConfigurationService(), { + const _service = new FileService(new TestContextService(new Workspace(_testDir, _testDir, toWorkspaceFolders([{ path: _testDir }]))), new TestTextResourceConfigurationService(), new TestConfigurationService(), { disableWatcher: true }); @@ -852,13 +852,4 @@ suite('FileService', () => { }); }); }); - - function aWorkspaceFolder(path: string, index: number, name: string = ''): WorkspaceFolder { - return { - uri: uri.file(path), - index, - raw: { path: path }, - name - }; - } }); diff --git a/src/vs/workbench/services/history/browser/history.ts b/src/vs/workbench/services/history/browser/history.ts index 2b8dedeb038..8e8e0c3efee 100644 --- a/src/vs/workbench/services/history/browser/history.ts +++ b/src/vs/workbench/services/history/browser/history.ts @@ -720,7 +720,10 @@ export class HistoryService extends BaseHistoryService implements IHistoryServic if (input instanceof EditorInput) { const factory = registry.getEditorInputFactory(input.getTypeId()); if (factory) { - return { editorInputJSON: { typeId: input.getTypeId(), deserialized: factory.serialize(input) } } as ISerializedEditorHistoryEntry; + const deserialized = factory.serialize(input); + if (deserialized) { + return { editorInputJSON: { typeId: input.getTypeId(), deserialized } } as ISerializedEditorHistoryEntry; + } } } @@ -754,10 +757,11 @@ export class HistoryService extends BaseHistoryService implements IHistoryServic } // Editor input: via factory - if (serializedEditorHistoryEntry.editorInputJSON) { - const factory = registry.getEditorInputFactory(serializedEditorHistoryEntry.editorInputJSON.typeId); + const { editorInputJSON } = serializedEditorHistoryEntry; + if (editorInputJSON && editorInputJSON.deserialized) { + const factory = registry.getEditorInputFactory(editorInputJSON.typeId); if (factory) { - return factory.deserialize(this.instantiationService, serializedEditorHistoryEntry.editorInputJSON.deserialized); + return factory.deserialize(this.instantiationService, editorInputJSON.deserialized); } } diff --git a/src/vs/workbench/services/keybinding/common/macLinuxKeyboardMapper.ts b/src/vs/workbench/services/keybinding/common/macLinuxKeyboardMapper.ts index 29dcb5a716d..2bb8da1d890 100644 --- a/src/vs/workbench/services/keybinding/common/macLinuxKeyboardMapper.ts +++ b/src/vs/workbench/services/keybinding/common/macLinuxKeyboardMapper.ts @@ -429,10 +429,6 @@ class ScanCodeKeyCodeMapper { export class MacLinuxKeyboardMapper implements IKeyboardMapper { - /** - * Is the keyboard type ISO (on Mac) - */ - private readonly _isISOKeyboard: boolean; /** * Is this the standard US keyboard layout? */ @@ -458,8 +454,7 @@ export class MacLinuxKeyboardMapper implements IKeyboardMapper { */ private readonly _scanCodeToDispatch: string[] = []; - constructor(isISOKeyboard: boolean, isUSStandard: boolean, rawMappings: IMacLinuxKeyboardMapping, OS: OperatingSystem) { - this._isISOKeyboard = isISOKeyboard; + constructor(isUSStandard: boolean, rawMappings: IMacLinuxKeyboardMapping, OS: OperatingSystem) { this._isUSStandard = isUSStandard; this._OS = OS; this._codeInfo = []; @@ -1055,21 +1050,6 @@ export class MacLinuxKeyboardMapper implements IKeyboardMapper { code = ScanCode.Enter; } - if (this._OS === OperatingSystem.Macintosh && this._isISOKeyboard) { - // See https://github.com/Microsoft/vscode/issues/24153 - // On OSX, on ISO keyboards, Chromium swaps the scan codes - // of IntlBackslash and Backquote. - - switch (code) { - case ScanCode.IntlBackslash: - code = ScanCode.Backquote; - break; - case ScanCode.Backquote: - code = ScanCode.IntlBackslash; - break; - } - } - const keyCode = keyboardEvent.keyCode; if ( diff --git a/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts b/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts index 4d7dfa3e616..0e599328bf5 100644 --- a/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts +++ b/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts @@ -40,7 +40,6 @@ import { onUnexpectedError } from 'vs/base/common/errors'; export class KeyboardMapperFactory { public static INSTANCE = new KeyboardMapperFactory(); - private _isISOKeyboard: boolean; private _layoutInfo: nativeKeymap.IKeyboardLayoutInfo; private _rawMapping: nativeKeymap.IKeyboardMapping; private _keyboardMapper: IKeyboardMapper; @@ -50,25 +49,21 @@ export class KeyboardMapperFactory { public onDidChangeKeyboardMapper: Event = this._onDidChangeKeyboardMapper.event; private constructor() { - this._isISOKeyboard = false; this._layoutInfo = null; this._rawMapping = null; this._keyboardMapper = null; this._initialized = false; } - public _onKeyboardLayoutChanged(isISOKeyboard: boolean): void { - isISOKeyboard = !!isISOKeyboard; + public _onKeyboardLayoutChanged(): void { if (this._initialized) { - this._setKeyboardData(isISOKeyboard, nativeKeymap.getCurrentKeyboardLayout(), nativeKeymap.getKeyMap()); - } else { - this._isISOKeyboard = isISOKeyboard; + this._setKeyboardData(nativeKeymap.getCurrentKeyboardLayout(), nativeKeymap.getKeyMap()); } } public getKeyboardMapper(dispatchConfig: DispatchConfig): IKeyboardMapper { if (!this._initialized) { - this._setKeyboardData(this._isISOKeyboard, nativeKeymap.getCurrentKeyboardLayout(), nativeKeymap.getKeyMap()); + this._setKeyboardData(nativeKeymap.getCurrentKeyboardLayout(), nativeKeymap.getKeyMap()); } if (dispatchConfig === DispatchConfig.KeyCode) { // Forcefully set to use keyCode @@ -79,7 +74,7 @@ export class KeyboardMapperFactory { public getCurrentKeyboardLayout(): nativeKeymap.IKeyboardLayoutInfo { if (!this._initialized) { - this._setKeyboardData(this._isISOKeyboard, nativeKeymap.getCurrentKeyboardLayout(), nativeKeymap.getKeyMap()); + this._setKeyboardData(nativeKeymap.getCurrentKeyboardLayout(), nativeKeymap.getKeyMap()); } return this._layoutInfo; } @@ -105,27 +100,26 @@ export class KeyboardMapperFactory { public getRawKeyboardMapping(): nativeKeymap.IKeyboardMapping { if (!this._initialized) { - this._setKeyboardData(this._isISOKeyboard, nativeKeymap.getCurrentKeyboardLayout(), nativeKeymap.getKeyMap()); + this._setKeyboardData(nativeKeymap.getCurrentKeyboardLayout(), nativeKeymap.getKeyMap()); } return this._rawMapping; } - private _setKeyboardData(isISOKeyboard: boolean, layoutInfo: nativeKeymap.IKeyboardLayoutInfo, rawMapping: nativeKeymap.IKeyboardMapping): void { + private _setKeyboardData(layoutInfo: nativeKeymap.IKeyboardLayoutInfo, rawMapping: nativeKeymap.IKeyboardMapping): void { this._layoutInfo = layoutInfo; - if (this._initialized && this._isISOKeyboard === isISOKeyboard && KeyboardMapperFactory._equals(this._rawMapping, rawMapping)) { + if (this._initialized && KeyboardMapperFactory._equals(this._rawMapping, rawMapping)) { // nothing to do... return; } this._initialized = true; - this._isISOKeyboard = isISOKeyboard; this._rawMapping = rawMapping; - this._keyboardMapper = KeyboardMapperFactory._createKeyboardMapper(this._isISOKeyboard, this._layoutInfo, this._rawMapping); + this._keyboardMapper = KeyboardMapperFactory._createKeyboardMapper(this._layoutInfo, this._rawMapping); this._onDidChangeKeyboardMapper.fire(); } - private static _createKeyboardMapper(isISOKeyboard: boolean, layoutInfo: nativeKeymap.IKeyboardLayoutInfo, rawMapping: nativeKeymap.IKeyboardMapping): IKeyboardMapper { + private static _createKeyboardMapper(layoutInfo: nativeKeymap.IKeyboardLayoutInfo, rawMapping: nativeKeymap.IKeyboardMapping): IKeyboardMapper { const isUSStandard = KeyboardMapperFactory._isUSStandard(layoutInfo); if (OS === OperatingSystem.Windows) { return new WindowsKeyboardMapper(isUSStandard, rawMapping); @@ -144,7 +138,7 @@ export class KeyboardMapperFactory { } } - return new MacLinuxKeyboardMapper(isISOKeyboard, isUSStandard, rawMapping, OS); + return new MacLinuxKeyboardMapper(isUSStandard, rawMapping, OS); } private static _equals(a: nativeKeymap.IKeyboardMapping, b: nativeKeymap.IKeyboardMapping): boolean { diff --git a/src/vs/workbench/services/keybinding/test/macLinuxKeyboardMapper.test.ts b/src/vs/workbench/services/keybinding/test/macLinuxKeyboardMapper.test.ts index aa5d104d8b7..bd0f29a1abe 100644 --- a/src/vs/workbench/services/keybinding/test/macLinuxKeyboardMapper.test.ts +++ b/src/vs/workbench/services/keybinding/test/macLinuxKeyboardMapper.test.ts @@ -19,7 +19,7 @@ const WRITE_FILE_IF_DIFFERENT = false; function createKeyboardMapper(isUSStandard: boolean, file: string, OS: OperatingSystem): TPromise { return readRawMapping(file).then((rawMappings) => { - return new MacLinuxKeyboardMapper(false, isUSStandard, rawMappings, OS); + return new MacLinuxKeyboardMapper(isUSStandard, rawMappings, OS); }); } @@ -1202,7 +1202,7 @@ suite('keyboardMapper - LINUX en_us', () => { suite('keyboardMapper', () => { test('issue #23706: Linux UK layout: Ctrl + Apostrophe also toggles terminal', () => { - let mapper = new MacLinuxKeyboardMapper(false, false, { + let mapper = new MacLinuxKeyboardMapper(false, { 'Backquote': { 'value': '`', 'withShift': '¬', @@ -1234,7 +1234,7 @@ suite('keyboardMapper', () => { }); test('issue #24064: NumLock/NumPad keys stopped working in 1.11 on Linux', () => { - let mapper = new MacLinuxKeyboardMapper(false, false, {}, OperatingSystem.Linux); + let mapper = new MacLinuxKeyboardMapper(false, {}, OperatingSystem.Linux); function assertNumpadKeyboardEvent(keyCode: KeyCode, code: string, label: string, electronAccelerator: string, userSettingsLabel: string, dispatch: string): void { assertResolveKeyboardEvent( @@ -1273,7 +1273,7 @@ suite('keyboardMapper', () => { }); test('issue #24107: Delete, Insert, Home, End, PgUp, PgDn, and arrow keys no longer work editor in 1.11', () => { - let mapper = new MacLinuxKeyboardMapper(false, false, {}, OperatingSystem.Linux); + let mapper = new MacLinuxKeyboardMapper(false, {}, OperatingSystem.Linux); function assertKeyboardEvent(keyCode: KeyCode, code: string, label: string, electronAccelerator: string, userSettingsLabel: string, dispatch: string): void { assertResolveKeyboardEvent( @@ -1322,66 +1322,6 @@ suite('keyboardMapper', () => { assertKeyboardEvent(KeyCode.DownArrow, 'NumpadEnter', 'DownArrow', 'Down', 'down', '[ArrowDown]'); assertKeyboardEvent(KeyCode.UpArrow, 'Lang3', 'UpArrow', 'Up', 'up', '[ArrowUp]'); }); - - test('issue #24153: ISO Keyboards: Backslash and IntlBackslash "swapped"', () => { - let mapper = new MacLinuxKeyboardMapper(true, false, { - 'Backquote': { - 'value': '`', - 'withShift': '~', - 'withAltGr': '`', - 'withShiftAltGr': '`' - }, - 'IntlBackslash': { - 'value': '§', - 'withShift': '°', - 'withAltGr': '§', - 'withShiftAltGr': '°' - } - }, OperatingSystem.Macintosh); - - assertResolveKeyboardEvent( - mapper, - { - ctrlKey: true, - shiftKey: false, - altKey: false, - metaKey: false, - keyCode: -1, - code: 'Backquote' - }, - { - label: '⌃§', - ariaLabel: 'Control+§', - electronAccelerator: null, - userSettingsLabel: 'ctrl+[IntlBackslash]', - isWYSIWYG: false, - isChord: false, - dispatchParts: ['ctrl+[IntlBackslash]', null], - } - ); - - assertResolveKeyboardEvent( - mapper, - { - ctrlKey: true, - shiftKey: false, - altKey: false, - metaKey: false, - keyCode: -1, - code: 'IntlBackslash' - }, - { - label: '⌃`', - ariaLabel: 'Control+`', - electronAccelerator: null, - userSettingsLabel: 'ctrl+`', - isWYSIWYG: true, - isChord: false, - dispatchParts: ['ctrl+[Backquote]', null], - } - ); - }); - }); suite('keyboardMapper - LINUX ru', () => { diff --git a/src/vs/workbench/services/keybinding/test/node/keybindingEditing.test.ts b/src/vs/workbench/services/keybinding/test/node/keybindingEditing.test.ts index de3d3e48d7e..9ee62bfdc44 100644 --- a/src/vs/workbench/services/keybinding/test/node/keybindingEditing.test.ts +++ b/src/vs/workbench/services/keybinding/test/node/keybindingEditing.test.ts @@ -9,7 +9,6 @@ import assert = require('assert'); import os = require('os'); import path = require('path'); import fs = require('fs'); -import uri from 'vs/base/common/uri'; import * as json from 'vs/base/common/json'; import { OS } from 'vs/base/common/platform'; import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding'; @@ -18,7 +17,7 @@ import { KeyCode, SimpleKeybinding, ChordKeybinding } from 'vs/base/common/keyCo import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import extfs = require('vs/base/node/extfs'); import { TestTextFileService, TestEditorGroupService, TestLifecycleService, TestBackupFileService, TestContextService, TestTextResourceConfigurationService } from 'vs/workbench/test/workbenchTestServices'; -import { IWorkspaceContextService, Workspace } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, Workspace, toWorkspaceFolders } from 'vs/platform/workspace/common/workspace'; import uuid = require('vs/base/common/uuid'); import { ConfigurationService } from 'vs/platform/configuration/node/configurationService'; import { FileService } from 'vs/workbench/services/files/node/fileService'; @@ -74,7 +73,7 @@ suite('Keybindings Editing', () => { instantiationService.stub(ITelemetryService, NullTelemetryService); instantiationService.stub(IModeService, ModeServiceImpl); instantiationService.stub(IModelService, instantiationService.createInstance(ModelServiceImpl)); - instantiationService.stub(IFileService, new FileService(new TestContextService(new Workspace(testDir, testDir, [{ uri: uri.file(testDir), raw: { path: testDir }, index: 0, name: '' }])), new TestTextResourceConfigurationService(), new TestConfigurationService(), { disableWatcher: true })); + instantiationService.stub(IFileService, new FileService(new TestContextService(new Workspace(testDir, testDir, toWorkspaceFolders([{ path: testDir }]))), new TestTextResourceConfigurationService(), new TestConfigurationService(), { disableWatcher: true })); instantiationService.stub(IUntitledEditorService, instantiationService.createInstance(UntitledEditorService)); instantiationService.stub(ITextFileService, instantiationService.createInstance(TestTextFileService)); instantiationService.stub(ITextModelService, instantiationService.createInstance(TextModelResolverService)); diff --git a/src/vs/workbench/services/scm/common/scm.ts b/src/vs/workbench/services/scm/common/scm.ts index 7147cff15d6..a9337cf69b8 100644 --- a/src/vs/workbench/services/scm/common/scm.ts +++ b/src/vs/workbench/services/scm/common/scm.ts @@ -88,7 +88,6 @@ export interface ISCMService { readonly _serviceBrand: any; readonly onDidAddRepository: Event; readonly onDidRemoveRepository: Event; - readonly onDidChangeRepository: Event; readonly repositories: ISCMRepository[]; diff --git a/src/vs/workbench/services/scm/common/scmService.ts b/src/vs/workbench/services/scm/common/scmService.ts index 6e429c117b1..1a582fa342a 100644 --- a/src/vs/workbench/services/scm/common/scmService.ts +++ b/src/vs/workbench/services/scm/common/scmService.ts @@ -62,9 +62,6 @@ export class SCMService implements ISCMService { private _onDidRemoveProvider = new Emitter(); get onDidRemoveRepository(): Event { return this._onDidRemoveProvider.event; } - private _onDidChangeProvider = new Emitter(); - get onDidChangeRepository(): Event { return this._onDidChangeProvider.event; } - constructor() { } registerSCMProvider(provider: ISCMProvider): ISCMRepository { diff --git a/src/vs/workbench/services/search/node/fileSearch.ts b/src/vs/workbench/services/search/node/fileSearch.ts index 5bf893c78ca..1ef4cf335bd 100644 --- a/src/vs/workbench/services/search/node/fileSearch.ts +++ b/src/vs/workbench/services/search/node/fileSearch.ts @@ -210,7 +210,11 @@ export class FileWalker { private cmdTraversal(folderQuery: IFolderSearch, onResult: (result: IRawFileMatch) => void, cb: (err?: Error) => void): void { const rootFolder = folderQuery.folder; const isMac = platform.isMacintosh; + let cmd: childProcess.ChildProcess; + const killCmd = () => cmd && cmd.kill(); + let done = (err?: Error) => { + process.removeListener('exit', killCmd); done = () => { }; cb(err); }; @@ -219,7 +223,6 @@ export class FileWalker { const tree = this.initDirectoryTree(); const useRipgrep = this.useRipgrep; - let cmd: childProcess.ChildProcess; let noSiblingsClauses: boolean; let filePatternSeen = false; if (useRipgrep) { @@ -230,6 +233,7 @@ export class FileWalker { cmd = this.spawnFindCmd(folderQuery); } + process.on('exit', killCmd); this.collectStdout(cmd, 'utf8', useRipgrep, (err: Error, stdout?: string, last?: boolean) => { if (err) { done(err); diff --git a/src/vs/workbench/services/search/node/ripgrepTextSearch.ts b/src/vs/workbench/services/search/node/ripgrepTextSearch.ts index 6750ffced0c..ead3ed5e53b 100644 --- a/src/vs/workbench/services/search/node/ripgrepTextSearch.ts +++ b/src/vs/workbench/services/search/node/ripgrepTextSearch.ts @@ -26,6 +26,7 @@ import { ISerializedFileMatch, ISerializedSearchComplete, IRawSearch, IFolderSea export class RipgrepEngine { private isDone = false; private rgProc: cp.ChildProcess; + private killRgProcFn: Function; private postProcessExclusions: glob.ParsedExpression; private ripgrepParser: RipgrepParser; @@ -33,6 +34,7 @@ export class RipgrepEngine { private resultsHandledP: TPromise = TPromise.wrap(null); constructor(private config: IRawSearch) { + this.killRgProcFn = () => this.rgProc && this.rgProc.kill(); } cancel(): void { @@ -44,6 +46,7 @@ export class RipgrepEngine { // TODO@Rob - make promise-based once the old search is gone, and I don't need them to have matching interfaces anymore search(onResult: (match: ISerializedFileMatch) => void, onMessage: (message: ISearchLog) => void, done: (error: Error, complete: ISerializedSearchComplete) => void): void { if (!this.config.folderQueries.length && !this.config.extraFiles.length) { + process.removeListener('exit', this.killRgProcFn); done(null, { limitHit: false, stats: null @@ -69,6 +72,7 @@ export class RipgrepEngine { } }); this.rgProc = cp.spawn(rgPath, rgArgs.globArgs, { cwd }); + process.once('exit', this.killRgProcFn); this.ripgrepParser = new RipgrepParser(this.config.maxResults, cwd); this.ripgrepParser.on('result', (match: ISerializedFileMatch) => { @@ -87,6 +91,7 @@ export class RipgrepEngine { }); this.ripgrepParser.on('hitLimit', () => { this.cancel(); + process.removeListener('exit', this.killRgProcFn); done(null, { limitHit: true, stats: null @@ -115,6 +120,7 @@ export class RipgrepEngine { if (!this.isDone) { this.isDone = true; let displayMsg: string; + process.removeListener('exit', this.killRgProcFn); if (stderr && !gotData && (displayMsg = this.rgErrorMsgForDisplay(stderr))) { done(new Error(displayMsg), { limitHit: false, diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index 579388facea..6fbdcf7a435 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -12,7 +12,7 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { guessMimeTypes } from 'vs/base/common/mime'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import URI from 'vs/base/common/uri'; -import * as assert from 'vs/base/common/assert'; +// import * as assert from 'vs/base/common/assert'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import paths = require('vs/base/common/paths'); import diagnostics = require('vs/base/common/diagnostics'); @@ -90,7 +90,8 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil ) { super(modelService, modeService); - assert.ok(resource.scheme === 'file', 'TextFileEditorModel can only handle file:// resources.'); + // TODO@remote + // assert.ok(resource.scheme === 'file', 'TextFileEditorModel can only handle file:// resources.'); this.resource = resource; this.toDispose = []; @@ -119,16 +120,18 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil this.toDispose.push(this.fileService.onFileChanges(e => this.onFileChanges(e))); this.toDispose.push(this.textFileService.onAutoSaveConfigurationChange(config => this.updateAutoSaveConfiguration(config))); this.toDispose.push(this.textFileService.onFilesAssociationChange(e => this.onFilesAssociationChange())); - this.toDispose.push(this.onDidStateChange(e => { - if (e === StateChange.REVERTED) { + this.toDispose.push(this.onDidStateChange(e => this.onStateChange(e))); + } - // Cancel any content change event promises as they are no longer valid. - this.contentChangeEventScheduler.cancel(); + private onStateChange(e: StateChange): void { + if (e === StateChange.REVERTED) { - // Refire state change reverted events as content change events - this._onDidContentChange.fire(StateChange.REVERTED); - } - })); + // Cancel any content change event promises as they are no longer valid. + this.contentChangeEventScheduler.cancel(); + + // Refire state change reverted events as content change events + this._onDidContentChange.fire(StateChange.REVERTED); + } } private onFileChanges(e: FileChangesEvent): void { @@ -449,10 +452,8 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil this.setDirty(false); } - // See https://github.com/Microsoft/vscode/issues/30189 - // This code has been extracted to a different method because it caused a memory leak - // where `value` was captured in the content change listener closure scope. - this._installChangeContentListener(); + // Model Listeners + this.installModelListeners(); return this; }, error => { @@ -465,13 +466,14 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil return this.createTextEditorModelPromise; } - private _installChangeContentListener(): void { + private installModelListeners(): void { + // See https://github.com/Microsoft/vscode/issues/30189 // This code has been extracted to a different method because it caused a memory leak // where `value` was captured in the content change listener closure scope. - this.toDispose.push(this.textEditorModel.onDidChangeContent(() => { - this.onModelContentChanged(); - })); + + // Content Change + this.toDispose.push(this.textEditorModel.onDidChangeContent(() => this.onModelContentChanged())); } private doLoadBackup(backup: URI): TPromise { diff --git a/src/vs/workbench/services/textfile/common/textFileService.ts b/src/vs/workbench/services/textfile/common/textFileService.ts index 33f5ee1e9d1..e754ef1bf38 100644 --- a/src/vs/workbench/services/textfile/common/textFileService.ts +++ b/src/vs/workbench/services/textfile/common/textFileService.ts @@ -407,10 +407,14 @@ export abstract class TextFileService implements ITextFileService { const filesToSave: URI[] = []; const untitledToSave: URI[] = []; toSave.forEach(s => { - if (s.scheme === Schemas.file) { - filesToSave.push(s); - } else if ((Array.isArray(arg1) || arg1 === true /* includeUntitled */) && s.scheme === UNTITLED_SCHEMA) { + // TODO@remote + // if (s.scheme === Schemas.file) { + // filesToSave.push(s); + // } else + if ((Array.isArray(arg1) || arg1 === true /* includeUntitled */) && s.scheme === UNTITLED_SCHEMA) { untitledToSave.push(s); + } else { + filesToSave.push(s); } }); @@ -712,4 +716,4 @@ export abstract class TextFileService implements ITextFileService { // Clear all caches this._models.clear(); } -} \ No newline at end of file +} diff --git a/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts b/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts index c7b7668b869..125ef301221 100644 --- a/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts +++ b/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts @@ -35,7 +35,10 @@ class ResourceModelCollection extends ReferenceCollection this.instantiationService.createInstance(ResourceEditorModel, resource)); } diff --git a/src/vs/workbench/services/workspace/node/workspaceEditingService.ts b/src/vs/workbench/services/workspace/node/workspaceEditingService.ts index 173f8e1d103..aa99ca9f75a 100644 --- a/src/vs/workbench/services/workspace/node/workspaceEditingService.ts +++ b/src/vs/workbench/services/workspace/node/workspaceEditingService.ts @@ -12,7 +12,7 @@ import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/ import { IWindowsService, IWindowService, IEnterWorkspaceResult } from 'vs/platform/windows/common/windows'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing'; -import { IWorkspacesService, IStoredWorkspaceFolder, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspacesService, IStoredWorkspaceFolder, IWorkspaceIdentifier, isStoredWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces'; import { dirname } from 'path'; import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { massageFolderPathForWorkspace } from 'vs/platform/workspaces/node/workspaces'; @@ -26,6 +26,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { IExtensionService } from 'vs/platform/extensions/common/extensions'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { BackupFileService } from 'vs/workbench/services/backup/node/backupFileService'; +import { Schemas } from 'vs/base/common/network'; export class WorkspaceEditingService implements IWorkspaceEditingService { @@ -33,7 +34,7 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { constructor( @IJSONEditingService private jsonEditingService: IJSONEditingService, - @IWorkspaceContextService private contextService: IWorkspaceContextService, + @IWorkspaceContextService private contextService: WorkspaceService, @IEnvironmentService private environmentService: IEnvironmentService, @IWindowsService private windowsService: IWindowsService, @IWindowService private windowService: IWindowService, @@ -58,14 +59,20 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { const workspaceConfigFolder = dirname(this.contextService.getWorkspace().configuration.fsPath); - foldersToAdd.forEach(foldersToAdd => { - if (this.contains(currentWorkspaceFolderUris, foldersToAdd)) { + foldersToAdd.forEach(folderToAdd => { + if (this.contains(currentWorkspaceFolderUris, folderToAdd)) { return; // already existing } - storedFoldersToAdd.push({ - path: massageFolderPathForWorkspace(foldersToAdd.fsPath, workspaceConfigFolder, currentStoredFolders) - }); + if (folderToAdd.scheme === Schemas.file) { + storedFoldersToAdd.push({ + path: massageFolderPathForWorkspace(folderToAdd.fsPath, workspaceConfigFolder, currentStoredFolders) + }); + } else { + storedFoldersToAdd.push({ + uri: folderToAdd.toString(true) + }); + } }); if (storedFoldersToAdd.length > 0) { @@ -84,7 +91,7 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { const currentStoredFolders = currentWorkspaceFolders.map(folder => folder.raw); const newStoredFolders: IStoredWorkspaceFolder[] = currentStoredFolders.filter((folder, index) => { - if (!folder.path) { + if (!isStoredWorkspaceFolder(folder)) { return true; // keep entries which are unrelated } @@ -99,15 +106,9 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { } private doSetFolders(folders: IStoredWorkspaceFolder[]): TPromise { - if (folders.length) { - const workspace = this.contextService.getWorkspace(); + const workspace = this.contextService.getWorkspace(); - return this.jsonEditingService.write(workspace.configuration, { key: 'folders', value: folders }, true); - } else { - // TODO: Sandeep - Removing all folders? - } - - return TPromise.as(void 0); + return this.jsonEditingService.write(workspace.configuration, { key: 'folders', value: folders }, true); } private isSupported(): boolean { @@ -185,11 +186,11 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { const configurationProperties = Registry.as(ConfigurationExtensions.Configuration).getConfigurationProperties(); const targetWorkspaceConfiguration = {}; for (const key of this.workspaceConfigurationService.keys().workspace) { - if (configurationProperties[key] && configurationProperties[key].scope === ConfigurationScope.WINDOW) { + if (configurationProperties[key] && !configurationProperties[key].isFromExtensions && configurationProperties[key].scope === ConfigurationScope.WINDOW) { targetWorkspaceConfiguration[key] = this.workspaceConfigurationService.lookup(key).workspace; } } return this.jsonEditingService.write(URI.file(toWorkspace.configPath), { key: 'settings', value: targetWorkspaceConfiguration }, true); } -} \ No newline at end of file +} diff --git a/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts b/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts index 20792c4a191..a935f3c9eed 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts @@ -15,8 +15,7 @@ import { ConfigurationTarget, ConfigurationEditingErrorCode, ConfigurationEditin import { ConfigurationModel } from 'vs/platform/configuration/common/configuration'; import { TestThreadService } from './testThreadService'; import { mock } from 'vs/workbench/test/electron-browser/api/mock'; -import { WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; -import { IStoredWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspaceFolder, WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; suite('ExtHostConfiguration', function () { @@ -133,7 +132,7 @@ suite('ExtHostConfiguration', function () { new class extends mock() { }, new ExtHostWorkspace(new TestThreadService(), { 'id': 'foo', - 'folders': [aWorkspaceFolder({ path: 'foo' }, 0)], + 'folders': [aWorkspaceFolder(URI.file('foo'), 0)], 'name': 'foo' }), { @@ -205,7 +204,7 @@ suite('ExtHostConfiguration', function () { new class extends mock() { }, new ExtHostWorkspace(new TestThreadService(), { 'id': 'foo', - 'folders': [aWorkspaceFolder({ path: firstRoot.path }, 0), aWorkspaceFolder({ path: secondRoot.path }, 1)], + 'folders': [aWorkspaceFolder(firstRoot, 0), aWorkspaceFolder(secondRoot, 1)], 'name': 'foo' }), { @@ -405,12 +404,7 @@ suite('ExtHostConfiguration', function () { .then(() => assert.ok(false), err => { /* expecting rejection */ }); }); - function aWorkspaceFolder(raw: IStoredWorkspaceFolder, index: number, name: string = ''): WorkspaceFolder { - return { - uri: URI.file(raw.path), - index, - raw, - name - }; + function aWorkspaceFolder(uri: URI, index: number, name: string = ''): IWorkspaceFolder { + return new WorkspaceFolder({ uri, name, index }); } }); diff --git a/src/vs/workbench/test/electron-browser/api/extHostWorkspace.test.ts b/src/vs/workbench/test/electron-browser/api/extHostWorkspace.test.ts index fcf619a2309..d9874115a7c 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostWorkspace.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostWorkspace.test.ts @@ -11,7 +11,7 @@ import { basename } from 'path'; import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace'; import { TestThreadService } from './testThreadService'; import { normalize } from 'vs/base/common/paths'; -import { WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceFolderData } from 'vs/platform/workspace/common/workspace'; suite('ExtHostWorkspace', function () { @@ -26,7 +26,7 @@ suite('ExtHostWorkspace', function () { test('asRelativePath', function () { - const ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', folders: [aWorkspaceFolder(URI.file('/Coding/Applications/NewsWoWBot'), 0)], name: 'Test' }); + const ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', folders: [aWorkspaceFolderData(URI.file('/Coding/Applications/NewsWoWBot'), 0)], name: 'Test' }); assertAsRelativePath(ws, '/Coding/Applications/NewsWoWBot/bernd/das/brot', 'bernd/das/brot'); assertAsRelativePath(ws, '/Apps/DartPubCache/hosted/pub.dartlang.org/convert-2.0.1/lib/src/hex.dart', @@ -40,7 +40,7 @@ suite('ExtHostWorkspace', function () { test('asRelativePath, same paths, #11402', function () { const root = '/home/aeschli/workspaces/samples/docker'; const input = '/home/aeschli/workspaces/samples/docker'; - const ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', folders: [aWorkspaceFolder(URI.file(root), 0)], name: 'Test' }); + const ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', folders: [aWorkspaceFolderData(URI.file(root), 0)], name: 'Test' }); assertAsRelativePath(ws, (input), input); @@ -55,14 +55,14 @@ suite('ExtHostWorkspace', function () { }); test('asRelativePath, multiple folders', function () { - const ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', folders: [aWorkspaceFolder(URI.file('/Coding/One'), 0), aWorkspaceFolder(URI.file('/Coding/Two'), 1)], name: 'Test' }); + const ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', folders: [aWorkspaceFolderData(URI.file('/Coding/One'), 0), aWorkspaceFolderData(URI.file('/Coding/Two'), 1)], name: 'Test' }); assertAsRelativePath(ws, '/Coding/One/file.txt', 'One/file.txt'); assertAsRelativePath(ws, '/Coding/Two/files/out.txt', 'Two/files/out.txt'); assertAsRelativePath(ws, '/Coding/Two2/files/out.txt', '/Coding/Two2/files/out.txt'); }); test('slightly inconsistent behaviour of asRelativePath and getWorkspaceFolder, #31553', function () { - const mrws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', folders: [aWorkspaceFolder(URI.file('/Coding/One'), 0), aWorkspaceFolder(URI.file('/Coding/Two'), 1)], name: 'Test' }); + const mrws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', folders: [aWorkspaceFolderData(URI.file('/Coding/One'), 0), aWorkspaceFolderData(URI.file('/Coding/Two'), 1)], name: 'Test' }); assertAsRelativePath(mrws, '/Coding/One/file.txt', 'One/file.txt'); assertAsRelativePath(mrws, '/Coding/One/file.txt', 'One/file.txt', true); @@ -74,7 +74,7 @@ suite('ExtHostWorkspace', function () { assertAsRelativePath(mrws, '/Coding/Two2/files/out.txt', '/Coding/Two2/files/out.txt', true); assertAsRelativePath(mrws, '/Coding/Two2/files/out.txt', '/Coding/Two2/files/out.txt', false); - const srws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', folders: [aWorkspaceFolder(URI.file('/Coding/One'), 0)], name: 'Test' }); + const srws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', folders: [aWorkspaceFolderData(URI.file('/Coding/One'), 0)], name: 'Test' }); assertAsRelativePath(srws, '/Coding/One/file.txt', 'file.txt'); assertAsRelativePath(srws, '/Coding/One/file.txt', 'file.txt', false); assertAsRelativePath(srws, '/Coding/One/file.txt', 'One/file.txt', true); @@ -93,15 +93,15 @@ suite('ExtHostWorkspace', function () { ws = new ExtHostWorkspace(new TestThreadService(), undefined); assert.equal(ws.getPath(), undefined); - ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', name: 'Test', folders: [aWorkspaceFolder(URI.file('Folder'), 0), aWorkspaceFolder(URI.file('Another/Folder'), 1)] }); + ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', name: 'Test', folders: [aWorkspaceFolderData(URI.file('Folder'), 0), aWorkspaceFolderData(URI.file('Another/Folder'), 1)] }); assert.equal(ws.getPath().replace(/\\/g, '/'), '/Folder'); - ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', name: 'Test', folders: [aWorkspaceFolder(URI.file('/Folder'), 0)] }); + ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', name: 'Test', folders: [aWorkspaceFolderData(URI.file('/Folder'), 0)] }); assert.equal(ws.getPath().replace(/\\/g, '/'), '/Folder'); }); test('WorkspaceFolder has name and index', function () { - const ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', folders: [aWorkspaceFolder(URI.file('/Coding/One'), 0), aWorkspaceFolder(URI.file('/Coding/Two'), 1)], name: 'Test' }); + const ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', folders: [aWorkspaceFolderData(URI.file('/Coding/One'), 0), aWorkspaceFolderData(URI.file('/Coding/Two'), 1)], name: 'Test' }); const [one, two] = ws.getWorkspaceFolders(); @@ -112,7 +112,7 @@ suite('ExtHostWorkspace', function () { }); test('getContainingWorkspaceFolder', function () { - const ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', name: 'Test', folders: [aWorkspaceFolder(URI.file('/Coding/One'), 0), aWorkspaceFolder(URI.file('/Coding/Two'), 1), aWorkspaceFolder(URI.file('/Coding/Two/Nested'), 2)] }); + const ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', name: 'Test', folders: [aWorkspaceFolderData(URI.file('/Coding/One'), 0), aWorkspaceFolderData(URI.file('/Coding/Two'), 1), aWorkspaceFolderData(URI.file('/Coding/Two/Nested'), 2)] }); let folder = ws.getWorkspaceFolder(URI.file('/foo/bar')); assert.equal(folder, undefined); @@ -157,7 +157,7 @@ suite('ExtHostWorkspace', function () { assert.equal(e.added.length, 1); assert.equal(e.added[0].uri.toString(), 'foo:bar'); }); - ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', folders: [aWorkspaceFolder(URI.parse('foo:bar'), 0)] }); + ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', folders: [aWorkspaceFolderData(URI.parse('foo:bar'), 0)] }); sub.dispose(); sub = ws.onDidChangeWorkspace(e => { @@ -165,7 +165,7 @@ suite('ExtHostWorkspace', function () { assert.equal(e.added.length, 1); assert.equal(e.added[0].uri.toString(), 'foo:bar2'); }); - ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', folders: [aWorkspaceFolder(URI.parse('foo:bar'), 0), aWorkspaceFolder(URI.parse('foo:bar2'), 1)] }); + ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', folders: [aWorkspaceFolderData(URI.parse('foo:bar'), 0), aWorkspaceFolderData(URI.parse('foo:bar2'), 1)] }); sub.dispose(); sub = ws.onDidChangeWorkspace(e => { @@ -176,7 +176,7 @@ suite('ExtHostWorkspace', function () { assert.equal(e.added.length, 1); assert.equal(e.added[0].uri.toString(), 'foo:bar3'); }); - ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', folders: [aWorkspaceFolder(URI.parse('foo:bar3'), 0)] }); + ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', folders: [aWorkspaceFolderData(URI.parse('foo:bar3'), 0)] }); sub.dispose(); }); @@ -195,13 +195,10 @@ suite('ExtHostWorkspace', function () { sub.dispose(); }); - function aWorkspaceFolder(uri: URI, index: number, name: string = ''): WorkspaceFolder { + function aWorkspaceFolderData(uri: URI, index: number, name: string = ''): IWorkspaceFolderData { return { uri, index, - raw: { - path: uri.toString() - }, name: name || basename(uri.path) }; } diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index 1502ef8fe6e..ada172ca08d 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -27,7 +27,7 @@ import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { IEditorInput, IEditorOptions, Position, Direction, IEditor, IResourceInput, ITextEditorSelection } from 'vs/platform/editor/common/editor'; import { IUntitledEditorService, UntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; import { IMessageService, IConfirmation } from 'vs/platform/message/common/message'; -import { IWorkspaceContextService, IWorkspace as IWorkbenchWorkspace, WorkbenchState, WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, IWorkspace as IWorkbenchWorkspace, WorkbenchState, IWorkspaceFolder, IWorkspaceFoldersChangeEvent } from 'vs/platform/workspace/common/workspace'; import { ILifecycleService, ShutdownEvent, ShutdownReason, StartupKind, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { EditorStacksModel } from 'vs/workbench/common/editor/editorStacksModel'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; @@ -74,14 +74,14 @@ export class TestContextService implements IWorkspaceContextService { private options: any; private _onDidChangeWorkspaceName: Emitter; - private _onDidChangeWorkspaceFolders: Emitter; + private _onDidChangeWorkspaceFolders: Emitter; private _onDidChangeWorkbenchState: Emitter; constructor(workspace: any = TestWorkspace, options: any = null) { this.workspace = workspace; this.id = generateUuid(); this.options = options || Object.create(null); - this._onDidChangeWorkspaceFolders = new Emitter(); + this._onDidChangeWorkspaceFolders = new Emitter(); this._onDidChangeWorkbenchState = new Emitter(); } @@ -89,7 +89,7 @@ export class TestContextService implements IWorkspaceContextService { return this._onDidChangeWorkspaceName.event; } - public get onDidChangeWorkspaceFolders(): Event { + public get onDidChangeWorkspaceFolders(): Event { return this._onDidChangeWorkspaceFolders.event; } @@ -97,7 +97,7 @@ export class TestContextService implements IWorkspaceContextService { return this._onDidChangeWorkbenchState.event; } - public getFolders(): WorkspaceFolder[] { + public getFolders(): IWorkspaceFolder[] { return this.workspace ? this.workspace.folders : []; } @@ -115,7 +115,7 @@ export class TestContextService implements IWorkspaceContextService { return this.workspace; } - public getWorkspaceFolder(resource: URI): WorkspaceFolder { + public getWorkspaceFolder(resource: URI): IWorkspaceFolder { return this.isInsideWorkspace(resource) ? this.workspace.folders[0] : null; } diff --git a/src/vs/workbench/workbench.main.ts b/src/vs/workbench/workbench.main.ts index d5c4c9ebf03..b162fb6a023 100644 --- a/src/vs/workbench/workbench.main.ts +++ b/src/vs/workbench/workbench.main.ts @@ -27,7 +27,6 @@ import 'vs/workbench/browser/actions/toggleEditorLayout'; import 'vs/workbench/browser/actions/toggleZenMode'; import 'vs/workbench/parts/preferences/browser/preferences.contribution'; import 'vs/workbench/parts/preferences/browser/keybindingsEditorContribution'; -import 'vs/workbench/browser/actions/configureLocale'; import 'vs/workbench/browser/parts/quickopen/quickopen.contribution'; import 'vs/workbench/parts/quickopen/browser/quickopen.contribution'; @@ -73,6 +72,7 @@ import 'vs/workbench/parts/terminal/browser/terminalQuickOpen'; import 'vs/workbench/parts/terminal/electron-browser/terminalPanel'; // can be packaged separately import 'vs/workbench/electron-browser/workbench'; +import 'vs/workbench/electron-browser/configureLocale'; import 'vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution'; diff --git a/test/smoke/package.json b/test/smoke/package.json index 99c529813c7..95b3ba9dc95 100644 --- a/test/smoke/package.json +++ b/test/smoke/package.json @@ -24,7 +24,6 @@ "spectron": "~3.6.4", "strip-json-comments": "^2.0.1", "tmp": "0.0.33", - "typescript": "^2.2.2", - "vscode-uri": "^1.0.1" + "typescript": "^2.2.2" } -} +} \ No newline at end of file diff --git a/test/smoke/src/areas/css/css.test.ts b/test/smoke/src/areas/css/css.test.ts index 3d249c5adc1..c13b2afdd83 100644 --- a/test/smoke/src/areas/css/css.test.ts +++ b/test/smoke/src/areas/css/css.test.ts @@ -23,7 +23,6 @@ describe('CSS', () => { it('verifies warnings for the empty rule', async () => { await app.workbench.quickopen.openFile('style.css'); - await app.client.waitForElement(`.monaco-editor.focused`); await app.client.type('.foo{}'); let warning = await app.client.waitForElement(Problems.getSelectorInEditor(ProblemSeverity.WARNING)); diff --git a/test/smoke/src/areas/debug/debug.test.ts b/test/smoke/src/areas/debug/debug.test.ts index a6fcf6c9bb2..fcd2648fe98 100644 --- a/test/smoke/src/areas/debug/debug.test.ts +++ b/test/smoke/src/areas/debug/debug.test.ts @@ -9,7 +9,7 @@ import * as os from 'os'; import * as path from 'path'; import * as fs from 'fs'; import * as stripJsonComments from 'strip-json-comments'; -import { SpectronApplication, VSCODE_BUILD, EXTENSIONS_DIR, findFreePort } from '../../spectron/application'; +import { SpectronApplication, VSCODE_BUILD, EXTENSIONS_DIR, findFreePort, WORKSPACE_PATH } from '../../spectron/application'; describe('Debug', () => { let app: SpectronApplication = new SpectronApplication(); @@ -48,16 +48,22 @@ describe('Debug', () => { await app.workbench.debug.openDebugViewlet(); await app.workbench.openFile('app.js'); await app.workbench.debug.configure(); - await app.screenCapturer.capture('launch.json file'); - const content = await app.workbench.editor.getEditorVisibleText(); - const json = JSON.parse(stripJsonComments(content)); - assert.equal(json.configurations[0].request, 'launch'); - assert.equal(json.configurations[0].type, 'node'); + const launchJsonPath = path.join(WORKSPACE_PATH, '.vscode', 'launch.json'); + const content = fs.readFileSync(launchJsonPath, 'utf8'); + const config = JSON.parse(stripJsonComments(content)); + config.configurations[0].protocol = 'inspector'; + fs.writeFileSync(launchJsonPath, JSON.stringify(config, undefined, 4), 'utf8'); + + await app.workbench.editor.waitForEditorContents('launch.json', contents => /"protocol": "inspector"/.test(contents)); + await app.screenCapturer.capture('launch.json file'); + + assert.equal(config.configurations[0].request, 'launch'); + assert.equal(config.configurations[0].type, 'node'); if (process.platform === 'win32') { - assert.equal(json.configurations[0].program, '${workspaceFolder}\\bin\\www'); + assert.equal(config.configurations[0].program, '${workspaceFolder}\\bin\\www'); } else { - assert.equal(json.configurations[0].program, '${workspaceFolder}/bin/www'); + assert.equal(config.configurations[0].program, '${workspaceFolder}/bin/www'); } }); @@ -74,7 +80,7 @@ describe('Debug', () => { await new Promise((c, e) => { const request = http.get(`http://localhost:${port}`); request.on('error', e); - app.workbench.debug.waitForStackFrame(sf => sf.name === 'index.js' && sf.lineNumber === 6).then(c, e); + app.workbench.debug.waitForStackFrame(sf => sf.name === 'index.js' && sf.lineNumber === 6, 'looking for index.js and line 6').then(c, e); }); await app.screenCapturer.capture('debugging is paused'); @@ -83,13 +89,13 @@ describe('Debug', () => { it('focus stack frames and variables', async function () { await app.client.waitFor(() => app.workbench.debug.getLocalVariableCount(), c => c === 4, 'there should be 4 local variables'); - await app.workbench.debug.focusStackFrame('layer.js'); + await app.workbench.debug.focusStackFrame('layer.js', 'looking for layer.js'); await app.client.waitFor(() => app.workbench.debug.getLocalVariableCount(), c => c === 5, 'there should be 5 local variables'); - await app.workbench.debug.focusStackFrame('route.js'); + await app.workbench.debug.focusStackFrame('route.js', 'looking for route.js'); await app.client.waitFor(() => app.workbench.debug.getLocalVariableCount(), c => c === 3, 'there should be 3 local variables'); - await app.workbench.debug.focusStackFrame('index.js'); + await app.workbench.debug.focusStackFrame('index.js', 'looking for index.js'); await app.client.waitFor(() => app.workbench.debug.getLocalVariableCount(), c => c === 4, 'there should be 4 local variables'); }); @@ -97,15 +103,15 @@ describe('Debug', () => { await app.workbench.debug.stepIn(); await app.screenCapturer.capture('debugging has stepped in'); - const first = await app.workbench.debug.waitForStackFrame(sf => sf.name === 'response.js'); + const first = await app.workbench.debug.waitForStackFrame(sf => sf.name === 'response.js', 'looking for response.js'); await app.workbench.debug.stepOver(); await app.screenCapturer.capture('debugging has stepped over'); - await app.workbench.debug.waitForStackFrame(sf => sf.name === 'response.js' && sf.lineNumber === first.lineNumber + 1); + await app.workbench.debug.waitForStackFrame(sf => sf.name === 'response.js' && sf.lineNumber === first.lineNumber + 1, `looking for response.js and line ${first.lineNumber + 1}`); await app.workbench.debug.stepOut(); await app.screenCapturer.capture('debugging has stepped out'); - await app.workbench.debug.waitForStackFrame(sf => sf.name === 'index.js' && sf.lineNumber === 7); + await app.workbench.debug.waitForStackFrame(sf => sf.name === 'index.js' && sf.lineNumber === 7, `looking for index.js and line 7`); }); it('continue', async function () { @@ -115,7 +121,7 @@ describe('Debug', () => { await new Promise((c, e) => { const request = http.get(`http://localhost:${port}`); request.on('error', e); - app.workbench.debug.waitForStackFrame(sf => sf.name === 'index.js' && sf.lineNumber === 6).then(c, e); + app.workbench.debug.waitForStackFrame(sf => sf.name === 'index.js' && sf.lineNumber === 6, `looking for index.js and line 6`).then(c, e); }); await app.screenCapturer.capture('debugging is paused'); diff --git a/test/smoke/src/areas/debug/debug.ts b/test/smoke/src/areas/debug/debug.ts index e16e3a51bc5..ae2770f5260 100644 --- a/test/smoke/src/areas/debug/debug.ts +++ b/test/smoke/src/areas/debug/debug.ts @@ -26,7 +26,7 @@ const VARIABLE = `${VIEWLET} .debug-variables .monaco-tree-row .expression`; const CONSOLE_OUTPUT = `.repl .output.expression`; const CONSOLE_INPUT_OUTPUT = `.repl .input-output-pair .output.expression .value`; -const REPL_FOCUSED = '.repl-input-wrapper .monaco-editor.focused'; +const REPL_FOCUSED = '.repl-input-wrapper .monaco-editor textarea'; export interface IStackFrame { id: string; @@ -94,26 +94,26 @@ export class Debug extends Viewlet { await this.spectron.client.waitForElement(NOT_DEBUG_STATUS_BAR); } - async waitForStackFrame(func: (stackFrame: IStackFrame) => boolean): Promise { + async waitForStackFrame(func: (stackFrame: IStackFrame) => boolean, message: string): Promise { return await this.spectron.client.waitFor(async () => { const stackFrames = await this.getStackFrames(); return stackFrames.filter(func)[0]; - }, void 0, 'Waiting for Stack Frame'); + }, void 0, `Waiting for Stack Frame: ${message}`); } async waitForStackFrameLength(length: number): Promise { return await this.spectron.client.waitFor(() => this.getStackFrames(), stackFrames => stackFrames.length === length); } - async focusStackFrame(name: string): Promise { - const stackFrame = await this.waitForStackFrame(sf => sf.name === name); + async focusStackFrame(name: string, message: string): Promise { + const stackFrame = await this.waitForStackFrame(sf => sf.name === name, message); await this.spectron.client.spectron.client.elementIdClick(stackFrame.id); await this.spectron.workbench.waitForTab(name); } async waitForReplCommand(text: string, accept: (result: string) => boolean): Promise { await this.spectron.workbench.quickopen.runCommand('Debug: Focus Debug Console'); - await this.spectron.client.waitForElement(REPL_FOCUSED); + await this.spectron.client.waitForActiveElement(REPL_FOCUSED); await this.spectron.client.type(text); await this.spectron.client.waitForElement(CONSOLE_INPUT_OUTPUT); await this.spectron.client.waitFor(async () => { diff --git a/test/smoke/src/areas/editor/editor.ts b/test/smoke/src/areas/editor/editor.ts index e37d916cedf..ab80a87f55d 100644 --- a/test/smoke/src/areas/editor/editor.ts +++ b/test/smoke/src/areas/editor/editor.ts @@ -18,15 +18,6 @@ export class Editor { constructor(private spectron: SpectronApplication) { } - public async getEditorFirstLineText(): Promise { - const result = await this.spectron.client.waitForText('.monaco-editor.focused .view-lines span span:nth-child(1)'); - return Array.isArray(result) ? result.join() : result; - } - - public async getEditorVisibleText(): Promise { - return await this.spectron.client.getText('.view-lines'); - } - public async openOutline(): Promise { const outline = new QuickOutline(this.spectron); await outline.open(); @@ -102,11 +93,44 @@ export class Editor { await this.spectron.client.waitAndClick(selector); } - public async getFocusedEditorUri(): Promise { - return this.spectron.webclient.selectorExecute(`.editor-container .monaco-editor.focused`, (elements: HTMLElement[]) => { - elements = Array.isArray(elements) ? elements : [elements]; - return elements[0].getAttribute('data-uri'); - }); + public async waitForEditorContents(filename: string, accept: (contents: string) => boolean): Promise { + const selector = `.editor-container .monaco-editor[data-uri$="${filename}"] .view-lines`; + return this.spectron.client.waitForTextContent(selector, undefined, c => accept(c.replace(/\u00a0/g, ' '))); + } + + public async waitForActiveEditor(filename: string): Promise { + const selector = `.editor-container .monaco-editor[data-uri$="${filename}"] textarea`; + return this.spectron.client.waitForActiveElement(selector); + } + + public async waitForActiveEditorFirstLineText(filename: string): Promise { + const selector = `.editor-container .monaco-editor[data-uri$="${filename}"] textarea`; + const result = await this.spectron.client.waitFor( + () => this.spectron.client.spectron.client.execute(s => { + if (!document.activeElement.matches(s)) { + return undefined; + } + + let element: Element | null = document.activeElement; + while (element && !/monaco-editor/.test(element.className) && element !== document.body) { + element = element.parentElement; + } + + if (element && /monaco-editor/.test(element.className)) { + const firstLine = element.querySelector('.view-lines span span:nth-child(1)'); + + if (firstLine) { + return (firstLine.textContent || '').replace(/\u00a0/g, ' '); // DAMN + } + } + + return undefined; + }, selector), + r => typeof r.value === 'string', + `wait for active editor first line: ${selector}` + ); + + return result.value; } private async getClassSelectors(term: string, viewline: number): Promise { diff --git a/test/smoke/src/areas/extensions/extensions.ts b/test/smoke/src/areas/extensions/extensions.ts index af4fdbb47a0..62e0bb936db 100644 --- a/test/smoke/src/areas/extensions/extensions.ts +++ b/test/smoke/src/areas/extensions/extensions.ts @@ -12,26 +12,25 @@ export class Extensions extends Viewlet { super(spectron); } - public async openExtensionsViewlet(): Promise { + async openExtensionsViewlet(): Promise { await this.spectron.command('workbench.view.extensions'); await this.waitForExtensionsViewlet(); } - public async waitForExtensionsViewlet(): Promise { - await this.spectron.client.waitForElement('div.extensions-viewlet[id="workbench.view.extensions"] .search-box.synthetic-focus'); + async waitForExtensionsViewlet(): Promise { + await this.spectron.client.waitForActiveElement('div.extensions-viewlet[id="workbench.view.extensions"] input.search-box'); } - public async searchForExtension(name: string): Promise { + async searchForExtension(name: string): Promise { const searchBoxSelector = 'div.extensions-viewlet[id="workbench.view.extensions"] .search-box'; await this.spectron.client.clearElement(searchBoxSelector); await this.spectron.client.click(searchBoxSelector); - await this.spectron.client.waitForElement('div.extensions-viewlet[id="workbench.view.extensions"] .search-box.synthetic-focus'); + await this.spectron.client.waitForActiveElement('div.extensions-viewlet[id="workbench.view.extensions"] input.search-box'); await this.spectron.client.keys(name); - } - public async installExtension(name: string): Promise { + async installExtension(name: string): Promise { await this.searchForExtension(name); // we might want to wait for a while longer since the Marketplace can be slow diff --git a/test/smoke/src/areas/multiroot/multiroot.test.ts b/test/smoke/src/areas/multiroot/multiroot.test.ts index 0d4225c9e48..56a1c9d483b 100644 --- a/test/smoke/src/areas/multiroot/multiroot.test.ts +++ b/test/smoke/src/areas/multiroot/multiroot.test.ts @@ -19,10 +19,9 @@ describe('Multi Root', () => { it('shows results from all folders', async function () { await app.workbench.quickopen.openQuickOpen(); - await app.workbench.quickopen.type('*.*'); - await app.workbench.quickopen.waitForQuickOpenElements(6); + await app.workbench.quickopen.closeQuickOpen(); }); it('shows workspace name in title', async function () { diff --git a/test/smoke/src/areas/preferences/settings.ts b/test/smoke/src/areas/preferences/settings.ts index 8e62c210e12..094fda6a9a0 100644 --- a/test/smoke/src/areas/preferences/settings.ts +++ b/test/smoke/src/areas/preferences/settings.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { SpectronApplication } from '../../spectron/application'; -import { Element } from 'webdriverio'; export enum ActivityBarPosition { LEFT = 0, @@ -17,18 +16,18 @@ export class SettingsEditor { // noop } - public async openUserSettings(): Promise { + async openUserSettings(): Promise { await this.spectron.command('workbench.action.openGlobalSettings'); - return this.spectron.client.waitForElement('.settings-search-input input:focus'); + await this.spectron.client.waitForActiveElement('.settings-search-input input'); } - public async focusEditableSettings(): Promise { + async focusEditableSettings(): Promise { await this.spectron.client.keys(['ArrowDown', 'NULL'], false); - await this.spectron.client.waitForElement(`.editable-preferences-editor-container .monaco-editor.focused`); + await this.spectron.client.waitForActiveElement('.editable-preferences-editor-container .monaco-editor textarea'); await this.spectron.client.keys(['ArrowRight', 'NULL'], false); } - public async addUserSetting(setting: string, value: string): Promise { + async addUserSetting(setting: string, value: string): Promise { await this.openUserSettings(); // await this.spectron.wait(1); diff --git a/test/smoke/src/areas/quickopen/quickopen.ts b/test/smoke/src/areas/quickopen/quickopen.ts index 9f91b96f1ae..3958af57349 100644 --- a/test/smoke/src/areas/quickopen/quickopen.ts +++ b/test/smoke/src/areas/quickopen/quickopen.ts @@ -10,43 +10,41 @@ export class QuickOpen { static QUICK_OPEN_HIDDEN = 'div.quick-open-widget[aria-hidden="true"]'; static QUICK_OPEN = 'div.quick-open-widget[aria-hidden="false"]'; - static QUICK_OPEN_FOCUSSED_INPUT = `${QuickOpen.QUICK_OPEN} .quick-open-input input:focus`; + static QUICK_OPEN_INPUT = `${QuickOpen.QUICK_OPEN} .quick-open-input input`; static QUICK_OPEN_FOCUSED_ELEMENT = `${QuickOpen.QUICK_OPEN} .quick-open-tree .monaco-tree-row.focused .monaco-highlighted-label`; static QUICK_OPEN_ENTRY_SELECTOR = 'div[aria-label="Quick Picker"] .monaco-tree-rows.show-twisties .monaco-tree-row .quick-open-entry'; constructor(readonly spectron: SpectronApplication) { } - public async openQuickOpen(): Promise { + async openQuickOpen(): Promise { await this.spectron.command('workbench.action.quickOpen'); await this.waitForQuickOpenOpened(); } - public async openCommandPallette(): Promise { + async openCommandPallette(): Promise { await this.spectron.command('workbench.action.showCommands'); await this.waitForQuickOpenOpened(); } - public async closeQuickOpen(): Promise { + async closeQuickOpen(): Promise { await this.spectron.command('workbench.action.closeQuickOpen'); await this.waitForQuickOpenClosed(); } - public async type(text: string): Promise { - let prefix = await this.spectron.client.getValue(QuickOpen.QUICK_OPEN_FOCUSSED_INPUT); + async type(text: string): Promise { await this.spectron.client.type(text); - await this.spectron.client.waitForValue(QuickOpen.QUICK_OPEN_FOCUSSED_INPUT, prefix + text); } - public async getQuickOpenElements(): Promise { + async getQuickOpenElements(): Promise { return this.spectron.client.waitForElements(QuickOpen.QUICK_OPEN_ENTRY_SELECTOR); } - public async waitForQuickOpenElements(count: number): Promise { + async waitForQuickOpenElements(count: number): Promise { return this.spectron.client.waitForElements(QuickOpen.QUICK_OPEN_ENTRY_SELECTOR, elements => elements && elements.length === count); } - public async openFile(fileName: string): Promise { + async openFile(fileName: string): Promise { await this.openQuickOpen(); await this.type(fileName); @@ -56,7 +54,7 @@ export class QuickOpen { await this.spectron.workbench.waitForEditorFocus(fileName); } - public async runCommand(commandText: string): Promise { + async runCommand(commandText: string): Promise { await this.openCommandPallette(); // type the text @@ -69,21 +67,24 @@ export class QuickOpen { await this.spectron.client.waitAndClick(QuickOpen.QUICK_OPEN_FOCUSED_ELEMENT); } - public waitForQuickOpenOpened(): Promise { - return this.spectron.client.waitForElement(QuickOpen.QUICK_OPEN_FOCUSSED_INPUT); + async waitForQuickOpenOpened(): Promise { + await this.spectron.client.waitForActiveElement(QuickOpen.QUICK_OPEN_INPUT); + + // we gotta wait 50 milliseconds due to https://github.com/Microsoft/vscode/blob/master/src/vs/platform/list/browser/listService.ts#L59 + await new Promise(c => setTimeout(c, 50)); } - public waitForQuickOpenClosed(): Promise { - return this.spectron.client.waitForElement(QuickOpen.QUICK_OPEN_HIDDEN); + private async waitForQuickOpenClosed(): Promise { + await this.spectron.client.waitForElement(QuickOpen.QUICK_OPEN_HIDDEN); } - public async submit(text: string): Promise { + async submit(text: string): Promise { await this.spectron.client.type(text); await this.spectron.client.keys(['Enter', 'NULL']); await this.waitForQuickOpenClosed(); } - public async selectQuickOpenElement(index: number): Promise { + async selectQuickOpenElement(index: number): Promise { await this.waitForQuickOpenOpened(); for (let from = 0; from < index; from++) { await this.spectron.client.keys(['ArrowDown', 'NULL']); diff --git a/test/smoke/src/areas/workbench/data-loss.test.ts b/test/smoke/src/areas/workbench/data-loss.test.ts index 62c4fde7667..aa33283753e 100644 --- a/test/smoke/src/areas/workbench/data-loss.test.ts +++ b/test/smoke/src/areas/workbench/data-loss.test.ts @@ -27,13 +27,13 @@ describe('Dataloss', () => { await app.workbench.waitForActiveTab(fileName, true); await app.screenCapturer.capture(`${fileName} after reload`); - let actual = await app.workbench.editor.getEditorFirstLineText(); - assert.ok(actual.startsWith(textToType), `${actual} did not start with ${textToType}`); + let actual = await app.workbench.editor.waitForActiveEditorFirstLineText(fileName); + assert.ok(actual.startsWith(textToType), `'${actual}' did not start with '${textToType}'`); await app.workbench.waitForTab(untitled, true); await app.workbench.selectTab('Untitled-1', true); await app.screenCapturer.capture('Untitled file after reload'); - actual = await app.workbench.editor.getEditorFirstLineText(); - assert.ok(actual.startsWith(textToTypeInUntitled), `${actual} did not start with ${textToTypeInUntitled}`); + actual = await app.workbench.editor.waitForActiveEditorFirstLineText('Untitled-1'); + assert.ok(actual.startsWith(textToTypeInUntitled), `'${actual}' did not start with '${textToTypeInUntitled}'`); }); }); \ No newline at end of file diff --git a/test/smoke/src/areas/workbench/data-migration.test.ts b/test/smoke/src/areas/workbench/data-migration.test.ts index 4156ad0501a..3e2c44674f7 100644 --- a/test/smoke/src/areas/workbench/data-migration.test.ts +++ b/test/smoke/src/areas/workbench/data-migration.test.ts @@ -37,7 +37,7 @@ describe('Data Migration', () => { app.screenCapturer.testName = 'Untitled is restorted'; assert.ok(await app.workbench.waitForActiveTab('Untitled-1', true), `Untitled-1 tab is not present after migration.`); - const actual = await app.workbench.editor.getEditorFirstLineText(); + const actual = await app.workbench.editor.waitForActiveEditorFirstLineText('Untitled-1'); await app.screenCapturer.capture('Untitled file text'); assert.ok(actual.startsWith(textToType), `${actual} did not start with ${textToType}`); }); @@ -65,8 +65,9 @@ describe('Data Migration', () => { await app.start('Data Migration'); app.screenCapturer.testName = 'Newly created dirty file is restorted'; - assert.ok(await app.workbench.waitForActiveTab(fileName.split('/')[1]), `Untitled-1 tab is not present after migration.`); - const actual = await app.workbench.editor.getEditorFirstLineText(); + const filename = fileName.split('/')[1]; + assert.ok(await app.workbench.waitForActiveTab(filename), `Untitled-1 tab is not present after migration.`); + const actual = await app.workbench.editor.waitForActiveEditorFirstLineText(filename); await app.screenCapturer.capture(fileName + ' text'); assert.ok(actual.startsWith(firstTextPart.concat(secondTextPart)), `${actual} did not start with ${firstTextPart.concat(secondTextPart)}`); diff --git a/test/smoke/src/areas/workbench/workbench.ts b/test/smoke/src/areas/workbench/workbench.ts index 1d04da84db5..7bbe09c719f 100644 --- a/test/smoke/src/areas/workbench/workbench.ts +++ b/test/smoke/src/areas/workbench/workbench.ts @@ -3,8 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as path from 'path'; -import URI from 'vscode-uri'; import { SpectronApplication } from '../../spectron/application'; import { Explorer } from '../explorer/explorer'; import { ActivityBar } from '../activitybar/activityBar'; @@ -70,10 +68,7 @@ export class Workbench { public async waitForEditorFocus(fileName: string, untitled: boolean = false): Promise { await this.waitForActiveTab(fileName); - await this.spectron.client.waitFor(async () => { - const uri = await this.editor.getFocusedEditorUri(); - return uri && path.basename(URI.parse(uri).path) === fileName; - }, void 0, `Wait for editor with ${fileName} is focussed`); + await this.editor.waitForActiveEditor(fileName); } public async waitForActiveTab(fileName: string, isDirty: boolean = false): Promise { diff --git a/test/smoke/src/spectron/client.ts b/test/smoke/src/spectron/client.ts index 72697d5d903..7868d48c5ef 100644 --- a/test/smoke/src/spectron/client.ts +++ b/test/smoke/src/spectron/client.ts @@ -101,8 +101,12 @@ export class SpectronClient { .then(result => result.value); } - public async waitForActiveElement(accept: (result: Element | undefined) => boolean = result => !!result): Promise { - return this.waitFor>(() => this.spectron.client.elementActive(), result => accept(result ? result.value : void 0), `elementActive`); + public async waitForActiveElement(selector: string): Promise { + return this.waitFor( + () => this.spectron.client.execute(s => document.activeElement.matches(s), selector), + r => r.value, + `wait for active element: ${selector}` + ); } public async waitForAttribute(selector: string, attribute: string, accept: (result: string) => boolean = result => !!result): Promise {