diff --git a/.github/new_release.yml b/.github/new_release.yml index 1a0465c011d..902078aa79c 100644 --- a/.github/new_release.yml +++ b/.github/new_release.yml @@ -1,5 +1,5 @@ { newReleaseLabel: 'new release', - newReleases: ['1.17.1'], + newReleases: ['1.17.2'], perform: true } \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index a04880dd308..0854813e23f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,8 @@ cache: notifications: email: false + webhooks: + - http://vscode-test-probot.westus.cloudapp.azure.com:3450/travis/notifications addons: apt: diff --git a/.vscode/settings.json b/.vscode/settings.json index 363bb9bcd4e..341a454f1ab 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -14,7 +14,7 @@ "**/node_modules": true, "**/bower_components": true, ".build/**": true, - "out*/**": true, + "out/**": true, "i18n/**": true, "extensions/**/out/**": true, "test/smoke/out/**": true diff --git a/build/gulpfile.hygiene.js b/build/gulpfile.hygiene.js index ee780903b6b..b14398194a1 100644 --- a/build/gulpfile.hygiene.js +++ b/build/gulpfile.hygiene.js @@ -12,6 +12,7 @@ const gulptslint = require('gulp-tslint'); const gulpeslint = require('gulp-eslint'); const tsfmt = require('typescript-formatter'); const tslint = require('tslint'); +const vfs = require('vinyl-fs'); /** * Hygiene works by creating cascading subsets of all our files and @@ -235,7 +236,7 @@ const hygiene = exports.hygiene = (some, options) => { this.emit('data', file); }); - const result = gulp.src(some || all, { base: '.' }) + const result = vfs.src(some || all, { base: '.', follow: true }) .pipe(filter(f => !f.stat.isDirectory())) .pipe(filter(eolFilter)) .pipe(options.skipEOL ? es.through() : eol) @@ -265,7 +266,7 @@ const hygiene = exports.hygiene = (some, options) => { })); }; -gulp.task('hygiene', () => hygiene()); +gulp.task('hygiene', () => hygiene('')); // this allows us to run hygiene as a git pre-commit hook if (require.main === module) { diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 9f7088b945e..83499a05144 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.18' }, - { name: 'ms-vscode.node-debug2', version: '1.18.1' } + { name: 'ms-vscode.node-debug', version: '1.18.2' }, + { name: 'ms-vscode.node-debug2', version: '1.18.3' } ]; const excludedExtensions = [ diff --git a/build/lib/i18n.resources.json b/build/lib/i18n.resources.json index 9325369a2a5..9aafba7dc49 100644 --- a/build/lib/i18n.resources.json +++ b/build/lib/i18n.resources.json @@ -186,6 +186,10 @@ "name": "vs/workbench/services/textMate", "project": "vscode-workbench" }, + { + "name": "vs/workbench/services/workspace", + "project": "vscode-workbench" + }, { "name": "setup_messages", "project": "vscode-workbench" diff --git a/build/lib/watch/index.js b/build/lib/watch/index.js index bed579ec075..93d9babc2de 100644 --- a/build/lib/watch/index.js +++ b/build/lib/watch/index.js @@ -19,14 +19,15 @@ function handleDeletions() { let watch = void 0; -if (!process.env['VSCODE_USE_LEGACY_WATCH']) { - try { - watch = require('./watch-nsfw'); - } catch (err) { - console.warn('Could not load our cross platform file watcher: ' + err.toString()); - console.warn('Falling back to our platform specific watcher...'); - } -} +// Disabled due to https://github.com/Microsoft/vscode/issues/36214 +// if (!process.env['VSCODE_USE_LEGACY_WATCH']) { +// try { +// watch = require('./watch-nsfw'); +// } catch (err) { +// console.warn('Could not load our cross platform file watcher: ' + err.toString()); +// console.warn('Falling back to our platform specific watcher...'); +// } +// } if (!watch) { watch = process.platform === 'win32' ? require('./watch-win32') : require('gulp-watch'); diff --git a/build/monaco/monaco.d.ts.recipe b/build/monaco/monaco.d.ts.recipe index cdaee801a9c..da006b6bce4 100644 --- a/build/monaco/monaco.d.ts.recipe +++ b/build/monaco/monaco.d.ts.recipe @@ -34,7 +34,7 @@ declare module monaco { #include(vs/base/common/winjs.base.d.ts): TValueCallback, ProgressCallback, Promise #include(vs/base/common/cancellation): CancellationTokenSource, CancellationToken -#include(vs/base/common/uri): URI +#include(vs/base/common/uri): URI, UriComponents #include(vs/editor/common/standalone/standaloneBase): KeyCode, KeyMod #include(vs/base/common/htmlContent): IMarkdownString #include(vs/base/browser/keyboardEvent): IKeyboardEvent diff --git a/build/tfs/darwin/build.sh b/build/tfs/darwin/build.sh index b13aaa78820..df7ee5e0b16 100755 --- a/build/tfs/darwin/build.sh +++ b/build/tfs/darwin/build.sh @@ -37,6 +37,17 @@ step "Run unit tests" \ step "Run integration tests" \ ./scripts/test-integration.sh +# function smoketest { +# ARTIFACTS="$AGENT_BUILDDIRECTORY/smoketest-artifacts" +# rm -rf $ARTIFACTS + +# [[ "$VSCODE_QUALITY" == "insider" ]] && VSCODE_APPNAME="Visual Studio Code - Insiders" || VSCODE_APPNAME="Visual Studio Code" +# npm run smoketest -- --build "$AGENT_BUILDDIRECTORY/VSCode-darwin/$VSCODE_APPNAME.app" --log $ARTIFACTS +# } + +# step "Run smoke test" \ +# smoketest + step "Publish release" \ ./build/tfs/darwin/release.sh diff --git a/build/tfs/darwin/build2.sh b/build/tfs/darwin/build2.sh deleted file mode 100755 index 350f594da7d..00000000000 --- a/build/tfs/darwin/build2.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/sh - -. ./build/tfs/common/node.sh -. ./scripts/env.sh -. ./build/tfs/common/common.sh - -export VSCODE_MIXIN_PASSWORD="$1" -export AZURE_STORAGE_ACCESS_KEY="$2" -export AZURE_STORAGE_ACCESS_KEY_2="$3" -export MOONCAKE_STORAGE_ACCESS_KEY="$4" -export AZURE_DOCUMENTDB_MASTERKEY="$5" -VSO_PAT="$6" - -echo "machine monacotools.visualstudio.com password $VSO_PAT" > ~/.netrc - -step "Install dependencies" \ - npm install - -step "Hygiene" \ - npm run gulp -- hygiene - -step "Mix in repository from vscode-distro" \ - npm run gulp -- mixin - -step "Install distro dependencies" \ - node build/tfs/common/installDistro.js - -step "Build minified & upload source maps" \ - npm run gulp -- vscode-darwin-min - -REPO=`pwd` -ZIP=$REPO/../VSCode-darwin.zip -BUILD=$REPO/../VSCode-darwin - -function createZip { - rm -rf $ZIP - cd $BUILD && zip -r -X -y $ZIP * -} - -step "Create unsigned archive" \ - createZip diff --git a/build/tfs/darwin/smoketest.sh b/build/tfs/darwin/smoketest.sh deleted file mode 100755 index cb89a32b783..00000000000 --- a/build/tfs/darwin/smoketest.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/sh - -. ./build/tfs/common/node.sh -. ./scripts/env.sh -. ./build/tfs/common/common.sh - -export VSCODE_MIXIN_PASSWORD="$1" -VSO_PAT="$2" - -echo "machine monacotools.visualstudio.com password $VSO_PAT" > ~/.netrc - -step "Install dependencies" \ - npm install - -step "Mix in repository from vscode-distro" \ - npm run gulp -- mixin - -step "Install distro dependencies" \ - node build/tfs/common/installDistro.js - -step "Build minified & upload source maps" \ - npm run gulp -- vscode-darwin-min - -function runSmokeTest { - SCREENSHOTS="$AGENT_BUILDDIRECTORY/smoketest-screenshots" - rm -rf $SCREENSHOTS - - npm run smoketest -- --build "$AGENT_BUILDDIRECTORY/VSCode-darwin/Visual Studio Code - Insiders.app" --screenshots $SCREENSHOTS -} - -step "Run smoke test" \ - runSmokeTest \ No newline at end of file diff --git a/build/tfs/linux/build.sh b/build/tfs/linux/build.sh index b3d1825c2d9..f138b5d237e 100755 --- a/build/tfs/linux/build.sh +++ b/build/tfs/linux/build.sh @@ -39,5 +39,22 @@ step "Build minified" \ step "Run unit tests" \ ./scripts/test.sh --build --reporter dot +# function smoketest { +# id -u testuser &>/dev/null || (useradd -m testuser; chpasswd <<< testuser:testpassword) +# sudo -i -u testuser -- sh -c 'git config --global user.name "VS Code Agent" && git config --global user.email "monacotools@microsoft.com"' + +# ARTIFACTS="$AGENT_BUILDDIRECTORY/smoketest-artifacts" +# rm -rf $ARTIFACTS +# mkdir -p $ARTIFACTS +# chown -R testuser $ARTIFACTS + +# ps -o pid= -u testuser | xargs sudo kill -9 +# DISPLAY=:10 sudo -i -u testuser -- sh -c "cd $BUILD_SOURCESDIRECTORY/test/smoke && ./node_modules/.bin/mocha --build $AGENT_BUILDDIRECTORY/VSCode-linux-$ARCH --log $ARTIFACTS" +# # DISPLAY=:10 sudo -i -u testuser -- sh -c "cd /vso/work/1/s/test/smoke && ./node_modules/.bin/mocha --build /vso/work/1/VSCode-linux-ia32" +# } + +# step "Run smoke test" \ +# smoketest + step "Publish release" \ ./build/tfs/linux/release.sh diff --git a/build/tfs/linux/ia32/Dockerfile b/build/tfs/linux/ia32/Dockerfile index b309a1e940f..25d621d99fb 100644 --- a/build/tfs/linux/ia32/Dockerfile +++ b/build/tfs/linux/ia32/Dockerfile @@ -40,6 +40,9 @@ ADD xvfb.init /etc/init.d/xvfb RUN chmod +x /etc/init.d/xvfb RUN update-rc.d xvfb defaults +# dbus +RUN ln -sf /bin/dbus-daemon /usr/bin/dbus-daemon + # nvm ENV NVM_DIR /usr/local/nvm RUN curl https://raw.githubusercontent.com/creationix/nvm/v0.33.2/install.sh | bash @@ -47,4 +50,4 @@ RUN curl https://raw.githubusercontent.com/creationix/nvm/v0.33.2/install.sh | b # for libsecret ENV PKG_CONFIG_PATH /usr/lib/i386-linux-gnu/pkgconfig -CMD (service xvfb start; export DISPLAY=:10; ./start.sh) \ No newline at end of file +CMD (service xvfb start; service dbus start; export DISPLAY=:10; ./start.sh) \ No newline at end of file diff --git a/build/tfs/linux/smoketest.sh b/build/tfs/linux/smoketest.sh deleted file mode 100644 index 10ecf535f84..00000000000 --- a/build/tfs/linux/smoketest.sh +++ /dev/null @@ -1,49 +0,0 @@ -#!/bin/bash -set -e - -. ./build/tfs/common/node.sh -. ./scripts/env.sh -. ./build/tfs/common/common.sh - -export ARCH="x64" -export VSCODE_MIXIN_PASSWORD="$1" -VSO_PAT="$2" - -echo "machine monacotools.visualstudio.com password $VSO_PAT" > ~/.netrc - -export SCREENSHOTS="$AGENT_BUILDDIRECTORY/smoketest-screenshots" - -function configureEnvironment { - id -u testuser &>/dev/null || (useradd -m testuser; chpasswd <<< testuser:testpassword) - sudo -i -u testuser -- sh -c 'git config --global user.name "VS Code Agent" && git config --global user.email "monacotools@microsoft.com"' - - sudo rm -rf $SCREENSHOTS - mkdir -p $SCREENSHOTS - chown -R testuser $SCREENSHOTS -} - -function runSmokeTest { - DISPLAY=:10 sudo -i -u testuser -- sh -c "cd $BUILD_SOURCESDIRECTORY/test/smoke && ./node_modules/.bin/mocha --build $AGENT_BUILDDIRECTORY/VSCode-linux-x64 --screenshots $SCREENSHOTS" -} - -step "Install dependencies" \ - npm install --arch=$ARCH --unsafe-perm - -step "Mix in repository from vscode-distro" \ - npm run gulp -- mixin - -step "Get Electron" \ - npm run gulp -- "electron-$ARCH" - -step "Install distro dependencies" \ - node build/tfs/common/installDistro.js --arch=$ARCH - -step "Build minified" \ - npm run gulp -- "vscode-linux-$ARCH-min" - -step "Configure environment" \ - configureEnvironment - -step "Run smoke test" \ - runSmokeTest - diff --git a/build/tfs/linux/x64/Dockerfile b/build/tfs/linux/x64/Dockerfile index 4fef9acb19a..ae9190c29db 100644 --- a/build/tfs/linux/x64/Dockerfile +++ b/build/tfs/linux/x64/Dockerfile @@ -36,8 +36,11 @@ ADD xvfb.init /etc/init.d/xvfb RUN chmod +x /etc/init.d/xvfb RUN update-rc.d xvfb defaults +# dbus +RUN ln -sf /bin/dbus-daemon /usr/bin/dbus-daemon + # nvm ENV NVM_DIR /usr/local/nvm RUN curl https://raw.githubusercontent.com/creationix/nvm/v0.33.2/install.sh | bash -CMD (service xvfb start; export DISPLAY=:10; ./start.sh) \ No newline at end of file +CMD (service xvfb start; service dbus start; export DISPLAY=:10; ./start.sh) \ No newline at end of file diff --git a/build/tfs/win32/1_build.ps1 b/build/tfs/win32/1_build.ps1 index 0090920d506..c7fb712e18f 100644 --- a/build/tfs/win32/1_build.ps1 +++ b/build/tfs/win32/1_build.ps1 @@ -52,4 +52,11 @@ step "Run unit tests" { # exec { & .\scripts\test-integration.bat } # } +# step "Run smoke test" { +# $Artifacts = "$env:AGENT_BUILDDIRECTORY\smoketest-artifacts" +# Remove-Item -Recurse -Force -ErrorAction Ignore $Artifacts + +# exec { & npm run smoketest -- --build "$env:AGENT_BUILDDIRECTORY\VSCode-win32-$global:arch" --log "$Artifacts" } +# } + done diff --git a/build/tfs/win32/build_unsigned.ps1 b/build/tfs/win32/build_unsigned.ps1 index dbb505b5683..207f633999c 100644 --- a/build/tfs/win32/build_unsigned.ps1 +++ b/build/tfs/win32/build_unsigned.ps1 @@ -47,6 +47,13 @@ step "Run unit tests" { exec { & .\scripts\test.bat --build --reporter dot } } +step "Run smoke test" { + $Artifacts = "$env:AGENT_BUILDDIRECTORY\smoketest-artifacts" + Remove-Item -Recurse -Force -ErrorAction Ignore $Artifacts + + exec { & npm run smoketest -- --build "$env:AGENT_BUILDDIRECTORY\VSCode-win32-$global:arch" --log "$Artifacts" } +} + step "Create archive and setup package" { exec { & npm run gulp -- "vscode-win32-$global:arch-archive" "vscode-win32-$global:arch-setup" } } diff --git a/build/tfs/win32/smoketest.ps1 b/build/tfs/win32/smoketest.ps1 deleted file mode 100644 index 6a2b457bf98..00000000000 --- a/build/tfs/win32/smoketest.ps1 +++ /dev/null @@ -1,47 +0,0 @@ -Param( - [string]$arch, - [string]$mixinPassword, - [string]$vsoPAT -) - -. .\build\tfs\win32\node.ps1 -. .\scripts\env.ps1 -. .\build\tfs\win32\lib.ps1 - -# Create a _netrc file to download distro dependencies -# In order to get _netrc to work, we need a HOME variable setup -$env:HOME = $env:USERPROFILE -"machine monacotools.visualstudio.com password ${vsoPAT}" | Out-File "$env:USERPROFILE\_netrc" -Encoding ASCII - -# Set the right architecture -$env:npm_config_arch = "$arch" - -step "Install dependencies" { - exec { & npm install } -} - -$env:VSCODE_MIXIN_PASSWORD = $mixinPassword -step "Mix in repository from vscode-distro" { - exec { & npm run gulp -- mixin } -} - -step "Get Electron" { - exec { & npm run gulp -- "electron-$global:arch" } -} - -step "Install distro dependencies" { - exec { & node build\tfs\common\installDistro.js } -} - -step "Build minified" { - exec { & npm run gulp -- "vscode-win32-$global:arch-min" } -} - -step "Run smoke test" { - $Screenshots = "$env:AGENT_BUILDDIRECTORY\smoketest-screenshots" - Remove-Item -Recurse -Force -ErrorAction Ignore $Screenshots - - exec { & npm run smoketest -- --build "$env:AGENT_BUILDDIRECTORY\VSCode-win32-$global:arch" --screenshots "$Screenshots" } -} - -done \ No newline at end of file diff --git a/build/win32/code.iss b/build/win32/code.iss index 73b031fe643..409bae2c2f5 100644 --- a/build/win32/code.iss +++ b/build/win32/code.iss @@ -19,6 +19,7 @@ OutputBaseFilename=VSCodeSetup Compression=lzma SolidCompression=yes AppMutex={#AppMutex} +SetupMutex={#AppMutex}setup WizardImageFile={#RepoDir}\resources\win32\inno-big.bmp WizardSmallImageFile={#RepoDir}\resources\win32\inno-small.bmp SetupIconFile={#RepoDir}\resources\win32\code.ico diff --git a/extensions/coffeescript/syntaxes/coffeescript.tmLanguage.json b/extensions/coffeescript/syntaxes/coffeescript.tmLanguage.json index 86441c11dd8..68e1bc4e83b 100644 --- a/extensions/coffeescript/syntaxes/coffeescript.tmLanguage.json +++ b/extensions/coffeescript/syntaxes/coffeescript.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/atom/language-coffee-script/commit/da81e3f537ccbbb70e542fa5af79583eb58ec50b", + "version": "https://github.com/atom/language-coffee-script/commit/8873cbc4e2f3b790603cbe7102d60f41fc82f726", "scopeName": "source.coffee", "name": "CoffeeScript", "fileTypes": [ @@ -535,13 +535,13 @@ "arguments": { "patterns": [ { - "begin": "(?=(@|@?[\\w$]+|[=-]>|\\-\\d|\\[|{|\"|'))|\\(", + "begin": "\\(", "beginCaptures": { "0": { "name": "punctuation.definition.arguments.begin.bracket.round.coffee" } }, - "end": "\\)|(?=\\s*(?|\\-\\d|\\[|{|\"|'))", + "end": "(?=\\s*(?|\\-\\d|\\[|\\{|\"|'))|(?=\\())", + "begin": "(@)?([\\w$]+)(?=\\()", "beginCaptures": { "1": { "name": "variable.other.readwrite.instance.coffee" @@ -600,27 +610,56 @@ "2": { "patterns": [ { - "match": "(?x)\n\\b(isNaN|isFinite|eval|uneval|parseInt|parseFloat|decodeURI|\ndecodeURIComponent|encodeURI|encodeURIComponent|escape|unescape|\nrequire|set(Interval|Timeout)|clear(Interval|Timeout))\\b", - "name": "support.function.coffee" - }, - { - "match": "[a-zA-Z_$][\\w$]*", - "name": "entity.name.function.coffee" - }, - { - "match": "\\d[\\w$]*", - "name": "invalid.illegal.identifier.coffee" + "include": "#function_names" } ] } }, - "end": "(?<=\\))|(?=\\s*(?|\\-\\d|\\[|{|\"|')))", + "beginCaptures": { + "1": { + "name": "variable.other.readwrite.instance.coffee" + }, + "2": { + "patterns": [ + { + "include": "#function_names" + } + ] + } + }, + "end": "(?=\\s*(?|\\-\\d|\\[|\\{|\"|'))|(?=\\())", + "begin": "(?:(\\.)|(::))\\s*([\\w$]+)\\s*(?=\\()", "beginCaptures": { "1": { "name": "punctuation.separator.method.period.coffee" @@ -724,35 +763,67 @@ "3": { "patterns": [ { - "match": "(?x)\n\\bon(Rowsinserted|Rowsdelete|Rowenter|Rowexit|Resize|Resizestart|Resizeend|Reset|\nReadystatechange|Mouseout|Mouseover|Mousedown|Mouseup|Mousemove|\nBefore(cut|deactivate|unload|update|paste|print|editfocus|activate)|\nBlur|Scrolltop|Submit|Select|Selectstart|Selectionchange|Hover|Help|\nChange|Contextmenu|Controlselect|Cut|Cellchange|Clock|Close|Deactivate|\nDatasetchanged|Datasetcomplete|Dataavailable|Drop|Drag|Dragstart|Dragover|\nDragdrop|Dragenter|Dragend|Dragleave|Dblclick|Unload|Paste|Propertychange|Error|\nErrorupdate|Keydown|Keyup|Keypress|Focus|Load|Activate|Afterupdate|Afterprint|Abort)\\b", - "name": "support.function.event-handler.coffee" - }, - { - "match": "(?x)\n\\b(shift|showModelessDialog|showModalDialog|showHelp|scroll|scrollX|scrollByPages|\nscrollByLines|scrollY|scrollTo|stop|strike|sizeToContent|sidebar|signText|sort|\nsup|sub|substr|substring|splice|split|send|set(Milliseconds|Seconds|Minutes|Hours|\nMonth|Year|FullYear|Date|UTC(Milliseconds|Seconds|Minutes|Hours|Month|FullYear|Date)|\nTime|Hotkeys|Cursor|ZOptions|Active|Resizable|RequestHeader)|search|slice|\nsavePreferences|small|home|handleEvent|navigate|char|charCodeAt|charAt|concat|\ncontextual|confirm|compile|clear|captureEvents|call|createStyleSheet|createPopup|\ncreateEventObject|to(GMTString|UTCString|String|Source|UpperCase|LowerCase|LocaleString)|\ntest|taint|taintEnabled|indexOf|italics|disableExternalCapture|dump|detachEvent|unshift|\nuntaint|unwatch|updateCommands|join|javaEnabled|pop|push|plugins.refresh|paddings|parse|\nprint|prompt|preference|enableExternalCapture|exec|execScript|valueOf|UTC|find|file|\nfileModifiedDate|fileSize|fileCreatedDate|fileUpdatedDate|fixed|fontsize|fontcolor|\nforward|fromCharCode|watch|link|load|lastIndexOf|anchor|attachEvent|atob|apply|alert|\nabort|routeEvents|resize|resizeBy|resizeTo|recalc|returnValue|replace|reverse|reload|\nreleaseCapture|releaseEvents|go|get(Milliseconds|Seconds|Minutes|Hours|Month|Day|Year|FullYear|\nTime|Date|TimezoneOffset|UTC(Milliseconds|Seconds|Minutes|Hours|Day|Month|FullYear|Date)|\nAttention|Selection|ResponseHeader|AllResponseHeaders)|moveBy|moveBelow|moveTo|\nmoveToAbsolute|moveAbove|mergeAttributes|match|margins|btoa|big|bold|borderWidths|blink|back)\\b", - "name": "support.function.coffee" - }, - { - "match": "(?x)\n\\b(acceptNode|add|addEventListener|addTextTrack|adoptNode|after|animate|append|\nappendChild|appendData|before|blur|canPlayType|captureStream|\ncaretPositionFromPoint|caretRangeFromPoint|checkValidity|clear|click|\ncloneContents|cloneNode|cloneRange|close|closest|collapse|\ncompareBoundaryPoints|compareDocumentPosition|comparePoint|contains|\nconvertPointFromNode|convertQuadFromNode|convertRectFromNode|createAttribute|\ncreateAttributeNS|createCaption|createCDATASection|createComment|\ncreateContextualFragment|createDocument|createDocumentFragment|\ncreateDocumentType|createElement|createElementNS|createEntityReference|\ncreateEvent|createExpression|createHTMLDocument|createNodeIterator|\ncreateNSResolver|createProcessingInstruction|createRange|createShadowRoot|\ncreateTBody|createTextNode|createTFoot|createTHead|createTreeWalker|delete|\ndeleteCaption|deleteCell|deleteContents|deleteData|deleteRow|deleteTFoot|\ndeleteTHead|detach|disconnect|dispatchEvent|elementFromPoint|elementsFromPoint|\nenableStyleSheetsForSet|entries|evaluate|execCommand|exitFullscreen|\nexitPointerLock|expand|extractContents|fastSeek|firstChild|focus|forEach|get|\ngetAll|getAnimations|getAttribute|getAttributeNames|getAttributeNode|\ngetAttributeNodeNS|getAttributeNS|getBoundingClientRect|getBoxQuads|\ngetClientRects|getContext|getDestinationInsertionPoints|getElementById|\ngetElementsByClassName|getElementsByName|getElementsByTagName|\ngetElementsByTagNameNS|getItem|getNamedItem|getSelection|getStartDate|\ngetVideoPlaybackQuality|has|hasAttribute|hasAttributeNS|hasAttributes|\nhasChildNodes|hasFeature|hasFocus|importNode|initEvent|insertAdjacentElement|\ninsertAdjacentHTML|insertAdjacentText|insertBefore|insertCell|insertData|\ninsertNode|insertRow|intersectsNode|isDefaultNamespace|isEqualNode|\nisPointInRange|isSameNode|item|key|keys|lastChild|load|lookupNamespaceURI|\nlookupPrefix|matches|move|moveAttribute|moveAttributeNode|moveChild|\nmoveNamedItem|namedItem|nextNode|nextSibling|normalize|observe|open|\nparentNode|pause|play|postMessage|prepend|preventDefault|previousNode|\npreviousSibling|probablySupportsContext|queryCommandEnabled|\nqueryCommandIndeterm|queryCommandState|queryCommandSupported|queryCommandValue|\nquerySelector|querySelectorAll|registerContentHandler|registerElement|\nregisterProtocolHandler|releaseCapture|releaseEvents|remove|removeAttribute|\nremoveAttributeNode|removeAttributeNS|removeChild|removeEventListener|\nremoveItem|replace|replaceChild|replaceData|replaceWith|reportValidity|\nrequestFullscreen|requestPointerLock|reset|scroll|scrollBy|scrollIntoView|\nscrollTo|seekToNextFrame|select|selectNode|selectNodeContents|set|setAttribute|\nsetAttributeNode|setAttributeNodeNS|setAttributeNS|setCapture|\nsetCustomValidity|setEnd|setEndAfter|setEndBefore|setItem|setNamedItem|\nsetRangeText|setSelectionRange|setSinkId|setStart|setStartAfter|setStartBefore|\nslice|splitText|stepDown|stepUp|stopImmediatePropagation|stopPropagation|\nsubmit|substringData|supports|surroundContents|takeRecords|terminate|toBlob|\ntoDataURL|toggle|toString|values|write|writeln)\\b", - "name": "support.function.dom.coffee" - }, - { - "match": "[a-zA-Z_$][\\w$]*", - "name": "entity.name.function.coffee" - }, - { - "match": "\\d[\\w$]*", - "name": "invalid.illegal.identifier.coffee" + "include": "#method_names" } ] } }, - "end": "(?<=\\))|(?=\\s*(?|\\-\\d|\\[|{|\"|')))", + "beginCaptures": { + "1": { + "name": "punctuation.separator.method.period.coffee" + }, + "2": { + "name": "keyword.operator.prototype.coffee" + }, + "3": { + "patterns": [ + { + "include": "#method_names" + } + ] + } + }, + "end": "(?=\\s*(? { + provideColorPresentations(color: Color, context): ColorPresentation[] | Thenable { let params: ColorPresentationParams = { - textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document), - colorInfo: { range: client.code2ProtocolConverter.asRange(colorInfo.range), color: colorInfo.color } + textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(context.document), + colorInfo: { range: client.code2ProtocolConverter.asRange(context.range), color } }; return client.sendRequest(ColorPresentationRequest.type, params).then(presentations => { return presentations.map(p => { diff --git a/extensions/emmet/package.json b/extensions/emmet/package.json index 9a8c21d3892..95c60978a31 100644 --- a/extensions/emmet/package.json +++ b/extensions/emmet/package.json @@ -16,6 +16,7 @@ }, "activationEvents": [ "onCommand:type", + "onCommand:emmet.expandAbbreviation", "onLanguage:html", "onLanguage:css", "onLanguage:scss", diff --git a/extensions/emmet/package.nls.json b/extensions/emmet/package.nls.json index 837b936b5b1..07ffacf4b44 100644 --- a/extensions/emmet/package.nls.json +++ b/extensions/emmet/package.nls.json @@ -23,13 +23,13 @@ "command.incrementNumberByTen": "Increment by 10", "command.decrementNumberByTen": "Decrement by 10", "emmetSyntaxProfiles": "Define profile for specified syntax or use your own profile with specific rules.", - "emmetExclude": "An array of languages where emmet abbreviations should not be expanded.", - "emmetExtensionsPath": "Path to a folder containing emmet profiles and snippets.'", - "emmetShowExpandedAbbreviation": "Shows expanded emmet abbreviations as suggestions.\nThe option \"inMarkupAndStylesheetFilesOnly\" applies to html, haml, jade, slim, xml, xsl, css, scss, sass, less and stylus.\nThe option \"always\" applies to all parts of the file regardless of markup/css.", - "emmetShowAbbreviationSuggestions": "Shows possible emmet abbreviations as suggestions. Not applicable in stylesheets or when emmet.showExpandedAbbreviation is set to \"never\".", - "emmetIncludeLanguages": "Enable emmet abbreviations in languages that are not supported by default. Add a mapping here between the language and emmet supported language.\n Eg: {\"vue-html\": \"html\", \"javascript\": \"javascriptreact\"}", - "emmetVariables": "Variables to be used in emmet snippets", - "emmetTriggerExpansionOnTab": "When enabled, emmet abbreviations are expanded when pressing TAB.", + "emmetExclude": "An array of languages where Emmet abbreviations should not be expanded.", + "emmetExtensionsPath": "Path to a folder containing Emmet profiles and snippets.'", + "emmetShowExpandedAbbreviation": "Shows expanded Emmet abbreviations as suggestions.\nThe option \"inMarkupAndStylesheetFilesOnly\" applies to html, haml, jade, slim, xml, xsl, css, scss, sass, less and stylus.\nThe option \"always\" applies to all parts of the file regardless of markup/css.", + "emmetShowAbbreviationSuggestions": "Shows possible Emmet abbreviations as suggestions. Not applicable in stylesheets or when emmet.showExpandedAbbreviation is set to \"never\".", + "emmetIncludeLanguages": "Enable Emmet abbreviations in languages that are not supported by default. Add a mapping here between the language and emmet supported language.\n Eg: {\"vue-html\": \"html\", \"javascript\": \"javascriptreact\"}", + "emmetVariables": "Variables to be used in Emmet snippets", + "emmetTriggerExpansionOnTab": "When enabled, Emmet abbreviations are expanded when pressing TAB.", "emmetPreferences": "Preferences used to modify behavior of some actions and resolvers of Emmet.", "emmetPreferencesIntUnit": "Default unit for integer values", "emmetPreferencesFloatUnit": "Default unit for float values", @@ -39,10 +39,10 @@ "emmetPreferencesCssBetween": "Symbol to be placed at the between CSS property and value when expanding CSS abbreviations", "emmetPreferencesSassBetween": "Symbol to be placed at the between CSS property and value when expanding CSS abbreviations in Sass files", "emmetPreferencesStylusBetween": "Symbol to be placed at the between CSS property and value when expanding CSS abbreviations in Stylus files", - "emmetShowSuggestionsAsSnippets": "If true, then emmet suggestions will show up as snippets allowing you to order them as per editor.snippetSuggestions setting.", - "emmetPreferencesBemElementSeparator": "Element separator used for classes when using the bem filter", - "emmetPreferencesBemModifierSeparator": "Modifer separator used for classes when using the bem filter", - "emmetPreferencesFilterCommentBefore": "A definition of comment that should be placed before after element when comment filter is applied.", - "emmetPreferencesFilterCommentAfter": "A definition of comment that should be placed before matched element when comment filter is applied.", + "emmetShowSuggestionsAsSnippets": "If true, then Emmet suggestions will show up as snippets allowing you to order them as per editor.snippetSuggestions setting.", + "emmetPreferencesBemElementSeparator": "Element separator used for classes when using the BEM filter", + "emmetPreferencesBemModifierSeparator": "Modifier separator used for classes when using the BEM filter", + "emmetPreferencesFilterCommentBefore": "A definition of comment that should be placed before matched element when comment filter is applied.", + "emmetPreferencesFilterCommentAfter": "A definition of comment that should be placed after matched element when comment filter is applied.", "emmetPreferencesFilterCommentTrigger": "A comma-separated list of attribute names that should exist in abbreviation for the comment filter to be applied" -} \ No newline at end of file +} diff --git a/extensions/git/package.json b/extensions/git/package.json index abb7f5400ea..a69c16b2429 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -850,6 +850,15 @@ "dark": "#73C991", "highContrast": "#73C991" } + }, + { + "id": "git.color.ignored", + "description": "Color for ignored resources", + "defaults": { + "light": "#8E8E90", + "dark": "#A7A8A9", + "highContrast": "#A7A8A9" + } } ] }, @@ -864,4 +873,4 @@ "@types/node": "7.0.43", "mocha": "^3.2.0" } -} \ No newline at end of file +} diff --git a/extensions/git/src/decorationProvider.ts b/extensions/git/src/decorationProvider.ts new file mode 100644 index 00000000000..813a035b519 --- /dev/null +++ b/extensions/git/src/decorationProvider.ts @@ -0,0 +1,151 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { window, workspace, Uri, Disposable, Event, EventEmitter, DecorationData, DecorationProvider, ThemeColor } from 'vscode'; +import { Repository, GitResourceGroup } from './repository'; +import { Model } from './model'; +import { debounce } from './decorators'; +import { filterEvent } from './util'; + +class GitIgnoreDecorationProvider implements DecorationProvider { + + private readonly _onDidChangeDecorations = new EventEmitter(); + readonly onDidChangeDecorations: Event = this._onDidChangeDecorations.event; + + private checkIgnoreQueue = new Map void, reject: (err: any) => void }>(); + private disposables: Disposable[] = []; + + constructor(private repository: Repository) { + this.disposables.push( + window.registerDecorationProvider(this), + filterEvent(workspace.onDidSaveTextDocument, e => e.fileName.endsWith('.gitignore'))(_ => this._onDidChangeDecorations.fire()) + //todo@joh -> events when the ignore status actually changes, not only when the file changes + ); + } + + dispose(): void { + this.disposables.forEach(d => d.dispose()); + this.checkIgnoreQueue.clear(); + } + + provideDecoration(uri: Uri): Promise { + return new Promise((resolve, reject) => { + this.checkIgnoreQueue.set(uri.fsPath, { resolve, reject }); + this.checkIgnoreSoon(); + }).then(ignored => { + if (ignored) { + return { + priority: 3, + color: new ThemeColor('git.color.ignored') + }; + } + }); + } + + @debounce(500) + private checkIgnoreSoon(): void { + const queue = new Map(this.checkIgnoreQueue.entries()); + this.checkIgnoreQueue.clear(); + this.repository.checkIgnore([...queue.keys()]).then(ignoreSet => { + for (const [key, value] of queue.entries()) { + value.resolve(ignoreSet.has(key)); + } + }, err => { + console.error(err); + for (const [, value] of queue.entries()) { + value.reject(err); + } + }); + } +} + +class GitDecorationProvider implements DecorationProvider { + + private readonly _onDidChangeDecorations = new EventEmitter(); + readonly onDidChangeDecorations: Event = this._onDidChangeDecorations.event; + + private disposables: Disposable[] = []; + private decorations = new Map(); + + constructor(private repository: Repository) { + this.disposables.push( + window.registerDecorationProvider(this), + repository.onDidRunOperation(this.onDidRunOperation, this) + ); + } + + private onDidRunOperation(): void { + let newDecorations = new Map(); + this.collectDecorationData(this.repository.indexGroup, newDecorations); + this.collectDecorationData(this.repository.workingTreeGroup, newDecorations); + + let uris: Uri[] = []; + newDecorations.forEach((value, uriString) => { + if (this.decorations.has(uriString)) { + this.decorations.delete(uriString); + } else { + uris.push(Uri.parse(uriString)); + } + }); + this.decorations.forEach((value, uriString) => { + uris.push(Uri.parse(uriString)); + }); + this.decorations = newDecorations; + this._onDidChangeDecorations.fire(uris); + } + + private collectDecorationData(group: GitResourceGroup, bucket: Map): void { + group.resourceStates.forEach(r => { + if (r.resourceDecoration) { + bucket.set(r.original.toString(), r.resourceDecoration); + } + }); + } + + provideDecoration(uri: Uri): DecorationData | undefined { + return this.decorations.get(uri.toString()); + } + + dispose(): void { + this.disposables.forEach(d => d.dispose()); + } +} + + +export class GitDecorations { + + private disposables: Disposable[] = []; + private providers = new Map(); + + constructor(private model: Model) { + this.disposables.push( + model.onDidOpenRepository(this.onDidOpenRepository, this), + model.onDidCloseRepository(this.onDidCloseRepository, this) + ); + model.repositories.forEach(this.onDidOpenRepository, this); + } + + private onDidOpenRepository(repository: Repository): void { + const provider = new GitDecorationProvider(repository); + const ignoreProvider = new GitIgnoreDecorationProvider(repository); + this.providers.set(repository, Disposable.from(provider, ignoreProvider)); + } + + private onDidCloseRepository(repository: Repository): void { + const provider = this.providers.get(repository); + if (provider) { + provider.dispose(); + this.providers.delete(repository); + } + } + + dispose(): void { + this.disposables.forEach(d => d.dispose()); + this.providers.forEach(value => value.dispose); + this.providers.clear(); + } +} diff --git a/extensions/git/src/main.ts b/extensions/git/src/main.ts index acc30eeb81d..b090495be71 100644 --- a/extensions/git/src/main.ts +++ b/extensions/git/src/main.ts @@ -12,6 +12,7 @@ import { findGit, Git, IGit } from './git'; import { Model } from './model'; import { CommandCenter } from './commands'; import { GitContentProvider } from './contentProvider'; +import { GitDecorations } from './decorationProvider'; import { Askpass } from './askpass'; import { toDisposable } from './util'; import TelemetryReporter from 'vscode-extension-telemetry'; @@ -54,6 +55,7 @@ async function init(context: ExtensionContext, disposables: Disposable[]): Promi disposables.push( new CommandCenter(git, model, outputChannel, telemetryReporter), new GitContentProvider(model), + new GitDecorations(model) ); await checkGitVersion(info); @@ -93,4 +95,4 @@ async function checkGitVersion(info: IGit): Promise { } else if (choice === neverShowAgain) { await config.update('ignoreLegacyWarning', true, true); } -} \ No newline at end of file +} diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 6c9f781c58f..04e7dc49012 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -5,8 +5,8 @@ 'use strict'; -import { Uri, Command, EventEmitter, Event, scm, SourceControl, SourceControlInputBox, SourceControlResourceGroup, SourceControlResourceState, SourceControlResourceDecorations, Disposable, ProgressLocation, window, workspace, WorkspaceEdit, ThemeColor } from 'vscode'; -import { Repository as BaseRepository, Ref, Branch, Remote, Commit, GitErrorCodes, Stash, RefType } from './git'; +import { Uri, Command, EventEmitter, Event, scm, SourceControl, SourceControlInputBox, SourceControlResourceGroup, SourceControlResourceState, SourceControlResourceDecorations, Disposable, ProgressLocation, window, workspace, WorkspaceEdit, ThemeColor, DecorationData } from 'vscode'; +import { Repository as BaseRepository, Ref, Branch, Remote, Commit, GitErrorCodes, Stash, RefType, GitError } from './git'; import { anyEvent, filterEvent, eventToPromise, dispose, find } from './util'; import { memoize, throttle, debounce } from './decorators'; import { toGitUri } from './uri'; @@ -170,27 +170,27 @@ export class Resource implements SourceControlResourceState { // return this.resourceUri.fsPath.substr(0, workspaceRootPath.length) !== workspaceRootPath; } - private get color(): ThemeColor | undefined { - switch (this.type) { - case Status.INDEX_MODIFIED: - case Status.MODIFIED: - return new ThemeColor('git.color.modified'); - case Status.UNTRACKED: - return new ThemeColor('git.color.untracked'); - default: - return undefined; - } - } - get decorations(): SourceControlResourceDecorations { const light = { iconPath: this.getIconPath('light') }; const dark = { iconPath: this.getIconPath('dark') }; const tooltip = this.tooltip; const strikeThrough = this.strikeThrough; const faded = this.faded; - const color = this.color; - return { strikeThrough, faded, tooltip, light, dark, color }; + return { strikeThrough, faded, tooltip, light, dark }; + } + + get resourceDecoration(): DecorationData | undefined { + const title = this.tooltip; + switch (this.type) { + case Status.UNTRACKED: + return { priority: 1, title, abbreviation: localize('untracked, short', "U"), bubble: true, color: new ThemeColor('git.color.untracked') }; + case Status.INDEX_MODIFIED: + case Status.MODIFIED: + return { priority: 2, title, abbreviation: localize('modified, short', "M"), bubble: true, color: new ThemeColor('git.color.modified') }; + default: + return undefined; + } } constructor( @@ -221,7 +221,8 @@ export enum Operation { Merge = 1 << 16, Ignore = 1 << 17, Tag = 1 << 18, - Stash = 1 << 19 + Stash = 1 << 19, + CheckIgnore = 1 << 20 } // function getOperationName(operation: Operation): string { @@ -250,6 +251,7 @@ function isReadOnly(operation: Operation): boolean { switch (operation) { case Operation.Show: case Operation.GetCommitTemplate: + case Operation.CheckIgnore: return true; default: return false; @@ -644,6 +646,49 @@ export class Repository implements Disposable { }); } + checkIgnore(filePaths: string[]): Promise> { + return this.run(Operation.CheckIgnore, () => { + return new Promise>((resolve, reject) => { + + filePaths = filePaths.filter(filePath => !path.relative(this.root, filePath).startsWith('..')); + + if (filePaths.length === 0) { + // nothing left + return Promise.resolve(new Set()); + } + + const child = this.repository.stream(['check-ignore', ...filePaths]); + + const onExit = exitCode => { + if (exitCode === 1) { + // nothing ignored + resolve(new Set()); + } else if (exitCode === 0) { + // each line is something ignored + resolve(new Set(data.split('\n'))); + } else { + reject(new GitError({ stdout: data, stderr, exitCode })); + } + }; + + let data = ''; + const onStdoutData = (raw: string) => { + data += raw; + }; + + child.stdout.setEncoding('utf8'); + child.stdout.on('data', onStdoutData); + + let stderr: string = ''; + child.stderr.setEncoding('utf8'); + child.stderr.on('data', raw => stderr += raw); + + child.on('error', reject); + child.on('exit', onExit); + }); + }); + } + private async run(operation: Operation, runOperation: () => Promise = () => Promise.resolve(null)): Promise { if (this.state !== RepositoryState.Idle) { throw new Error('Repository not initialized'); diff --git a/extensions/html/client/src/htmlMain.ts b/extensions/html/client/src/htmlMain.ts index 2dc9899c125..4f355644a04 100644 --- a/extensions/html/client/src/htmlMain.ts +++ b/extensions/html/client/src/htmlMain.ts @@ -83,10 +83,10 @@ export function activate(context: ExtensionContext) { }); }); }, - provideColorPresentations(document: TextDocument, colorInfo: ColorInformation): Thenable { + provideColorPresentations(color, context): Thenable { let params: ColorPresentationParams = { - textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document), - colorInfo: { range: client.code2ProtocolConverter.asRange(colorInfo.range), color: colorInfo.color } + textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(context.document), + colorInfo: { range: client.code2ProtocolConverter.asRange(context.range), color } }; return client.sendRequest(ColorPresentationRequest.type, params).then(presentations => { return presentations.map(p => { @@ -175,4 +175,4 @@ function getPackageInfo(context: ExtensionContext): IPackageInfo { }; } return null; -} \ No newline at end of file +} diff --git a/extensions/java/language-configuration.json b/extensions/java/language-configuration.json index 5ae0cec3192..cc45a8a0139 100644 --- a/extensions/java/language-configuration.json +++ b/extensions/java/language-configuration.json @@ -23,5 +23,11 @@ ["\"", "\""], ["'", "'"], ["<", ">"] - ] + ], + "folding": { + "markers": { + "start": "^\\s*//\\s*(#?region\\b)|()" + } + } } diff --git a/extensions/javascript/package.json b/extensions/javascript/package.json index 4d7fa6dff20..866167e1ba6 100644 --- a/extensions/javascript/package.json +++ b/extensions/javascript/package.json @@ -68,6 +68,7 @@ "embeddedLanguages": { "meta.tag.js": "jsx-tags", "meta.tag.without-attributes.js": "jsx-tags", + "meta.tag.attributes.js.jsx": "javascriptreact", "meta.embedded.expression.js": "javascriptreact" } }, @@ -78,6 +79,7 @@ "embeddedLanguages": { "meta.tag.js": "jsx-tags", "meta.tag.without-attributes.js": "jsx-tags", + "meta.tag.attributes.js": "javascript", "meta.embedded.expression.js": "javascript" } }, diff --git a/extensions/javascript/syntaxes/JavaScript.tmLanguage.json b/extensions/javascript/syntaxes/JavaScript.tmLanguage.json index 466cb34a314..7b1ec3d76c5 100644 --- a/extensions/javascript/syntaxes/JavaScript.tmLanguage.json +++ b/extensions/javascript/syntaxes/JavaScript.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/5955a5aed3d8d2862c614f2137d22f2334d490e9", + "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/824f47ea6e98590ac2e75db5bebdf6eff71421ad", "name": "JavaScript (with React support)", "scopeName": "source.js", "fileTypes": [ @@ -278,7 +278,7 @@ "patterns": [ { "name": "meta.var-single-variable.expr.js", - "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n # sure shot arrow functions even if => is on new line\n(\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\((\\s*[_$[:alpha:]\\{\\(]([^()]|\\((\\s*[_$[:alpha:]\\{\\(]\\{\\(][^()]*)?\\))*)?\\) # parameteres\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)))", + "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n # sure shot arrow functions even if => is on new line\n(\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*[_$[:alpha:]\\{\\(\\[]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(\\[]([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\((\\s*[_$[:alpha:]\\{\\(]([^()]|\\((\\s*[_$[:alpha:]\\{\\(]\\{\\(][^()]*)?\\))*)?\\) # parameteres\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)))", "beginCaptures": { "1": { "name": "meta.definition.variable.js entity.name.function.js" @@ -512,7 +512,7 @@ } }, { - "match": "(?x)(?:\\s*\\b(public|private|protected|readonly)\\s+)?(\\.\\.\\.)?\\s*(?)\n )) |\n ((async\\s*)?(\n # sure shot arrow functions even if => is on new line\n(\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\((\\s*[_$[:alpha:]\\{\\(]([^()]|\\((\\s*[_$[:alpha:]\\{\\(]\\{\\(][^()]*)?\\))*)?\\) # parameteres\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)))", + "match": "(?x)(?:\\s*\\b(public|private|protected|readonly)\\s+)?(\\.\\.\\.)?\\s*(?)\n )) |\n ((async\\s*)?(\n # sure shot arrow functions even if => is on new line\n(\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*[_$[:alpha:]\\{\\(\\[]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(\\[]([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\((\\s*[_$[:alpha:]\\{\\(]([^()]|\\((\\s*[_$[:alpha:]\\{\\(]\\{\\(][^()]*)?\\))*)?\\) # parameteres\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)))", "captures": { "1": { "name": "storage.modifier.js" @@ -739,7 +739,7 @@ }, { "name": "meta.definition.property.js entity.name.function.js", - "match": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=(\\?\\s*)?\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n # sure shot arrow functions even if => is on new line\n(\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\((\\s*[_$[:alpha:]\\{\\(]([^()]|\\((\\s*[_$[:alpha:]\\{\\(]\\{\\(][^()]*)?\\))*)?\\) # parameteres\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)))" + "match": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=(\\?\\s*)?\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n # sure shot arrow functions even if => is on new line\n(\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*[_$[:alpha:]\\{\\(\\[]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(\\[]([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\((\\s*[_$[:alpha:]\\{\\(]([^()]|\\((\\s*[_$[:alpha:]\\{\\(]\\{\\(][^()]*)?\\))*)?\\) # parameteres\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)))" }, { "name": "meta.definition.property.js variable.object.property.js", @@ -1002,7 +1002,7 @@ }, { "name": "meta.arrow.js", - "begin": "(?x) (?:\n (? is on new line\n(\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\((\\s*[_$[:alpha:]\\{\\(]([^()]|\\((\\s*[_$[:alpha:]\\{\\(]\\{\\(][^()]*)?\\))*)?\\) # parameteres\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n )\n)", + "begin": "(?x) (?:\n (? is on new line\n(\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*[_$[:alpha:]\\{\\(\\[]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(\\[]([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\((\\s*[_$[:alpha:]\\{\\(]([^()]|\\((\\s*[_$[:alpha:]\\{\\(]\\{\\(][^()]*)?\\))*)?\\) # parameteres\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n )\n)", "beginCaptures": { "1": { "name": "storage.modifier.async.js" @@ -1707,8 +1707,7 @@ "include": "#comment" }, { - "comment": "(default|*|name) as alias", - "match": "(?x) (?: \\b(default)\\b | (\\*) | ([_$[:alpha:]][_$[:alnum:]]*)) \\s+\n (as) \\s+ (?: (\\b default \\b | \\*) | ([_$[:alpha:]][_$[:alnum:]]*))", + "match": "(?)\n )) |\n ((async\\s*)?(\n # sure shot arrow functions even if => is on new line\n(\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\((\\s*[_$[:alpha:]\\{\\(]([^()]|\\((\\s*[_$[:alpha:]\\{\\(]\\{\\(][^()]*)?\\))*)?\\) # parameteres\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=:\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n # sure shot arrow functions even if => is on new line\n(\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*[_$[:alpha:]\\{\\(\\[]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(\\[]([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\((\\s*[_$[:alpha:]\\{\\(]([^()]|\\((\\s*[_$[:alpha:]\\{\\(]\\{\\(][^()]*)?\\))*)?\\) # parameteres\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "0": { "name": "meta.object-literal.key.js" @@ -2043,13 +2039,13 @@ ] }, "function-call": { - "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\.\\s*)*|(\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)?\\()", - "end": "(?<=\\))(?!(([_$[:alpha:]][_$[:alnum:]]*\\s*\\.\\s*)*|(\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)?\\()", + "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\.\\s*)*|(\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(<\\s*[_$[:alpha:]\\{\\(\\[]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(\\[]([^=<>]|=[^<])+\\>)+>\\s*)?\\()", + "end": "(?<=\\))(?!(([_$[:alpha:]][_$[:alnum:]]*\\s*\\.\\s*)*|(\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(<\\s*[_$[:alpha:]\\{\\(\\[]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(\\[]([^=<>]|=[^<])+\\>)+>\\s*)?\\()", "patterns": [ { "name": "meta.function-call.js", "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\.\\s*)*|(\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*))", - "end": "(?=\\s*(<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)?\\()", + "end": "(?=\\s*(<\\s*[_$[:alpha:]\\{\\(\\[]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(\\[]([^=<>]|=[^<])+\\>)+>\\s*)?\\()", "patterns": [ { "include": "#literal" @@ -2496,7 +2492,7 @@ } }, { - "match": "(?x) (\\.) \\s* (?:\n (ATTRIBUTE_NODE|CDATA_SECTION_NODE|COMMENT_NODE|DOCUMENT_FRAGMENT_NODE|DOCUMENT_NODE|DOCUMENT_TYPE_NODE\n |DOMSTRING_SIZE_ERR|ELEMENT_NODE|ENTITY_NODE|ENTITY_REFERENCE_NODE|HIERARCHY_REQUEST_ERR|INDEX_SIZE_ERR\n |INUSE_ATTRIBUTE_ERR|INVALID_CHARACTER_ERR|NO_DATA_ALLOWED_ERR|NO_MODIFICATION_ALLOWED_ERR|NOT_FOUND_ERR\n |NOT_SUPPORTED_ERR|NOTATION_NODE|PROCESSING_INSTRUCTION_NODE|TEXT_NODE|WRONG_DOCUMENT_ERR)\n |\n (_content|[xyz]|abbr|above|accept|acceptCharset|accessKey|action|align|[av]Link(?:color)?|all|alt|anchors|appCodeName\n |appCore|applets|appMinorVersion|appName|appVersion|archive|areas|arguments|attributes|availHeight|availLeft|availTop\n |availWidth|axis|background|backgroundColor|backgroundImage|below|bgColor|body|border|borderBottomWidth|borderColor\n |borderLeftWidth|borderRightWidth|borderStyle|borderTopWidth|borderWidth|bottom|bufferDepth|callee|caller|caption\n |cellPadding|cells|cellSpacing|ch|characterSet|charset|checked|childNodes|chOff|cite|classes|className|clear\n |clientInformation|clip|clipBoardData|closed|code|codeBase|codeType|color|colorDepth|cols|colSpan|compact|complete\n |components|content|controllers|cookie|cookieEnabled|cords|cpuClass|crypto|current|data|dateTime|declare|defaultCharset\n |defaultChecked|defaultSelected|defaultStatus|defaultValue|defaultView|defer|description|dialogArguments|dialogHeight\n |dialogLeft|dialogTop|dialogWidth|dir|directories|disabled|display|docmain|doctype|documentElement|elements|embeds\n |enabledPlugin|encoding|enctype|entities|event|expando|external|face|fgColor|filename|firstChild|fontFamily|fontSize\n |fontWeight|form|formName|forms|frame|frameBorder|frameElement|frames|hasFocus|hash|headers|height|history|host\n |hostname|href|hreflang|hspace|htmlFor|httpEquiv|id|ids|ignoreCase|images|implementation|index|innerHeight|innerWidth\n |input|isMap|label|lang|language|lastChild|lastIndex|lastMatch|lastModified|lastParen|layer[sXY]|left|leftContext\n |lineHeight|link|linkColor|links|listStyleType|localName|location|locationbar|longDesc|lowsrc|lowSrc|marginBottom\n |marginHeight|marginLeft|marginRight|marginTop|marginWidth|maxLength|media|menubar|method|mimeTypes|multiline|multiple\n |name|nameProp|namespaces|namespaceURI|next|nextSibling|nodeName|nodeType|nodeValue|noHref|noResize|noShade|notationName\n |notations|noWrap|object|offscreenBuffering|onLine|onreadystatechange|opener|opsProfile|options|oscpu|outerHeight\n |outerWidth|ownerDocument|paddingBottom|paddingLeft|paddingRight|paddingTop|page[XY]|page[XY]Offset|parent|parentLayer\n |parentNode|parentWindow|pathname|personalbar|pixelDepth|pkcs11|platform|plugins|port|prefix|previous|previousDibling\n |product|productSub|profile|profileend|prompt|prompter|protocol|publicId|readOnly|readyState|referrer|rel|responseText\n |responseXML|rev|right|rightContext|rowIndex|rows|rowSpan|rules|scheme|scope|screen[XY]|screenLeft|screenTop|scripts\n |scrollbars|scrolling|sectionRowIndex|security|securityPolicy|selected|selectedIndex|selection|self|shape|siblingAbove\n |siblingBelow|size|source|specified|standby|start|status|statusbar|statusText|style|styleSheets|suffixes|summary\n |systemId|systemLanguage|tagName|tags|target|tBodies|text|textAlign|textDecoration|textIndent|textTransform|tFoot|tHead\n |title|toolbar|top|type|undefined|uniqueID|updateInterval|URL|URLUnencoded|useMap|userAgent|userLanguage|userProfile\n |vAlign|value|valueType|vendor|vendorSub|version|visibility|vspace|whiteSpace|width|X[MS]LDocument|zIndex))\\b(?!\\$|\\s*(<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)?\\()", + "match": "(?x) (\\.) \\s* (?:\n (ATTRIBUTE_NODE|CDATA_SECTION_NODE|COMMENT_NODE|DOCUMENT_FRAGMENT_NODE|DOCUMENT_NODE|DOCUMENT_TYPE_NODE\n |DOMSTRING_SIZE_ERR|ELEMENT_NODE|ENTITY_NODE|ENTITY_REFERENCE_NODE|HIERARCHY_REQUEST_ERR|INDEX_SIZE_ERR\n |INUSE_ATTRIBUTE_ERR|INVALID_CHARACTER_ERR|NO_DATA_ALLOWED_ERR|NO_MODIFICATION_ALLOWED_ERR|NOT_FOUND_ERR\n |NOT_SUPPORTED_ERR|NOTATION_NODE|PROCESSING_INSTRUCTION_NODE|TEXT_NODE|WRONG_DOCUMENT_ERR)\n |\n (_content|[xyz]|abbr|above|accept|acceptCharset|accessKey|action|align|[av]Link(?:color)?|all|alt|anchors|appCodeName\n |appCore|applets|appMinorVersion|appName|appVersion|archive|areas|arguments|attributes|availHeight|availLeft|availTop\n |availWidth|axis|background|backgroundColor|backgroundImage|below|bgColor|body|border|borderBottomWidth|borderColor\n |borderLeftWidth|borderRightWidth|borderStyle|borderTopWidth|borderWidth|bottom|bufferDepth|callee|caller|caption\n |cellPadding|cells|cellSpacing|ch|characterSet|charset|checked|childNodes|chOff|cite|classes|className|clear\n |clientInformation|clip|clipBoardData|closed|code|codeBase|codeType|color|colorDepth|cols|colSpan|compact|complete\n |components|content|controllers|cookie|cookieEnabled|cords|cpuClass|crypto|current|data|dateTime|declare|defaultCharset\n |defaultChecked|defaultSelected|defaultStatus|defaultValue|defaultView|defer|description|dialogArguments|dialogHeight\n |dialogLeft|dialogTop|dialogWidth|dir|directories|disabled|display|docmain|doctype|documentElement|elements|embeds\n |enabledPlugin|encoding|enctype|entities|event|expando|external|face|fgColor|filename|firstChild|fontFamily|fontSize\n |fontWeight|form|formName|forms|frame|frameBorder|frameElement|frames|hasFocus|hash|headers|height|history|host\n |hostname|href|hreflang|hspace|htmlFor|httpEquiv|id|ids|ignoreCase|images|implementation|index|innerHeight|innerWidth\n |input|isMap|label|lang|language|lastChild|lastIndex|lastMatch|lastModified|lastParen|layer[sXY]|left|leftContext\n |lineHeight|link|linkColor|links|listStyleType|localName|location|locationbar|longDesc|lowsrc|lowSrc|marginBottom\n |marginHeight|marginLeft|marginRight|marginTop|marginWidth|maxLength|media|menubar|method|mimeTypes|multiline|multiple\n |name|nameProp|namespaces|namespaceURI|next|nextSibling|nodeName|nodeType|nodeValue|noHref|noResize|noShade|notationName\n |notations|noWrap|object|offscreenBuffering|onLine|onreadystatechange|opener|opsProfile|options|oscpu|outerHeight\n |outerWidth|ownerDocument|paddingBottom|paddingLeft|paddingRight|paddingTop|page[XY]|page[XY]Offset|parent|parentLayer\n |parentNode|parentWindow|pathname|personalbar|pixelDepth|pkcs11|platform|plugins|port|prefix|previous|previousDibling\n |product|productSub|profile|profileend|prompt|prompter|protocol|publicId|readOnly|readyState|referrer|rel|responseText\n |responseXML|rev|right|rightContext|rowIndex|rows|rowSpan|rules|scheme|scope|screen[XY]|screenLeft|screenTop|scripts\n |scrollbars|scrolling|sectionRowIndex|security|securityPolicy|selected|selectedIndex|selection|self|shape|siblingAbove\n |siblingBelow|size|source|specified|standby|start|status|statusbar|statusText|style|styleSheets|suffixes|summary\n |systemId|systemLanguage|tagName|tags|target|tBodies|text|textAlign|textDecoration|textIndent|textTransform|tFoot|tHead\n |title|toolbar|top|type|undefined|uniqueID|updateInterval|URL|URLUnencoded|useMap|userAgent|userLanguage|userProfile\n |vAlign|value|valueType|vendor|vendorSub|version|visibility|vspace|whiteSpace|width|X[MS]LDocument|zIndex))\\b(?!\\$|\\s*(<\\s*[_$[:alpha:]\\{\\(\\[]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(\\[]([^=<>]|=[^<])+\\>)+>\\s*)?\\()", "captures": { "1": { "name": "punctuation.accessor.js" @@ -2576,7 +2572,7 @@ "include": "#object-identifiers" }, { - "match": "(?x)(?:(\\.)\\s*)?([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n # sure shot arrow functions even if => is on new line\n(\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\((\\s*[_$[:alpha:]\\{\\(]([^()]|\\((\\s*[_$[:alpha:]\\{\\(]\\{\\(][^()]*)?\\))*)?\\) # parameteres\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n))", + "match": "(?x)(?:(\\.)\\s*)?([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n # sure shot arrow functions even if => is on new line\n(\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*[_$[:alpha:]\\{\\(\\[]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(\\[]([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\((\\s*[_$[:alpha:]\\{\\(]([^()]|\\((\\s*[_$[:alpha:]\\{\\(]\\{\\(][^()]*)?\\))*)?\\) # parameteres\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n))", "captures": { "1": { "name": "punctuation.accessor.js" @@ -3225,7 +3221,7 @@ "include": "#expression" } ], - "contentName": "meta.embedded.line.tsx" + "contentName": "meta.embedded.line.js" }, "regex": { "patterns": [ @@ -3482,7 +3478,7 @@ } }, "end": "(?=^)", - "contentName": "comment.line.double-slash.tsx" + "contentName": "comment.line.double-slash.js" } ] }, @@ -4020,7 +4016,7 @@ "name": "punctuation.definition.tag.end.js" } }, - "contentName": "meta.jsx.children.tsx", + "contentName": "meta.jsx.children.js", "patterns": [ { "include": "#jsx-children" @@ -4104,6 +4100,7 @@ } }, "end": "(?=[/]?>)", + "contentName": "meta.tag.attributes.js", "patterns": [ { "include": "#comment" @@ -4124,7 +4121,7 @@ } }, "end": "(?=)\n )) |\n ((async\\s*)?(\n # sure shot arrow functions even if => is on new line\n(\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\((\\s*[_$[:alpha:]\\{\\(]([^()]|\\((\\s*[_$[:alpha:]\\{\\(]\\{\\(][^()]*)?\\))*)?\\) # parameteres\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)))", + "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n # sure shot arrow functions even if => is on new line\n(\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*[_$[:alpha:]\\{\\(\\[]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(\\[]([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\((\\s*[_$[:alpha:]\\{\\(]([^()]|\\((\\s*[_$[:alpha:]\\{\\(]\\{\\(][^()]*)?\\))*)?\\) # parameteres\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)))", "beginCaptures": { "1": { "name": "meta.definition.variable.js.jsx entity.name.function.js.jsx" @@ -512,7 +512,7 @@ } }, { - "match": "(?x)(?:\\s*\\b(public|private|protected|readonly)\\s+)?(\\.\\.\\.)?\\s*(?)\n )) |\n ((async\\s*)?(\n # sure shot arrow functions even if => is on new line\n(\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\((\\s*[_$[:alpha:]\\{\\(]([^()]|\\((\\s*[_$[:alpha:]\\{\\(]\\{\\(][^()]*)?\\))*)?\\) # parameteres\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)))", + "match": "(?x)(?:\\s*\\b(public|private|protected|readonly)\\s+)?(\\.\\.\\.)?\\s*(?)\n )) |\n ((async\\s*)?(\n # sure shot arrow functions even if => is on new line\n(\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*[_$[:alpha:]\\{\\(\\[]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(\\[]([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\((\\s*[_$[:alpha:]\\{\\(]([^()]|\\((\\s*[_$[:alpha:]\\{\\(]\\{\\(][^()]*)?\\))*)?\\) # parameteres\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)))", "captures": { "1": { "name": "storage.modifier.js.jsx" @@ -739,7 +739,7 @@ }, { "name": "meta.definition.property.js.jsx entity.name.function.js.jsx", - "match": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=(\\?\\s*)?\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n # sure shot arrow functions even if => is on new line\n(\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\((\\s*[_$[:alpha:]\\{\\(]([^()]|\\((\\s*[_$[:alpha:]\\{\\(]\\{\\(][^()]*)?\\))*)?\\) # parameteres\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)))" + "match": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=(\\?\\s*)?\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n # sure shot arrow functions even if => is on new line\n(\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*[_$[:alpha:]\\{\\(\\[]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(\\[]([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\((\\s*[_$[:alpha:]\\{\\(]([^()]|\\((\\s*[_$[:alpha:]\\{\\(]\\{\\(][^()]*)?\\))*)?\\) # parameteres\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)))" }, { "name": "meta.definition.property.js.jsx variable.object.property.js.jsx", @@ -1002,7 +1002,7 @@ }, { "name": "meta.arrow.js.jsx", - "begin": "(?x) (?:\n (? is on new line\n(\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\((\\s*[_$[:alpha:]\\{\\(]([^()]|\\((\\s*[_$[:alpha:]\\{\\(]\\{\\(][^()]*)?\\))*)?\\) # parameteres\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n )\n)", + "begin": "(?x) (?:\n (? is on new line\n(\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*[_$[:alpha:]\\{\\(\\[]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(\\[]([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\((\\s*[_$[:alpha:]\\{\\(]([^()]|\\((\\s*[_$[:alpha:]\\{\\(]\\{\\(][^()]*)?\\))*)?\\) # parameteres\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n )\n)", "beginCaptures": { "1": { "name": "storage.modifier.async.js.jsx" @@ -1707,8 +1707,7 @@ "include": "#comment" }, { - "comment": "(default|*|name) as alias", - "match": "(?x) (?: \\b(default)\\b | (\\*) | ([_$[:alpha:]][_$[:alnum:]]*)) \\s+\n (as) \\s+ (?: (\\b default \\b | \\*) | ([_$[:alpha:]][_$[:alnum:]]*))", + "match": "(?)\n )) |\n ((async\\s*)?(\n # sure shot arrow functions even if => is on new line\n(\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\((\\s*[_$[:alpha:]\\{\\(]([^()]|\\((\\s*[_$[:alpha:]\\{\\(]\\{\\(][^()]*)?\\))*)?\\) # parameteres\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=:\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n # sure shot arrow functions even if => is on new line\n(\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*[_$[:alpha:]\\{\\(\\[]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(\\[]([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\((\\s*[_$[:alpha:]\\{\\(]([^()]|\\((\\s*[_$[:alpha:]\\{\\(]\\{\\(][^()]*)?\\))*)?\\) # parameteres\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "0": { "name": "meta.object-literal.key.js.jsx" @@ -2043,13 +2039,13 @@ ] }, "function-call": { - "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\.\\s*)*|(\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)?\\()", - "end": "(?<=\\))(?!(([_$[:alpha:]][_$[:alnum:]]*\\s*\\.\\s*)*|(\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)?\\()", + "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\.\\s*)*|(\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(<\\s*[_$[:alpha:]\\{\\(\\[]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(\\[]([^=<>]|=[^<])+\\>)+>\\s*)?\\()", + "end": "(?<=\\))(?!(([_$[:alpha:]][_$[:alnum:]]*\\s*\\.\\s*)*|(\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(<\\s*[_$[:alpha:]\\{\\(\\[]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(\\[]([^=<>]|=[^<])+\\>)+>\\s*)?\\()", "patterns": [ { "name": "meta.function-call.js.jsx", "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\.\\s*)*|(\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*))", - "end": "(?=\\s*(<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)?\\()", + "end": "(?=\\s*(<\\s*[_$[:alpha:]\\{\\(\\[]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(\\[]([^=<>]|=[^<])+\\>)+>\\s*)?\\()", "patterns": [ { "include": "#literal" @@ -2496,7 +2492,7 @@ } }, { - "match": "(?x) (\\.) \\s* (?:\n (ATTRIBUTE_NODE|CDATA_SECTION_NODE|COMMENT_NODE|DOCUMENT_FRAGMENT_NODE|DOCUMENT_NODE|DOCUMENT_TYPE_NODE\n |DOMSTRING_SIZE_ERR|ELEMENT_NODE|ENTITY_NODE|ENTITY_REFERENCE_NODE|HIERARCHY_REQUEST_ERR|INDEX_SIZE_ERR\n |INUSE_ATTRIBUTE_ERR|INVALID_CHARACTER_ERR|NO_DATA_ALLOWED_ERR|NO_MODIFICATION_ALLOWED_ERR|NOT_FOUND_ERR\n |NOT_SUPPORTED_ERR|NOTATION_NODE|PROCESSING_INSTRUCTION_NODE|TEXT_NODE|WRONG_DOCUMENT_ERR)\n |\n (_content|[xyz]|abbr|above|accept|acceptCharset|accessKey|action|align|[av]Link(?:color)?|all|alt|anchors|appCodeName\n |appCore|applets|appMinorVersion|appName|appVersion|archive|areas|arguments|attributes|availHeight|availLeft|availTop\n |availWidth|axis|background|backgroundColor|backgroundImage|below|bgColor|body|border|borderBottomWidth|borderColor\n |borderLeftWidth|borderRightWidth|borderStyle|borderTopWidth|borderWidth|bottom|bufferDepth|callee|caller|caption\n |cellPadding|cells|cellSpacing|ch|characterSet|charset|checked|childNodes|chOff|cite|classes|className|clear\n |clientInformation|clip|clipBoardData|closed|code|codeBase|codeType|color|colorDepth|cols|colSpan|compact|complete\n |components|content|controllers|cookie|cookieEnabled|cords|cpuClass|crypto|current|data|dateTime|declare|defaultCharset\n |defaultChecked|defaultSelected|defaultStatus|defaultValue|defaultView|defer|description|dialogArguments|dialogHeight\n |dialogLeft|dialogTop|dialogWidth|dir|directories|disabled|display|docmain|doctype|documentElement|elements|embeds\n |enabledPlugin|encoding|enctype|entities|event|expando|external|face|fgColor|filename|firstChild|fontFamily|fontSize\n |fontWeight|form|formName|forms|frame|frameBorder|frameElement|frames|hasFocus|hash|headers|height|history|host\n |hostname|href|hreflang|hspace|htmlFor|httpEquiv|id|ids|ignoreCase|images|implementation|index|innerHeight|innerWidth\n |input|isMap|label|lang|language|lastChild|lastIndex|lastMatch|lastModified|lastParen|layer[sXY]|left|leftContext\n |lineHeight|link|linkColor|links|listStyleType|localName|location|locationbar|longDesc|lowsrc|lowSrc|marginBottom\n |marginHeight|marginLeft|marginRight|marginTop|marginWidth|maxLength|media|menubar|method|mimeTypes|multiline|multiple\n |name|nameProp|namespaces|namespaceURI|next|nextSibling|nodeName|nodeType|nodeValue|noHref|noResize|noShade|notationName\n |notations|noWrap|object|offscreenBuffering|onLine|onreadystatechange|opener|opsProfile|options|oscpu|outerHeight\n |outerWidth|ownerDocument|paddingBottom|paddingLeft|paddingRight|paddingTop|page[XY]|page[XY]Offset|parent|parentLayer\n |parentNode|parentWindow|pathname|personalbar|pixelDepth|pkcs11|platform|plugins|port|prefix|previous|previousDibling\n |product|productSub|profile|profileend|prompt|prompter|protocol|publicId|readOnly|readyState|referrer|rel|responseText\n |responseXML|rev|right|rightContext|rowIndex|rows|rowSpan|rules|scheme|scope|screen[XY]|screenLeft|screenTop|scripts\n |scrollbars|scrolling|sectionRowIndex|security|securityPolicy|selected|selectedIndex|selection|self|shape|siblingAbove\n |siblingBelow|size|source|specified|standby|start|status|statusbar|statusText|style|styleSheets|suffixes|summary\n |systemId|systemLanguage|tagName|tags|target|tBodies|text|textAlign|textDecoration|textIndent|textTransform|tFoot|tHead\n |title|toolbar|top|type|undefined|uniqueID|updateInterval|URL|URLUnencoded|useMap|userAgent|userLanguage|userProfile\n |vAlign|value|valueType|vendor|vendorSub|version|visibility|vspace|whiteSpace|width|X[MS]LDocument|zIndex))\\b(?!\\$|\\s*(<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)?\\()", + "match": "(?x) (\\.) \\s* (?:\n (ATTRIBUTE_NODE|CDATA_SECTION_NODE|COMMENT_NODE|DOCUMENT_FRAGMENT_NODE|DOCUMENT_NODE|DOCUMENT_TYPE_NODE\n |DOMSTRING_SIZE_ERR|ELEMENT_NODE|ENTITY_NODE|ENTITY_REFERENCE_NODE|HIERARCHY_REQUEST_ERR|INDEX_SIZE_ERR\n |INUSE_ATTRIBUTE_ERR|INVALID_CHARACTER_ERR|NO_DATA_ALLOWED_ERR|NO_MODIFICATION_ALLOWED_ERR|NOT_FOUND_ERR\n |NOT_SUPPORTED_ERR|NOTATION_NODE|PROCESSING_INSTRUCTION_NODE|TEXT_NODE|WRONG_DOCUMENT_ERR)\n |\n (_content|[xyz]|abbr|above|accept|acceptCharset|accessKey|action|align|[av]Link(?:color)?|all|alt|anchors|appCodeName\n |appCore|applets|appMinorVersion|appName|appVersion|archive|areas|arguments|attributes|availHeight|availLeft|availTop\n |availWidth|axis|background|backgroundColor|backgroundImage|below|bgColor|body|border|borderBottomWidth|borderColor\n |borderLeftWidth|borderRightWidth|borderStyle|borderTopWidth|borderWidth|bottom|bufferDepth|callee|caller|caption\n |cellPadding|cells|cellSpacing|ch|characterSet|charset|checked|childNodes|chOff|cite|classes|className|clear\n |clientInformation|clip|clipBoardData|closed|code|codeBase|codeType|color|colorDepth|cols|colSpan|compact|complete\n |components|content|controllers|cookie|cookieEnabled|cords|cpuClass|crypto|current|data|dateTime|declare|defaultCharset\n |defaultChecked|defaultSelected|defaultStatus|defaultValue|defaultView|defer|description|dialogArguments|dialogHeight\n |dialogLeft|dialogTop|dialogWidth|dir|directories|disabled|display|docmain|doctype|documentElement|elements|embeds\n |enabledPlugin|encoding|enctype|entities|event|expando|external|face|fgColor|filename|firstChild|fontFamily|fontSize\n |fontWeight|form|formName|forms|frame|frameBorder|frameElement|frames|hasFocus|hash|headers|height|history|host\n |hostname|href|hreflang|hspace|htmlFor|httpEquiv|id|ids|ignoreCase|images|implementation|index|innerHeight|innerWidth\n |input|isMap|label|lang|language|lastChild|lastIndex|lastMatch|lastModified|lastParen|layer[sXY]|left|leftContext\n |lineHeight|link|linkColor|links|listStyleType|localName|location|locationbar|longDesc|lowsrc|lowSrc|marginBottom\n |marginHeight|marginLeft|marginRight|marginTop|marginWidth|maxLength|media|menubar|method|mimeTypes|multiline|multiple\n |name|nameProp|namespaces|namespaceURI|next|nextSibling|nodeName|nodeType|nodeValue|noHref|noResize|noShade|notationName\n |notations|noWrap|object|offscreenBuffering|onLine|onreadystatechange|opener|opsProfile|options|oscpu|outerHeight\n |outerWidth|ownerDocument|paddingBottom|paddingLeft|paddingRight|paddingTop|page[XY]|page[XY]Offset|parent|parentLayer\n |parentNode|parentWindow|pathname|personalbar|pixelDepth|pkcs11|platform|plugins|port|prefix|previous|previousDibling\n |product|productSub|profile|profileend|prompt|prompter|protocol|publicId|readOnly|readyState|referrer|rel|responseText\n |responseXML|rev|right|rightContext|rowIndex|rows|rowSpan|rules|scheme|scope|screen[XY]|screenLeft|screenTop|scripts\n |scrollbars|scrolling|sectionRowIndex|security|securityPolicy|selected|selectedIndex|selection|self|shape|siblingAbove\n |siblingBelow|size|source|specified|standby|start|status|statusbar|statusText|style|styleSheets|suffixes|summary\n |systemId|systemLanguage|tagName|tags|target|tBodies|text|textAlign|textDecoration|textIndent|textTransform|tFoot|tHead\n |title|toolbar|top|type|undefined|uniqueID|updateInterval|URL|URLUnencoded|useMap|userAgent|userLanguage|userProfile\n |vAlign|value|valueType|vendor|vendorSub|version|visibility|vspace|whiteSpace|width|X[MS]LDocument|zIndex))\\b(?!\\$|\\s*(<\\s*[_$[:alpha:]\\{\\(\\[]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(\\[]([^=<>]|=[^<])+\\>)+>\\s*)?\\()", "captures": { "1": { "name": "punctuation.accessor.js.jsx" @@ -2576,7 +2572,7 @@ "include": "#object-identifiers" }, { - "match": "(?x)(?:(\\.)\\s*)?([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n # sure shot arrow functions even if => is on new line\n(\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\((\\s*[_$[:alpha:]\\{\\(]([^()]|\\((\\s*[_$[:alpha:]\\{\\(]\\{\\(][^()]*)?\\))*)?\\) # parameteres\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n))", + "match": "(?x)(?:(\\.)\\s*)?([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n # sure shot arrow functions even if => is on new line\n(\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*[_$[:alpha:]\\{\\(\\[]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(\\[]([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\((\\s*[_$[:alpha:]\\{\\(]([^()]|\\((\\s*[_$[:alpha:]\\{\\(]\\{\\(][^()]*)?\\))*)?\\) # parameteres\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n))", "captures": { "1": { "name": "punctuation.accessor.js.jsx" @@ -3225,7 +3221,7 @@ "include": "#expression" } ], - "contentName": "meta.embedded.line.tsx" + "contentName": "meta.embedded.line.js.jsx" }, "regex": { "patterns": [ @@ -3482,7 +3478,7 @@ } }, "end": "(?=^)", - "contentName": "comment.line.double-slash.tsx" + "contentName": "comment.line.double-slash.js.jsx" } ] }, @@ -4020,7 +4016,7 @@ "name": "punctuation.definition.tag.end.js.jsx" } }, - "contentName": "meta.jsx.children.tsx", + "contentName": "meta.jsx.children.js.jsx", "patterns": [ { "include": "#jsx-children" @@ -4104,6 +4100,7 @@ } }, "end": "(?=[/]?>)", + "contentName": "meta.tag.attributes.js.jsx", "patterns": [ { "include": "#comment" @@ -4124,7 +4121,7 @@ } }, "end": "(?=", - "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.tsx meta.tag.without-attributes.js.jsx punctuation.definition.tag.end.js.jsx", + "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.js.jsx meta.tag.without-attributes.js.jsx punctuation.definition.tag.end.js.jsx", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -1651,7 +1651,7 @@ }, { "c": "Hello ", - "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.tsx meta.tag.without-attributes.js.jsx meta.jsx.children.tsx", + "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.js.jsx", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1662,7 +1662,7 @@ }, { "c": "{", - "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.tsx meta.tag.without-attributes.js.jsx meta.jsx.children.tsx meta.embedded.expression.js.jsx punctuation.section.embedded.begin.js.jsx", + "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.js.jsx meta.embedded.expression.js.jsx punctuation.section.embedded.begin.js.jsx", "r": { "dark_plus": "punctuation.section.embedded: #569CD6", "light_plus": "punctuation.section.embedded: #0000FF", @@ -1673,7 +1673,7 @@ }, { "c": "message", - "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.tsx meta.tag.without-attributes.js.jsx meta.jsx.children.tsx meta.embedded.expression.js.jsx variable.other.readwrite.js.jsx", + "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.js.jsx meta.embedded.expression.js.jsx variable.other.readwrite.js.jsx", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -1684,7 +1684,7 @@ }, { "c": "}", - "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.tsx meta.tag.without-attributes.js.jsx meta.jsx.children.tsx meta.embedded.expression.js.jsx punctuation.section.embedded.end.js.jsx", + "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.js.jsx meta.embedded.expression.js.jsx punctuation.section.embedded.end.js.jsx", "r": { "dark_plus": "punctuation.section.embedded: #569CD6", "light_plus": "punctuation.section.embedded: #0000FF", @@ -1695,7 +1695,7 @@ }, { "c": "!", - "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.tsx meta.tag.without-attributes.js.jsx meta.jsx.children.tsx", + "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.js.jsx", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1706,7 +1706,7 @@ }, { "c": "", - "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.tsx meta.tag.without-attributes.js.jsx punctuation.definition.tag.end.js.jsx", + "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.js.jsx meta.tag.without-attributes.js.jsx punctuation.definition.tag.end.js.jsx", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -1739,7 +1739,7 @@ }, { "c": " ", - "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.tsx", + "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.js.jsx", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1750,7 +1750,7 @@ }, { "c": "<", - "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.tsx meta.tag.js.jsx punctuation.definition.tag.begin.js.jsx", + "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.js.jsx meta.tag.js.jsx punctuation.definition.tag.begin.js.jsx", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -1761,7 +1761,7 @@ }, { "c": "a", - "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.tsx meta.tag.js.jsx entity.name.tag.js.jsx", + "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.js.jsx meta.tag.js.jsx entity.name.tag.js.jsx", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -1772,7 +1772,7 @@ }, { "c": " ", - "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.tsx meta.tag.js.jsx", + "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.js.jsx meta.tag.js.jsx meta.tag.attributes.js.jsx", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1783,7 +1783,7 @@ }, { "c": "href", - "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.tsx meta.tag.js.jsx entity.other.attribute-name.js.jsx", + "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.js.jsx meta.tag.js.jsx meta.tag.attributes.js.jsx entity.other.attribute-name.js.jsx", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", "light_plus": "entity.other.attribute-name: #FF0000", @@ -1794,7 +1794,7 @@ }, { "c": "=", - "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.tsx meta.tag.js.jsx keyword.operator.assignment.js.jsx", + "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.js.jsx meta.tag.js.jsx meta.tag.attributes.js.jsx keyword.operator.assignment.js.jsx", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1805,7 +1805,7 @@ }, { "c": "\"", - "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.tsx meta.tag.js.jsx string.quoted.double.js.jsx punctuation.definition.string.begin.js.jsx", + "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.js.jsx meta.tag.js.jsx meta.tag.attributes.js.jsx string.quoted.double.js.jsx punctuation.definition.string.begin.js.jsx", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1816,7 +1816,7 @@ }, { "c": "\"", - "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.tsx meta.tag.js.jsx string.quoted.double.js.jsx punctuation.definition.string.end.js.jsx", + "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.js.jsx meta.tag.js.jsx meta.tag.attributes.js.jsx string.quoted.double.js.jsx punctuation.definition.string.end.js.jsx", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1827,7 +1827,7 @@ }, { "c": " ", - "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.tsx meta.tag.js.jsx", + "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.js.jsx meta.tag.js.jsx meta.tag.attributes.js.jsx", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1838,7 +1838,7 @@ }, { "c": "onClick", - "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.tsx meta.tag.js.jsx entity.other.attribute-name.js.jsx", + "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.js.jsx meta.tag.js.jsx meta.tag.attributes.js.jsx entity.other.attribute-name.js.jsx", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", "light_plus": "entity.other.attribute-name: #FF0000", @@ -1849,7 +1849,7 @@ }, { "c": "=", - "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.tsx meta.tag.js.jsx keyword.operator.assignment.js.jsx", + "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.js.jsx meta.tag.js.jsx meta.tag.attributes.js.jsx keyword.operator.assignment.js.jsx", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1860,7 +1860,7 @@ }, { "c": "{", - "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.tsx meta.tag.js.jsx meta.embedded.expression.js.jsx punctuation.section.embedded.begin.js.jsx", + "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.js.jsx meta.tag.js.jsx meta.tag.attributes.js.jsx meta.embedded.expression.js.jsx punctuation.section.embedded.begin.js.jsx", "r": { "dark_plus": "punctuation.section.embedded: #569CD6", "light_plus": "punctuation.section.embedded: #0000FF", @@ -1871,7 +1871,7 @@ }, { "c": "this", - "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.tsx meta.tag.js.jsx meta.embedded.expression.js.jsx variable.language.this.js.jsx", + "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.js.jsx meta.tag.js.jsx meta.tag.attributes.js.jsx meta.embedded.expression.js.jsx variable.language.this.js.jsx", "r": { "dark_plus": "variable.language: #569CD6", "light_plus": "variable.language: #0000FF", @@ -1882,7 +1882,7 @@ }, { "c": ".", - "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.tsx meta.tag.js.jsx meta.embedded.expression.js.jsx punctuation.accessor.js.jsx", + "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.js.jsx meta.tag.js.jsx meta.tag.attributes.js.jsx meta.embedded.expression.js.jsx punctuation.accessor.js.jsx", "r": { "dark_plus": "meta.embedded: #D4D4D4", "light_plus": "meta.embedded: #000000", @@ -1893,7 +1893,7 @@ }, { "c": "toggle", - "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.tsx meta.tag.js.jsx meta.embedded.expression.js.jsx variable.other.property.js.jsx", + "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.js.jsx meta.tag.js.jsx meta.tag.attributes.js.jsx meta.embedded.expression.js.jsx variable.other.property.js.jsx", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -1904,7 +1904,7 @@ }, { "c": "}", - "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.tsx meta.tag.js.jsx meta.embedded.expression.js.jsx punctuation.section.embedded.end.js.jsx", + "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.js.jsx meta.tag.js.jsx meta.tag.attributes.js.jsx meta.embedded.expression.js.jsx punctuation.section.embedded.end.js.jsx", "r": { "dark_plus": "punctuation.section.embedded: #569CD6", "light_plus": "punctuation.section.embedded: #0000FF", @@ -1915,7 +1915,7 @@ }, { "c": ">", - "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.tsx meta.tag.js.jsx punctuation.definition.tag.end.js.jsx", + "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.js.jsx meta.tag.js.jsx punctuation.definition.tag.end.js.jsx", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -1926,7 +1926,7 @@ }, { "c": "Toggle", - "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.tsx meta.tag.js.jsx meta.jsx.children.tsx", + "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.js.jsx meta.tag.js.jsx meta.jsx.children.js.jsx", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1937,7 +1937,7 @@ }, { "c": "", - "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.tsx meta.tag.js.jsx punctuation.definition.tag.end.js.jsx", + "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.js.jsx meta.tag.js.jsx punctuation.definition.tag.end.js.jsx", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -1970,7 +1970,7 @@ }, { "c": " ", - "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.tsx", + "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx meta.tag.without-attributes.js.jsx meta.jsx.children.js.jsx", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2168,7 +2168,7 @@ }, { "c": " ", - "t": "source.js.jsx meta.tag.js.jsx", + "t": "source.js.jsx meta.tag.js.jsx meta.tag.attributes.js.jsx", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2179,7 +2179,7 @@ }, { "c": "default", - "t": "source.js.jsx meta.tag.js.jsx entity.other.attribute-name.js.jsx", + "t": "source.js.jsx meta.tag.js.jsx meta.tag.attributes.js.jsx entity.other.attribute-name.js.jsx", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", "light_plus": "entity.other.attribute-name: #FF0000", @@ -2190,7 +2190,7 @@ }, { "c": "=", - "t": "source.js.jsx meta.tag.js.jsx keyword.operator.assignment.js.jsx", + "t": "source.js.jsx meta.tag.js.jsx meta.tag.attributes.js.jsx keyword.operator.assignment.js.jsx", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -2201,7 +2201,7 @@ }, { "c": "\"", - "t": "source.js.jsx meta.tag.js.jsx string.quoted.double.js.jsx punctuation.definition.string.begin.js.jsx", + "t": "source.js.jsx meta.tag.js.jsx meta.tag.attributes.js.jsx string.quoted.double.js.jsx punctuation.definition.string.begin.js.jsx", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -2212,7 +2212,7 @@ }, { "c": "World", - "t": "source.js.jsx meta.tag.js.jsx string.quoted.double.js.jsx", + "t": "source.js.jsx meta.tag.js.jsx meta.tag.attributes.js.jsx string.quoted.double.js.jsx", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -2223,7 +2223,7 @@ }, { "c": "\"", - "t": "source.js.jsx meta.tag.js.jsx string.quoted.double.js.jsx punctuation.definition.string.end.js.jsx", + "t": "source.js.jsx meta.tag.js.jsx meta.tag.attributes.js.jsx string.quoted.double.js.jsx punctuation.definition.string.end.js.jsx", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -2234,7 +2234,7 @@ }, { "c": " ", - "t": "source.js.jsx meta.tag.js.jsx", + "t": "source.js.jsx meta.tag.js.jsx meta.tag.attributes.js.jsx", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2245,7 +2245,7 @@ }, { "c": "alt", - "t": "source.js.jsx meta.tag.js.jsx entity.other.attribute-name.js.jsx", + "t": "source.js.jsx meta.tag.js.jsx meta.tag.attributes.js.jsx entity.other.attribute-name.js.jsx", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", "light_plus": "entity.other.attribute-name: #FF0000", @@ -2256,7 +2256,7 @@ }, { "c": "=", - "t": "source.js.jsx meta.tag.js.jsx keyword.operator.assignment.js.jsx", + "t": "source.js.jsx meta.tag.js.jsx meta.tag.attributes.js.jsx keyword.operator.assignment.js.jsx", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -2267,7 +2267,7 @@ }, { "c": "\"", - "t": "source.js.jsx meta.tag.js.jsx string.quoted.double.js.jsx punctuation.definition.string.begin.js.jsx", + "t": "source.js.jsx meta.tag.js.jsx meta.tag.attributes.js.jsx string.quoted.double.js.jsx punctuation.definition.string.begin.js.jsx", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -2278,7 +2278,7 @@ }, { "c": "Mars", - "t": "source.js.jsx meta.tag.js.jsx string.quoted.double.js.jsx", + "t": "source.js.jsx meta.tag.js.jsx meta.tag.attributes.js.jsx string.quoted.double.js.jsx", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -2289,7 +2289,7 @@ }, { "c": "\"", - "t": "source.js.jsx meta.tag.js.jsx string.quoted.double.js.jsx punctuation.definition.string.end.js.jsx", + "t": "source.js.jsx meta.tag.js.jsx meta.tag.attributes.js.jsx string.quoted.double.js.jsx punctuation.definition.string.end.js.jsx", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -2300,7 +2300,7 @@ }, { "c": " ", - "t": "source.js.jsx meta.tag.js.jsx", + "t": "source.js.jsx meta.tag.js.jsx meta.tag.attributes.js.jsx", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", diff --git a/extensions/json/client/src/jsonMain.ts b/extensions/json/client/src/jsonMain.ts index 1498820c33d..2509a46e385 100644 --- a/extensions/json/client/src/jsonMain.ts +++ b/extensions/json/client/src/jsonMain.ts @@ -145,10 +145,10 @@ export function activate(context: ExtensionContext) { }); }); }, - provideColorPresentations(document: TextDocument, colorInfo: ColorInformation): Thenable { + provideColorPresentations(color: Color, context): Thenable { let params: ColorPresentationParams = { - textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document), - colorInfo: { range: client.code2ProtocolConverter.asRange(colorInfo.range), color: colorInfo.color } + textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(context.document), + colorInfo: { range: client.code2ProtocolConverter.asRange(context.range), color } }; return client.sendRequest(ColorPresentationRequest.type, params).then(presentations => { return presentations.map(p => { @@ -288,4 +288,4 @@ function getPackageInfo(context: ExtensionContext): IPackageInfo { }; } return null; -} \ No newline at end of file +} diff --git a/extensions/json/server/npm-shrinkwrap.json b/extensions/json/server/npm-shrinkwrap.json index 13925889795..4f69e09581f 100644 --- a/extensions/json/server/npm-shrinkwrap.json +++ b/extensions/json/server/npm-shrinkwrap.json @@ -43,9 +43,9 @@ "resolved": "https://registry.npmjs.org/request-light/-/request-light-0.2.1.tgz" }, "vscode-json-languageservice": { - "version": "2.0.21", + "version": "2.0.22", "from": "vscode-json-languageservice@next", - "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-2.0.21.tgz" + "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-2.0.22.tgz" }, "vscode-jsonrpc": { "version": "3.5.0-next.1", @@ -60,12 +60,19 @@ "vscode-languageserver-protocol": { "version": "3.5.0-next.3", "from": "vscode-languageserver-protocol@>=3.5.0-next.2 <4.0.0", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.5.0-next.3.tgz" + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.5.0-next.3.tgz", + "dependencies": { + "vscode-languageserver-types": { + "version": "3.5.0-next.1", + "from": "vscode-languageserver-types@>=3.5.0-next.1 <4.0.0", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.5.0-next.1.tgz" + } + } }, "vscode-languageserver-types": { - "version": "3.5.0-next.1", - "from": "vscode-languageserver-types@>=3.5.0-next.1 <4.0.0", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.5.0-next.1.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 3e4dfd6508f..e570819c565 100644 --- a/extensions/json/server/package.json +++ b/extensions/json/server/package.json @@ -10,7 +10,7 @@ "dependencies": { "jsonc-parser": "^1.0.0", "request-light": "^0.2.1", - "vscode-json-languageservice": "^2.0.21", + "vscode-json-languageservice": "^2.0.22", "vscode-languageserver": "^3.5.0-next.2", "vscode-nls": "^2.0.2", "vscode-uri": "^1.0.1" diff --git a/extensions/markdown/package.json b/extensions/markdown/package.json index 769c6091855..5cb34c4c662 100644 --- a/extensions/markdown/package.json +++ b/extensions/markdown/package.json @@ -46,6 +46,7 @@ "meta.embedded.block.html": "html", "source.js": "javascript", "source.css": "css", + "meta.embedded.block.frontmatter": "yaml", "meta.embedded.block.css": "css", "meta.embedded.block.ini": "ini", diff --git a/extensions/markdown/syntaxes/markdown.tmLanguage b/extensions/markdown/syntaxes/markdown.tmLanguage index 7d5448a80d2..5e7d26dea2b 100644 --- a/extensions/markdown/syntaxes/markdown.tmLanguage +++ b/extensions/markdown/syntaxes/markdown.tmLanguage @@ -3691,6 +3691,8 @@ frontMatter + contentName + meta.embedded.block.frontmatter begin \A-{3}\s*$ while diff --git a/extensions/markdown/syntaxes/markdown.tmLanguage.base b/extensions/markdown/syntaxes/markdown.tmLanguage.base index 6d0a8510217..d501f50a86f 100644 --- a/extensions/markdown/syntaxes/markdown.tmLanguage.base +++ b/extensions/markdown/syntaxes/markdown.tmLanguage.base @@ -1181,6 +1181,8 @@ frontMatter + contentName + meta.embedded.block.frontmatter begin \A-{3}\s*$ while diff --git a/extensions/npm-shrinkwrap.json b/extensions/npm-shrinkwrap.json index 4a636d0dcfe..004d491bbe7 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", - "from": "typescript@2.5.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.5.3.tgz" + "version": "2.6.1-insiders.20171019", + "from": "typescript@2.6.1-insiders.20171019", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.6.1-insiders.20171019.tgz" } } } diff --git a/extensions/package.json b/extensions/package.json index 798babbda78..c2911d05072 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" + "typescript": "2.6.1-insiders.20171019" }, "scripts": { "postinstall": "node ./postinstall" diff --git a/extensions/php/language-configuration.json b/extensions/php/language-configuration.json index 0b35ccae67d..8172fcd09e5 100644 --- a/extensions/php/language-configuration.json +++ b/extensions/php/language-configuration.json @@ -26,5 +26,11 @@ "indentationRules": { "increaseIndentPattern": "({(?!.+}).*|\\(|\\[|((else(\\s)?)?if|else|for(each)?|while|switch).*:)\\s*(/[/*].*)?$", "decreaseIndentPattern": "^(.*\\*\\/)?\\s*((\\})|(\\)+[;,])|(\\][;,])|\\b(else:)|\\b((end(if|for(each)?|while|switch));))" + }, + "folding": { + "markers": { + "start": "^\\s*(#|\/\/)region\\b", + "end": "^\\s*(#|\/\/)endregion\\b" + } } } \ No newline at end of file diff --git a/extensions/theme-defaults/fileicons/vs_minimal_icons.json b/extensions/theme-defaults/fileicons/vs_minimal-icon-theme.json similarity index 100% rename from extensions/theme-defaults/fileicons/vs_minimal_icons.json rename to extensions/theme-defaults/fileicons/vs_minimal-icon-theme.json diff --git a/extensions/theme-defaults/package.json b/extensions/theme-defaults/package.json index a73e2654255..7a48ad6ece2 100644 --- a/extensions/theme-defaults/package.json +++ b/extensions/theme-defaults/package.json @@ -43,7 +43,7 @@ { "id": "vs-minimal", "label": "Minimal (Visual Studio Code)", - "path": "./fileicons/vs_minimal_icons.json" + "path": "./fileicons/vs_minimal-icon-theme.json" } ] } diff --git a/extensions/typescript/build/update-grammars.js b/extensions/typescript/build/update-grammars.js index e82549b4eef..bae3329d9b1 100644 --- a/extensions/typescript/build/update-grammars.js +++ b/extensions/typescript/build/update-grammars.js @@ -15,6 +15,9 @@ function adaptToJavaScript(grammar, replacementScope) { if (typeof rule.name === 'string') { rule.name = rule.name.replace(/\.tsx/g, replacementScope); } + if (typeof rule.contentName === 'string') { + rule.contentName = rule.contentName.replace(/\.tsx/g, replacementScope); + } for (var property in rule) { var value = rule[property]; if (typeof value === 'object') { diff --git a/extensions/typescript/package.json b/extensions/typescript/package.json index 6934e4221bd..ea920c7f29f 100644 --- a/extensions/typescript/package.json +++ b/extensions/typescript/package.json @@ -78,6 +78,7 @@ "embeddedLanguages": { "meta.tag.tsx": "jsx-tags", "meta.tag.without-attributes.tsx": "jsx-tags", + "meta.tag.attributes.tsx": "typescriptreact", "meta.embedded.expression.tsx": "typescriptreact" } } @@ -392,6 +393,21 @@ ], "description": "%typescript.tsc.autoDetect%", "scope": "resource" + }, + "typescript.quickSuggestionsForPaths": { + "type": "boolean", + "default": true, + "description": "%typescript.quickSuggestionsForPaths%", + "scope": "resource" + }, + "typescript.locale": { + "type": [ + "string", + "null" + ], + "default": null, + "description": "%typescript.locale%", + "scope": "window" } } }, @@ -570,4 +586,4 @@ } ] } -} +} \ No newline at end of file diff --git a/extensions/typescript/package.nls.json b/extensions/typescript/package.nls.json index f23e78bf4e1..284db837016 100644 --- a/extensions/typescript/package.nls.json +++ b/extensions/typescript/package.nls.json @@ -41,5 +41,7 @@ "javascript.nameSuggestions": "Enable/disable including unique names from the file in JavaScript suggestion lists.", "typescript.tsc.autoDetect": "Controls auto detection of tsc tasks. 'off' disables this feature. 'build' only creates single run compile tasks. 'watch' only creates compile and watch tasks. 'on' creates both build and watch tasks. Default is 'on'.", "typescript.problemMatchers.tsc.label": "TypeScript problems", - "typescript.problemMatchers.tscWatch.label": "TypeScript problems (watch mode)" + "typescript.problemMatchers.tscWatch.label": "TypeScript problems (watch mode)", + "typescript.quickSuggestionsForPaths": "Enable/disable quick suggestions when typing out an import path.", + "typescript.locale": "Sets the locale used to report TypeScript errors. Requires TypeScript >= 2.6.0. Default of 'null' uses VS Code's locale for TypeScript errors." } diff --git a/extensions/typescript/src/features/codeActionProvider.ts b/extensions/typescript/src/features/codeActionProvider.ts index 32f50fef59f..80773371264 100644 --- a/extensions/typescript/src/features/codeActionProvider.ts +++ b/extensions/typescript/src/features/codeActionProvider.ts @@ -3,12 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CodeActionProvider, TextDocument, Range, CancellationToken, CodeActionContext, Command, commands, workspace, WorkspaceEdit } from 'vscode'; +import { CodeActionProvider, TextDocument, Range, CancellationToken, CodeActionContext, Command, commands } from 'vscode'; import * as Proto from '../protocol'; import { ITypescriptServiceClient } from '../typescriptService'; -import { tsTextSpanToVsRange, vsRangeToTsFileRange } from '../utils/convert'; +import { vsRangeToTsFileRange } from '../utils/convert'; import FormattingConfigurationManager from './formattingConfigurationManager'; +import { applyCodeAction } from '../utils/codeAction'; interface NumberSet { [key: number]: boolean; @@ -55,7 +56,7 @@ export default class TypeScriptCodeActionProvider implements CodeActionProvider errorCodes: Array.from(supportedActions) }; const response = await this.client.execute('getCodeFixes', args, token); - return (response.body || []).map(action => this.getCommandForAction(action)); + return (response.body || []).map(action => this.getCommandForAction(action, file)); } private get supportedCodeActions(): Thenable { @@ -72,31 +73,22 @@ export default class TypeScriptCodeActionProvider implements CodeActionProvider return this._supportedCodeActions; } - private getSupportedActionsForContext(context: CodeActionContext): Thenable> { - return this.supportedCodeActions.then(supportedActions => - new Set(context.diagnostics - .map(diagnostic => +diagnostic.code) - .filter(code => supportedActions[code]))); + private async getSupportedActionsForContext(context: CodeActionContext): Promise> { + const supportedActions = await this.supportedCodeActions; + return new Set(context.diagnostics + .map(diagnostic => +diagnostic.code) + .filter(code => supportedActions[code])); } - private getCommandForAction(action: Proto.CodeAction): Command { + private getCommandForAction(action: Proto.CodeAction, file: string): Command { return { title: action.description, command: this.commandId, - arguments: [action] + arguments: [action, file] }; } - 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), - tsTextSpanToVsRange(textChange), - textChange.newText); - } - } - - return workspace.applyEdit(workspaceEdit); + private onCodeAction(action: Proto.CodeAction, file: string): Promise { + return applyCodeAction(this.client, action, file); } } \ No newline at end of file diff --git a/extensions/typescript/src/features/completionItemProvider.ts b/extensions/typescript/src/features/completionItemProvider.ts index 77fe1015a32..2036add07d1 100644 --- a/extensions/typescript/src/features/completionItemProvider.ts +++ b/extensions/typescript/src/features/completionItemProvider.ts @@ -3,17 +3,18 @@ * 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, CompletionContext } from 'vscode'; +import { CompletionItem, TextDocument, Position, CompletionItemKind, CompletionItemProvider, CancellationToken, TextEdit, Range, SnippetString, workspace, ProviderResult, CompletionContext, commands } from 'vscode'; import { ITypescriptServiceClient } from '../typescriptService'; import TypingsStatus from '../utils/typingsStatus'; import * as PConst from '../protocol.const'; -import { CompletionEntry, CompletionsRequestArgs, CompletionDetailsRequestArgs, CompletionEntryDetails } from '../protocol'; +import { CompletionEntry, CompletionsRequestArgs, CompletionDetailsRequestArgs, CompletionEntryDetails, CodeAction } from '../protocol'; import * as Previewer from './previewer'; import { tsTextSpanToVsRange, vsPositionToTsFileLocation } from '../utils/convert'; import * as nls from 'vscode-nls'; +import { applyCodeAction } from '../utils/codeAction'; let localize = nls.loadMessageBundle(); class MyCompletionItem extends CompletionItem { @@ -122,34 +123,41 @@ class MyCompletionItem extends CompletionItem { interface Configuration { useCodeSnippetsOnMethodSuggest: boolean; nameSuggestions: boolean; + quickSuggestionsForPaths: boolean; } namespace Configuration { export const useCodeSnippetsOnMethodSuggest = 'useCodeSnippetsOnMethodSuggest'; export const nameSuggestions = 'nameSuggestions'; + export const quickSuggestionsForPaths = 'quickSuggestionsForPaths'; } export default class TypeScriptCompletionItemProvider implements CompletionItemProvider { + private readonly commandId: string; - private config: Configuration; + private config: Configuration = { + useCodeSnippetsOnMethodSuggest: false, + nameSuggestions: true, + quickSuggestionsForPaths: true + }; constructor( private client: ITypescriptServiceClient, + mode: string, private typingsStatus: TypingsStatus ) { - this.config = { - useCodeSnippetsOnMethodSuggest: false, - nameSuggestions: true - }; + this.commandId = `_typescript.applyCompletionCodeAction.${mode}`; + commands.registerCommand(this.commandId, this.applyCompletionCodeAction, this); } public updateConfiguration(): void { // Use shared setting for js and ts const typeScriptConfig = workspace.getConfiguration('typescript'); - this.config.useCodeSnippetsOnMethodSuggest = typeScriptConfig.get(Configuration.useCodeSnippetsOnMethodSuggest, false); - const jsConfig = workspace.getConfiguration('javascript'); - this.config.nameSuggestions = jsConfig.get(Configuration.nameSuggestions, true); + this.config.useCodeSnippetsOnMethodSuggest = typeScriptConfig.get(Configuration.useCodeSnippetsOnMethodSuggest, false); + this.config.quickSuggestionsForPaths = typeScriptConfig.get(Configuration.quickSuggestionsForPaths, true); + + this.config.nameSuggestions = workspace.getConfiguration('javascript').get(Configuration.nameSuggestions, true); } public provideCompletionItems( @@ -175,6 +183,10 @@ export default class TypeScriptCompletionItemProvider implements CompletionItemP } if (context.triggerCharacter === '"' || context.triggerCharacter === '\'') { + if (!this.config.quickSuggestionsForPaths) { + return Promise.resolve([]); + } + // 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(/\b(from|import)\s*["']$/) && !line.match(/\b(import|require)\(['"]$/)) { @@ -183,6 +195,10 @@ export default class TypeScriptCompletionItemProvider implements CompletionItemP } if (context.triggerCharacter === '/') { + if (!this.config.quickSuggestionsForPaths) { + return Promise.resolve([]); + } + // 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(/\bfrom\s*["'][^'"]*$/) && !line.match(/\b(import|require)\(['"][^'"]*$/)) { @@ -267,9 +283,16 @@ export default class TypeScriptCompletionItemProvider implements CompletionItemP } const detail = details[0]; item.detail = Previewer.plain(detail.displayParts); - item.documentation = Previewer.markdownDocumentation(detail.documentation, detail.tags); + if (detail.codeActions && detail.codeActions.length) { + item.command = { + title: '', + command: this.commandId, + arguments: [filepath, detail.codeActions] + }; + } + if (detail && this.config.useCodeSnippetsOnMethodSuggest && (item.kind === CompletionItemKind.Function || item.kind === CompletionItemKind.Method)) { return this.isValidFunctionCompletionContext(filepath, item.position).then(shouldCompleteFunction => { if (shouldCompleteFunction) { @@ -331,4 +354,13 @@ export default class TypeScriptCompletionItemProvider implements CompletionItemP return new SnippetString(codeSnippet); } + + private async applyCompletionCodeAction(file: string, codeActions: CodeAction[]): Promise { + for (const action of codeActions) { + if (!(await applyCodeAction(this.client, action, file))) { + return false; + } + } + return true; + } } diff --git a/extensions/typescript/src/features/definitionProviderBase.ts b/extensions/typescript/src/features/definitionProviderBase.ts index bf5ffabd704..ee3a086a4fa 100644 --- a/extensions/typescript/src/features/definitionProviderBase.ts +++ b/extensions/typescript/src/features/definitionProviderBase.ts @@ -13,7 +13,7 @@ export default class TypeScriptDefinitionProviderBase { constructor( private client: ITypescriptServiceClient) { } - protected getSymbolLocations( + protected async getSymbolLocations( definitionType: 'definition' | 'implementation' | 'typeDefinition', document: TextDocument, position: Position, @@ -21,24 +21,24 @@ export default class TypeScriptDefinitionProviderBase { ): Promise { const filepath = this.client.normalizePath(document.uri); if (!filepath) { - return Promise.resolve(null); + return null; } + const args = vsPositionToTsFileLocation(filepath, position); - return this.client.execute(definitionType, args, token).then(response => { + try { + const response = await this.client.execute(definitionType, args, token); const locations: Proto.FileSpan[] = (response && response.body) || []; if (!locations || locations.length === 0) { return []; } return locations.map(location => { const resource = this.client.asUrl(location.file); - if (resource === null) { - return null; - } else { - return new Location(resource, tsTextSpanToVsRange(location)); - } - }).filter(x => x !== null) as Location[]; - }, () => { + return !resource + ? null + : new Location(resource, tsTextSpanToVsRange(location)); + }).filter(x => x) as Location[]; + } catch { return []; - }); + } } } \ No newline at end of file diff --git a/extensions/typescript/src/features/documentHighlightProvider.ts b/extensions/typescript/src/features/documentHighlightProvider.ts index d91cb8fd90b..871629630dc 100644 --- a/extensions/typescript/src/features/documentHighlightProvider.ts +++ b/extensions/typescript/src/features/documentHighlightProvider.ts @@ -13,14 +13,20 @@ export default class TypeScriptDocumentHighlightProvider implements DocumentHigh public constructor( private client: ITypescriptServiceClient) { } - public provideDocumentHighlights(resource: TextDocument, position: Position, token: CancellationToken): Promise { + public async provideDocumentHighlights( + resource: TextDocument, + position: Position, + token: CancellationToken + ): Promise { const filepath = this.client.normalizePath(resource.uri); if (!filepath) { - return Promise.resolve([]); + return []; } + const args = vsPositionToTsFileLocation(filepath, position); - return this.client.execute('occurrences', args, token).then((response): DocumentHighlight[] => { - let data = response.body; + try { + const response = await this.client.execute('occurrences', args, token); + const data = response.body; if (data && data.length) { // Workaround for https://github.com/Microsoft/TypeScript/issues/12780 // Don't highlight string occurrences @@ -39,8 +45,8 @@ export default class TypeScriptDocumentHighlightProvider implements DocumentHigh item.isWriteAccess ? DocumentHighlightKind.Write : DocumentHighlightKind.Read)); } return []; - }, () => { + } catch { return []; - }); + } } } \ No newline at end of file diff --git a/extensions/typescript/src/features/refactorProvider.ts b/extensions/typescript/src/features/refactorProvider.ts index a17533f4b4b..8b5094ce228 100644 --- a/extensions/typescript/src/features/refactorProvider.ts +++ b/extensions/typescript/src/features/refactorProvider.ts @@ -52,6 +52,11 @@ export default class TypeScriptRefactorProvider implements CodeActionProvider { const actions: Command[] = []; for (const info of response.body) { + // Workaround for https://github.com/Microsoft/TypeScript/issues/19378 + if (info.name.startsWith('Install missing ')) { + continue; + } + if (info.inlineable === false) { actions.push({ title: info.description, diff --git a/extensions/typescript/src/features/taskProvider.ts b/extensions/typescript/src/features/taskProvider.ts index 290c07a7709..d7d042e9abb 100644 --- a/extensions/typescript/src/features/taskProvider.ts +++ b/extensions/typescript/src/features/taskProvider.ts @@ -168,6 +168,7 @@ class TscTaskProvider implements vscode.TaskProvider { const buildTaskidentifier: TypeScriptTaskDefinition = { type: 'typescript', tsconfig: label }; const buildTask = new vscode.Task( buildTaskidentifier, + project.workspaceFolder || vscode.TaskScope.Workspace, localize('buildTscLabel', 'build - {0}', label), 'tsc', new vscode.ShellExecution(`${command} -p "${project.path}"`), @@ -181,6 +182,7 @@ class TscTaskProvider implements vscode.TaskProvider { const watchTaskidentifier: TypeScriptTaskDefinition = { type: 'typescript', tsconfig: label, option: 'watch' }; const watchTask = new vscode.Task( watchTaskidentifier, + project.workspaceFolder || vscode.TaskScope.Workspace, localize('buildAndWatchTscLabel', 'watch - {0}', label), 'tsc', new vscode.ShellExecution(`${command} --watch -p "${project.path}"`), diff --git a/extensions/typescript/src/typescriptMain.ts b/extensions/typescript/src/typescriptMain.ts index b4bd55a6357..b7f99cb8024 100644 --- a/extensions/typescript/src/typescriptMain.ts +++ b/extensions/typescript/src/typescriptMain.ts @@ -239,7 +239,7 @@ class LanguageProvider { const selector = this.description.modeIds; const config = workspace.getConfiguration(this.id); - const completionItemProvider = new (await import('./features/completionItemProvider')).default(client, this.typingsStatus); + const completionItemProvider = new (await import('./features/completionItemProvider')).default(client, this.description.id, this.typingsStatus); completionItemProvider.updateConfiguration(); this.toUpdateOnConfigurationChanged.push(completionItemProvider); this.disposables.push(languages.registerCompletionItemProvider(selector, completionItemProvider, '.', '"', '\'', '/', '@')); @@ -690,7 +690,9 @@ class TypeScriptServiceClientHost implements ITypescriptServiceClientHost { const converted = new Diagnostic(range, text); converted.severity = this.getDiagnosticSeverity(diagnostic); converted.source = diagnostic.source || source; - converted.code = '' + diagnostic.code; + if (diagnostic.code) { + converted.code = diagnostic.code; + } result.push(converted); } return result; diff --git a/extensions/typescript/src/typescriptService.ts b/extensions/typescript/src/typescriptService.ts index bf4067894ef..bf367883cb5 100644 --- a/extensions/typescript/src/typescriptService.ts +++ b/extensions/typescript/src/typescriptService.ts @@ -66,6 +66,7 @@ export interface ITypescriptServiceClient { execute(command: 'docCommentTemplate', args: Proto.FileLocationRequestArgs, token?: CancellationToken): Promise; execute(command: 'getApplicableRefactors', args: Proto.GetApplicableRefactorsRequestArgs, token?: CancellationToken): Promise; execute(command: 'getEditsForRefactor', args: Proto.GetEditsForRefactorRequestArgs, token?: CancellationToken): Promise; + execute(command: 'applyCodeActionCommand', args: Proto.ApplyCodeActionCommandRequestArgs, token?: CancellationToken): Promise; // execute(command: 'compileOnSaveAffectedFileList', args: Proto.CompileOnSaveEmitFileRequestArgs, token?: CancellationToken): Promise; // execute(command: 'compileOnSaveEmitFile', args: Proto.CompileOnSaveEmitFileRequestArgs, token?: CancellationToken): Promise; execute(command: string, args: any, expectedResult: boolean | CancellationToken, token?: CancellationToken): Promise; diff --git a/extensions/typescript/src/typescriptServiceClient.ts b/extensions/typescript/src/typescriptServiceClient.ts index bd5936aa22c..3224a1f64b5 100644 --- a/extensions/typescript/src/typescriptServiceClient.ts +++ b/extensions/typescript/src/typescriptServiceClient.ts @@ -11,7 +11,7 @@ import * as os from 'os'; import * as electron from './utils/electron'; import { Reader } from './utils/wireProtocol'; -import { workspace, window, Uri, CancellationToken, Disposable, Memento, MessageItem, EventEmitter, Event, commands } from 'vscode'; +import { workspace, window, Uri, CancellationToken, Disposable, Memento, MessageItem, EventEmitter, Event, commands, env } from 'vscode'; import * as Proto from './protocol'; import { ITypescriptServiceClient, ITypescriptServiceClientHost } from './typescriptService'; import { TypeScriptServerPlugin } from './utils/plugins'; @@ -231,6 +231,7 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient if (this.servicePromise) { this.servicePromise = this.servicePromise.then(cp => { if (cp) { + this.info('Killing TS Server'); this.isRestarting = true; cp.kill(); } @@ -381,6 +382,13 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient } } + if (this.apiVersion.has260Features()) { + const tsLocale = getTsLocale(this.configuration); + if (tsLocale) { + args.push('--locale', tsLocale); + } + } + electron.fork(currentVersion.tsServerPath, args, options, this.logger, (err: any, childProcess: cp.ChildProcess) => { if (err) { this.lastError = err; @@ -394,7 +402,10 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient this.logTelemetry('error', { message: err.message }); return; } + + this.info('Started TSServer'); this.lastStart = Date.now(); + childProcess.on('error', (err: Error) => { this.lastError = err; this.error('TSServer errored with error.', err); @@ -409,7 +420,7 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient }); childProcess.on('exit', (code: any) => { if (code === null || typeof code === 'undefined') { - this.info(`TSServer exited`); + this.info('TSServer exited'); } else { this.error(`TSServer exited with code: ${code}`); /* __GDPR__ @@ -862,3 +873,9 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient this.logTelemetry(telemetryData.telemetryEventName, properties); } } + + +const getTsLocale = (configuration: TypeScriptServiceConfiguration): string | undefined => + (configuration.locale + ? configuration.locale + : env.language); \ No newline at end of file diff --git a/extensions/typescript/src/utils/api.ts b/extensions/typescript/src/utils/api.ts index 91888a52f58..69307cad289 100644 --- a/extensions/typescript/src/utils/api.ts +++ b/extensions/typescript/src/utils/api.ts @@ -69,4 +69,8 @@ export default class API { public has250Features(): boolean { return semver.gte(this.version, '2.5.0'); } + + public has260Features(): boolean { + return semver.gte(this.version, '2.6.0'); + } } \ No newline at end of file diff --git a/extensions/typescript/src/utils/codeAction.ts b/extensions/typescript/src/utils/codeAction.ts new file mode 100644 index 00000000000..c975a4ef9f2 --- /dev/null +++ b/extensions/typescript/src/utils/codeAction.ts @@ -0,0 +1,41 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { WorkspaceEdit, workspace } from 'vscode'; +import * as Proto from '../protocol'; +import { tsTextSpanToVsRange } from './convert'; +import { ITypescriptServiceClient } from '../typescriptService'; + + +export async function applyCodeAction( + client: ITypescriptServiceClient, + action: Proto.CodeAction, + file: string +): Promise { + if (action.changes && action.changes.length) { + const workspaceEdit = new WorkspaceEdit(); + for (const change of action.changes) { + for (const textChange of change.textChanges) { + workspaceEdit.replace(client.asUrl(change.fileName), + tsTextSpanToVsRange(textChange), + textChange.newText); + } + } + + if (!(await workspace.applyEdit(workspaceEdit))) { + return false; + } + } + + if (action.commands && action.commands.length) { + for (const command of action.commands) { + const response = await client.execute('applyCodeActionCommand', { file, command }); + if (!response || !response.body) { + return false; + } + } + } + return true; +} \ No newline at end of file diff --git a/extensions/typescript/src/utils/configuration.ts b/extensions/typescript/src/utils/configuration.ts index 4a759024dd5..52792e4c73a 100644 --- a/extensions/typescript/src/utils/configuration.ts +++ b/extensions/typescript/src/utils/configuration.ts @@ -42,6 +42,7 @@ export namespace TsServerLogLevel { } export class TypeScriptServiceConfiguration { + public readonly locale: string | null; public readonly globalTsdk: string | null; public readonly localTsdk: string | null; public readonly npmLocation: string | null; @@ -56,6 +57,7 @@ export class TypeScriptServiceConfiguration { private constructor() { const configuration = workspace.getConfiguration(); + this.locale = TypeScriptServiceConfiguration.extractLocale(configuration); this.globalTsdk = TypeScriptServiceConfiguration.extractGlobalTsdk(configuration); this.localTsdk = TypeScriptServiceConfiguration.extractLocalTsdk(configuration); this.npmLocation = TypeScriptServiceConfiguration.readNpmLocation(configuration); @@ -65,7 +67,8 @@ export class TypeScriptServiceConfiguration { } public isEqualTo(other: TypeScriptServiceConfiguration): boolean { - return this.globalTsdk === other.globalTsdk + return this.locale === other.locale + && this.globalTsdk === other.globalTsdk && this.localTsdk === other.localTsdk && this.npmLocation === other.npmLocation && this.tsServerLogLevel === other.tsServerLogLevel @@ -105,4 +108,8 @@ export class TypeScriptServiceConfiguration { private static readDisableAutomaticTypeAcquisition(configuration: WorkspaceConfiguration): boolean { return configuration.get('typescript.disableAutomaticTypeAcquisition', false); } + + private static extractLocale(configuration: WorkspaceConfiguration): string | null { + return configuration.get('typescript.locale', null); + } } diff --git a/extensions/typescript/syntaxes/TypeScript.tmLanguage.json b/extensions/typescript/syntaxes/TypeScript.tmLanguage.json index ba5c664ccf4..454dc854bdf 100644 --- a/extensions/typescript/syntaxes/TypeScript.tmLanguage.json +++ b/extensions/typescript/syntaxes/TypeScript.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/5955a5aed3d8d2862c614f2137d22f2334d490e9", + "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/f3a2069b99f45c34ac5cc7dc5f1dcb4e81486ab9", "name": "TypeScript", "scopeName": "source.ts", "fileTypes": [ @@ -272,7 +272,7 @@ "patterns": [ { "name": "meta.var-single-variable.expr.ts", - "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n # sure shot arrow functions even if => is on new line\n(\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\((\\s*[_$[:alpha:]\\{\\(]([^()]|\\((\\s*[_$[:alpha:]\\{\\(]\\{\\(][^()]*)?\\))*)?\\) # parameteres\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)))", + "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n # sure shot arrow functions even if => is on new line\n(\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*[_$[:alpha:]\\{\\(\\[]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(\\[]([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\((\\s*[_$[:alpha:]\\{\\(]([^()]|\\((\\s*[_$[:alpha:]\\{\\(]\\{\\(][^()]*)?\\))*)?\\) # parameteres\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)))", "beginCaptures": { "1": { "name": "meta.definition.variable.ts entity.name.function.ts" @@ -506,7 +506,7 @@ } }, { - "match": "(?x)(?:\\s*\\b(public|private|protected|readonly)\\s+)?(\\.\\.\\.)?\\s*(?)\n )) |\n ((async\\s*)?(\n # sure shot arrow functions even if => is on new line\n(\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\((\\s*[_$[:alpha:]\\{\\(]([^()]|\\((\\s*[_$[:alpha:]\\{\\(]\\{\\(][^()]*)?\\))*)?\\) # parameteres\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)))", + "match": "(?x)(?:\\s*\\b(public|private|protected|readonly)\\s+)?(\\.\\.\\.)?\\s*(?)\n )) |\n ((async\\s*)?(\n # sure shot arrow functions even if => is on new line\n(\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*[_$[:alpha:]\\{\\(\\[]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(\\[]([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\((\\s*[_$[:alpha:]\\{\\(]([^()]|\\((\\s*[_$[:alpha:]\\{\\(]\\{\\(][^()]*)?\\))*)?\\) # parameteres\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)))", "captures": { "1": { "name": "storage.modifier.ts" @@ -733,7 +733,7 @@ }, { "name": "meta.definition.property.ts entity.name.function.ts", - "match": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=(\\?\\s*)?\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n # sure shot arrow functions even if => is on new line\n(\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\((\\s*[_$[:alpha:]\\{\\(]([^()]|\\((\\s*[_$[:alpha:]\\{\\(]\\{\\(][^()]*)?\\))*)?\\) # parameteres\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)))" + "match": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=(\\?\\s*)?\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n # sure shot arrow functions even if => is on new line\n(\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*[_$[:alpha:]\\{\\(\\[]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(\\[]([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\((\\s*[_$[:alpha:]\\{\\(]([^()]|\\((\\s*[_$[:alpha:]\\{\\(]\\{\\(][^()]*)?\\))*)?\\) # parameteres\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)))" }, { "name": "meta.definition.property.ts variable.object.property.ts", @@ -996,7 +996,7 @@ }, { "name": "meta.arrow.ts", - "begin": "(?x) (?:\n (? is on new line\n(\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\((\\s*[_$[:alpha:]\\{\\(]([^()]|\\((\\s*[_$[:alpha:]\\{\\(]\\{\\(][^()]*)?\\))*)?\\) # parameteres\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n )\n)", + "begin": "(?x) (?:\n (? is on new line\n(\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*[_$[:alpha:]\\{\\(\\[]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(\\[]([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\((\\s*[_$[:alpha:]\\{\\(]([^()]|\\((\\s*[_$[:alpha:]\\{\\(]\\{\\(][^()]*)?\\))*)?\\) # parameteres\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n )\n)", "beginCaptures": { "1": { "name": "storage.modifier.async.ts" @@ -1701,8 +1701,7 @@ "include": "#comment" }, { - "comment": "(default|*|name) as alias", - "match": "(?x) (?: \\b(default)\\b | (\\*) | ([_$[:alpha:]][_$[:alnum:]]*)) \\s+\n (as) \\s+ (?: (\\b default \\b | \\*) | ([_$[:alpha:]][_$[:alnum:]]*))", + "match": "(?)\n )) |\n ((async\\s*)?(\n # sure shot arrow functions even if => is on new line\n(\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\((\\s*[_$[:alpha:]\\{\\(]([^()]|\\((\\s*[_$[:alpha:]\\{\\(]\\{\\(][^()]*)?\\))*)?\\) # parameteres\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=:\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n # sure shot arrow functions even if => is on new line\n(\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*[_$[:alpha:]\\{\\(\\[]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(\\[]([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\((\\s*[_$[:alpha:]\\{\\(]([^()]|\\((\\s*[_$[:alpha:]\\{\\(]\\{\\(][^()]*)?\\))*)?\\) # parameteres\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "0": { "name": "meta.object-literal.key.ts" @@ -2037,13 +2033,13 @@ ] }, "function-call": { - "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\.\\s*)*|(\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)?\\()", - "end": "(?<=\\))(?!(([_$[:alpha:]][_$[:alnum:]]*\\s*\\.\\s*)*|(\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)?\\()", + "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\.\\s*)*|(\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(<\\s*[_$[:alpha:]\\{\\(\\[]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(\\[]([^=<>]|=[^<])+\\>)+>\\s*)?\\()", + "end": "(?<=\\))(?!(([_$[:alpha:]][_$[:alnum:]]*\\s*\\.\\s*)*|(\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(<\\s*[_$[:alpha:]\\{\\(\\[]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(\\[]([^=<>]|=[^<])+\\>)+>\\s*)?\\()", "patterns": [ { "name": "meta.function-call.ts", "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\.\\s*)*|(\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*))", - "end": "(?=\\s*(<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)?\\()", + "end": "(?=\\s*(<\\s*[_$[:alpha:]\\{\\(\\[]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(\\[]([^=<>]|=[^<])+\\>)+>\\s*)?\\()", "patterns": [ { "include": "#literal" @@ -2527,7 +2523,7 @@ } }, { - "match": "(?x) (\\.) \\s* (?:\n (ATTRIBUTE_NODE|CDATA_SECTION_NODE|COMMENT_NODE|DOCUMENT_FRAGMENT_NODE|DOCUMENT_NODE|DOCUMENT_TYPE_NODE\n |DOMSTRING_SIZE_ERR|ELEMENT_NODE|ENTITY_NODE|ENTITY_REFERENCE_NODE|HIERARCHY_REQUEST_ERR|INDEX_SIZE_ERR\n |INUSE_ATTRIBUTE_ERR|INVALID_CHARACTER_ERR|NO_DATA_ALLOWED_ERR|NO_MODIFICATION_ALLOWED_ERR|NOT_FOUND_ERR\n |NOT_SUPPORTED_ERR|NOTATION_NODE|PROCESSING_INSTRUCTION_NODE|TEXT_NODE|WRONG_DOCUMENT_ERR)\n |\n (_content|[xyz]|abbr|above|accept|acceptCharset|accessKey|action|align|[av]Link(?:color)?|all|alt|anchors|appCodeName\n |appCore|applets|appMinorVersion|appName|appVersion|archive|areas|arguments|attributes|availHeight|availLeft|availTop\n |availWidth|axis|background|backgroundColor|backgroundImage|below|bgColor|body|border|borderBottomWidth|borderColor\n |borderLeftWidth|borderRightWidth|borderStyle|borderTopWidth|borderWidth|bottom|bufferDepth|callee|caller|caption\n |cellPadding|cells|cellSpacing|ch|characterSet|charset|checked|childNodes|chOff|cite|classes|className|clear\n |clientInformation|clip|clipBoardData|closed|code|codeBase|codeType|color|colorDepth|cols|colSpan|compact|complete\n |components|content|controllers|cookie|cookieEnabled|cords|cpuClass|crypto|current|data|dateTime|declare|defaultCharset\n |defaultChecked|defaultSelected|defaultStatus|defaultValue|defaultView|defer|description|dialogArguments|dialogHeight\n |dialogLeft|dialogTop|dialogWidth|dir|directories|disabled|display|docmain|doctype|documentElement|elements|embeds\n |enabledPlugin|encoding|enctype|entities|event|expando|external|face|fgColor|filename|firstChild|fontFamily|fontSize\n |fontWeight|form|formName|forms|frame|frameBorder|frameElement|frames|hasFocus|hash|headers|height|history|host\n |hostname|href|hreflang|hspace|htmlFor|httpEquiv|id|ids|ignoreCase|images|implementation|index|innerHeight|innerWidth\n |input|isMap|label|lang|language|lastChild|lastIndex|lastMatch|lastModified|lastParen|layer[sXY]|left|leftContext\n |lineHeight|link|linkColor|links|listStyleType|localName|location|locationbar|longDesc|lowsrc|lowSrc|marginBottom\n |marginHeight|marginLeft|marginRight|marginTop|marginWidth|maxLength|media|menubar|method|mimeTypes|multiline|multiple\n |name|nameProp|namespaces|namespaceURI|next|nextSibling|nodeName|nodeType|nodeValue|noHref|noResize|noShade|notationName\n |notations|noWrap|object|offscreenBuffering|onLine|onreadystatechange|opener|opsProfile|options|oscpu|outerHeight\n |outerWidth|ownerDocument|paddingBottom|paddingLeft|paddingRight|paddingTop|page[XY]|page[XY]Offset|parent|parentLayer\n |parentNode|parentWindow|pathname|personalbar|pixelDepth|pkcs11|platform|plugins|port|prefix|previous|previousDibling\n |product|productSub|profile|profileend|prompt|prompter|protocol|publicId|readOnly|readyState|referrer|rel|responseText\n |responseXML|rev|right|rightContext|rowIndex|rows|rowSpan|rules|scheme|scope|screen[XY]|screenLeft|screenTop|scripts\n |scrollbars|scrolling|sectionRowIndex|security|securityPolicy|selected|selectedIndex|selection|self|shape|siblingAbove\n |siblingBelow|size|source|specified|standby|start|status|statusbar|statusText|style|styleSheets|suffixes|summary\n |systemId|systemLanguage|tagName|tags|target|tBodies|text|textAlign|textDecoration|textIndent|textTransform|tFoot|tHead\n |title|toolbar|top|type|undefined|uniqueID|updateInterval|URL|URLUnencoded|useMap|userAgent|userLanguage|userProfile\n |vAlign|value|valueType|vendor|vendorSub|version|visibility|vspace|whiteSpace|width|X[MS]LDocument|zIndex))\\b(?!\\$|\\s*(<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)?\\()", + "match": "(?x) (\\.) \\s* (?:\n (ATTRIBUTE_NODE|CDATA_SECTION_NODE|COMMENT_NODE|DOCUMENT_FRAGMENT_NODE|DOCUMENT_NODE|DOCUMENT_TYPE_NODE\n |DOMSTRING_SIZE_ERR|ELEMENT_NODE|ENTITY_NODE|ENTITY_REFERENCE_NODE|HIERARCHY_REQUEST_ERR|INDEX_SIZE_ERR\n |INUSE_ATTRIBUTE_ERR|INVALID_CHARACTER_ERR|NO_DATA_ALLOWED_ERR|NO_MODIFICATION_ALLOWED_ERR|NOT_FOUND_ERR\n |NOT_SUPPORTED_ERR|NOTATION_NODE|PROCESSING_INSTRUCTION_NODE|TEXT_NODE|WRONG_DOCUMENT_ERR)\n |\n (_content|[xyz]|abbr|above|accept|acceptCharset|accessKey|action|align|[av]Link(?:color)?|all|alt|anchors|appCodeName\n |appCore|applets|appMinorVersion|appName|appVersion|archive|areas|arguments|attributes|availHeight|availLeft|availTop\n |availWidth|axis|background|backgroundColor|backgroundImage|below|bgColor|body|border|borderBottomWidth|borderColor\n |borderLeftWidth|borderRightWidth|borderStyle|borderTopWidth|borderWidth|bottom|bufferDepth|callee|caller|caption\n |cellPadding|cells|cellSpacing|ch|characterSet|charset|checked|childNodes|chOff|cite|classes|className|clear\n |clientInformation|clip|clipBoardData|closed|code|codeBase|codeType|color|colorDepth|cols|colSpan|compact|complete\n |components|content|controllers|cookie|cookieEnabled|cords|cpuClass|crypto|current|data|dateTime|declare|defaultCharset\n |defaultChecked|defaultSelected|defaultStatus|defaultValue|defaultView|defer|description|dialogArguments|dialogHeight\n |dialogLeft|dialogTop|dialogWidth|dir|directories|disabled|display|docmain|doctype|documentElement|elements|embeds\n |enabledPlugin|encoding|enctype|entities|event|expando|external|face|fgColor|filename|firstChild|fontFamily|fontSize\n |fontWeight|form|formName|forms|frame|frameBorder|frameElement|frames|hasFocus|hash|headers|height|history|host\n |hostname|href|hreflang|hspace|htmlFor|httpEquiv|id|ids|ignoreCase|images|implementation|index|innerHeight|innerWidth\n |input|isMap|label|lang|language|lastChild|lastIndex|lastMatch|lastModified|lastParen|layer[sXY]|left|leftContext\n |lineHeight|link|linkColor|links|listStyleType|localName|location|locationbar|longDesc|lowsrc|lowSrc|marginBottom\n |marginHeight|marginLeft|marginRight|marginTop|marginWidth|maxLength|media|menubar|method|mimeTypes|multiline|multiple\n |name|nameProp|namespaces|namespaceURI|next|nextSibling|nodeName|nodeType|nodeValue|noHref|noResize|noShade|notationName\n |notations|noWrap|object|offscreenBuffering|onLine|onreadystatechange|opener|opsProfile|options|oscpu|outerHeight\n |outerWidth|ownerDocument|paddingBottom|paddingLeft|paddingRight|paddingTop|page[XY]|page[XY]Offset|parent|parentLayer\n |parentNode|parentWindow|pathname|personalbar|pixelDepth|pkcs11|platform|plugins|port|prefix|previous|previousDibling\n |product|productSub|profile|profileend|prompt|prompter|protocol|publicId|readOnly|readyState|referrer|rel|responseText\n |responseXML|rev|right|rightContext|rowIndex|rows|rowSpan|rules|scheme|scope|screen[XY]|screenLeft|screenTop|scripts\n |scrollbars|scrolling|sectionRowIndex|security|securityPolicy|selected|selectedIndex|selection|self|shape|siblingAbove\n |siblingBelow|size|source|specified|standby|start|status|statusbar|statusText|style|styleSheets|suffixes|summary\n |systemId|systemLanguage|tagName|tags|target|tBodies|text|textAlign|textDecoration|textIndent|textTransform|tFoot|tHead\n |title|toolbar|top|type|undefined|uniqueID|updateInterval|URL|URLUnencoded|useMap|userAgent|userLanguage|userProfile\n |vAlign|value|valueType|vendor|vendorSub|version|visibility|vspace|whiteSpace|width|X[MS]LDocument|zIndex))\\b(?!\\$|\\s*(<\\s*[_$[:alpha:]\\{\\(\\[]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(\\[]([^=<>]|=[^<])+\\>)+>\\s*)?\\()", "captures": { "1": { "name": "punctuation.accessor.ts" @@ -2607,7 +2603,7 @@ "include": "#object-identifiers" }, { - "match": "(?x)(?:(\\.)\\s*)?([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n # sure shot arrow functions even if => is on new line\n(\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\((\\s*[_$[:alpha:]\\{\\(]([^()]|\\((\\s*[_$[:alpha:]\\{\\(]\\{\\(][^()]*)?\\))*)?\\) # parameteres\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n))", + "match": "(?x)(?:(\\.)\\s*)?([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n # sure shot arrow functions even if => is on new line\n(\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*[_$[:alpha:]\\{\\(\\[]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(\\[]([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\((\\s*[_$[:alpha:]\\{\\(]([^()]|\\((\\s*[_$[:alpha:]\\{\\(]\\{\\(][^()]*)?\\))*)?\\) # parameteres\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n))", "captures": { "1": { "name": "punctuation.accessor.ts" diff --git a/extensions/typescript/syntaxes/TypeScriptReact.tmLanguage.json b/extensions/typescript/syntaxes/TypeScriptReact.tmLanguage.json index 8266fec39bb..3c825612994 100644 --- a/extensions/typescript/syntaxes/TypeScriptReact.tmLanguage.json +++ b/extensions/typescript/syntaxes/TypeScriptReact.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/5955a5aed3d8d2862c614f2137d22f2334d490e9", + "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/824f47ea6e98590ac2e75db5bebdf6eff71421ad", "name": "TypeScriptReact", "scopeName": "source.tsx", "fileTypes": [ @@ -275,7 +275,7 @@ "patterns": [ { "name": "meta.var-single-variable.expr.tsx", - "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n # sure shot arrow functions even if => is on new line\n(\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\((\\s*[_$[:alpha:]\\{\\(]([^()]|\\((\\s*[_$[:alpha:]\\{\\(]\\{\\(][^()]*)?\\))*)?\\) # parameteres\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)))", + "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n # sure shot arrow functions even if => is on new line\n(\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*[_$[:alpha:]\\{\\(\\[]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(\\[]([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\((\\s*[_$[:alpha:]\\{\\(]([^()]|\\((\\s*[_$[:alpha:]\\{\\(]\\{\\(][^()]*)?\\))*)?\\) # parameteres\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)))", "beginCaptures": { "1": { "name": "meta.definition.variable.tsx entity.name.function.tsx" @@ -509,7 +509,7 @@ } }, { - "match": "(?x)(?:\\s*\\b(public|private|protected|readonly)\\s+)?(\\.\\.\\.)?\\s*(?)\n )) |\n ((async\\s*)?(\n # sure shot arrow functions even if => is on new line\n(\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\((\\s*[_$[:alpha:]\\{\\(]([^()]|\\((\\s*[_$[:alpha:]\\{\\(]\\{\\(][^()]*)?\\))*)?\\) # parameteres\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)))", + "match": "(?x)(?:\\s*\\b(public|private|protected|readonly)\\s+)?(\\.\\.\\.)?\\s*(?)\n )) |\n ((async\\s*)?(\n # sure shot arrow functions even if => is on new line\n(\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*[_$[:alpha:]\\{\\(\\[]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(\\[]([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\((\\s*[_$[:alpha:]\\{\\(]([^()]|\\((\\s*[_$[:alpha:]\\{\\(]\\{\\(][^()]*)?\\))*)?\\) # parameteres\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)))", "captures": { "1": { "name": "storage.modifier.tsx" @@ -736,7 +736,7 @@ }, { "name": "meta.definition.property.tsx entity.name.function.tsx", - "match": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=(\\?\\s*)?\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n # sure shot arrow functions even if => is on new line\n(\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\((\\s*[_$[:alpha:]\\{\\(]([^()]|\\((\\s*[_$[:alpha:]\\{\\(]\\{\\(][^()]*)?\\))*)?\\) # parameteres\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)))" + "match": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=(\\?\\s*)?\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n # sure shot arrow functions even if => is on new line\n(\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*[_$[:alpha:]\\{\\(\\[]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(\\[]([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\((\\s*[_$[:alpha:]\\{\\(]([^()]|\\((\\s*[_$[:alpha:]\\{\\(]\\{\\(][^()]*)?\\))*)?\\) # parameteres\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)))" }, { "name": "meta.definition.property.tsx variable.object.property.tsx", @@ -999,7 +999,7 @@ }, { "name": "meta.arrow.tsx", - "begin": "(?x) (?:\n (? is on new line\n(\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\((\\s*[_$[:alpha:]\\{\\(]([^()]|\\((\\s*[_$[:alpha:]\\{\\(]\\{\\(][^()]*)?\\))*)?\\) # parameteres\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n )\n)", + "begin": "(?x) (?:\n (? is on new line\n(\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*[_$[:alpha:]\\{\\(\\[]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(\\[]([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\((\\s*[_$[:alpha:]\\{\\(]([^()]|\\((\\s*[_$[:alpha:]\\{\\(]\\{\\(][^()]*)?\\))*)?\\) # parameteres\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n )\n)", "beginCaptures": { "1": { "name": "storage.modifier.async.tsx" @@ -1704,8 +1704,7 @@ "include": "#comment" }, { - "comment": "(default|*|name) as alias", - "match": "(?x) (?: \\b(default)\\b | (\\*) | ([_$[:alpha:]][_$[:alnum:]]*)) \\s+\n (as) \\s+ (?: (\\b default \\b | \\*) | ([_$[:alpha:]][_$[:alnum:]]*))", + "match": "(?)\n )) |\n ((async\\s*)?(\n # sure shot arrow functions even if => is on new line\n(\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\((\\s*[_$[:alpha:]\\{\\(]([^()]|\\((\\s*[_$[:alpha:]\\{\\(]\\{\\(][^()]*)?\\))*)?\\) # parameteres\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=:\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n # sure shot arrow functions even if => is on new line\n(\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*[_$[:alpha:]\\{\\(\\[]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(\\[]([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\((\\s*[_$[:alpha:]\\{\\(]([^()]|\\((\\s*[_$[:alpha:]\\{\\(]\\{\\(][^()]*)?\\))*)?\\) # parameteres\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "0": { "name": "meta.object-literal.key.tsx" @@ -2040,13 +2036,13 @@ ] }, "function-call": { - "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\.\\s*)*|(\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)?\\()", - "end": "(?<=\\))(?!(([_$[:alpha:]][_$[:alnum:]]*\\s*\\.\\s*)*|(\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)?\\()", + "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\.\\s*)*|(\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(<\\s*[_$[:alpha:]\\{\\(\\[]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(\\[]([^=<>]|=[^<])+\\>)+>\\s*)?\\()", + "end": "(?<=\\))(?!(([_$[:alpha:]][_$[:alnum:]]*\\s*\\.\\s*)*|(\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(<\\s*[_$[:alpha:]\\{\\(\\[]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(\\[]([^=<>]|=[^<])+\\>)+>\\s*)?\\()", "patterns": [ { "name": "meta.function-call.tsx", "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\.\\s*)*|(\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*))", - "end": "(?=\\s*(<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)?\\()", + "end": "(?=\\s*(<\\s*[_$[:alpha:]\\{\\(\\[]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(\\[]([^=<>]|=[^<])+\\>)+>\\s*)?\\()", "patterns": [ { "include": "#literal" @@ -2493,7 +2489,7 @@ } }, { - "match": "(?x) (\\.) \\s* (?:\n (ATTRIBUTE_NODE|CDATA_SECTION_NODE|COMMENT_NODE|DOCUMENT_FRAGMENT_NODE|DOCUMENT_NODE|DOCUMENT_TYPE_NODE\n |DOMSTRING_SIZE_ERR|ELEMENT_NODE|ENTITY_NODE|ENTITY_REFERENCE_NODE|HIERARCHY_REQUEST_ERR|INDEX_SIZE_ERR\n |INUSE_ATTRIBUTE_ERR|INVALID_CHARACTER_ERR|NO_DATA_ALLOWED_ERR|NO_MODIFICATION_ALLOWED_ERR|NOT_FOUND_ERR\n |NOT_SUPPORTED_ERR|NOTATION_NODE|PROCESSING_INSTRUCTION_NODE|TEXT_NODE|WRONG_DOCUMENT_ERR)\n |\n (_content|[xyz]|abbr|above|accept|acceptCharset|accessKey|action|align|[av]Link(?:color)?|all|alt|anchors|appCodeName\n |appCore|applets|appMinorVersion|appName|appVersion|archive|areas|arguments|attributes|availHeight|availLeft|availTop\n |availWidth|axis|background|backgroundColor|backgroundImage|below|bgColor|body|border|borderBottomWidth|borderColor\n |borderLeftWidth|borderRightWidth|borderStyle|borderTopWidth|borderWidth|bottom|bufferDepth|callee|caller|caption\n |cellPadding|cells|cellSpacing|ch|characterSet|charset|checked|childNodes|chOff|cite|classes|className|clear\n |clientInformation|clip|clipBoardData|closed|code|codeBase|codeType|color|colorDepth|cols|colSpan|compact|complete\n |components|content|controllers|cookie|cookieEnabled|cords|cpuClass|crypto|current|data|dateTime|declare|defaultCharset\n |defaultChecked|defaultSelected|defaultStatus|defaultValue|defaultView|defer|description|dialogArguments|dialogHeight\n |dialogLeft|dialogTop|dialogWidth|dir|directories|disabled|display|docmain|doctype|documentElement|elements|embeds\n |enabledPlugin|encoding|enctype|entities|event|expando|external|face|fgColor|filename|firstChild|fontFamily|fontSize\n |fontWeight|form|formName|forms|frame|frameBorder|frameElement|frames|hasFocus|hash|headers|height|history|host\n |hostname|href|hreflang|hspace|htmlFor|httpEquiv|id|ids|ignoreCase|images|implementation|index|innerHeight|innerWidth\n |input|isMap|label|lang|language|lastChild|lastIndex|lastMatch|lastModified|lastParen|layer[sXY]|left|leftContext\n |lineHeight|link|linkColor|links|listStyleType|localName|location|locationbar|longDesc|lowsrc|lowSrc|marginBottom\n |marginHeight|marginLeft|marginRight|marginTop|marginWidth|maxLength|media|menubar|method|mimeTypes|multiline|multiple\n |name|nameProp|namespaces|namespaceURI|next|nextSibling|nodeName|nodeType|nodeValue|noHref|noResize|noShade|notationName\n |notations|noWrap|object|offscreenBuffering|onLine|onreadystatechange|opener|opsProfile|options|oscpu|outerHeight\n |outerWidth|ownerDocument|paddingBottom|paddingLeft|paddingRight|paddingTop|page[XY]|page[XY]Offset|parent|parentLayer\n |parentNode|parentWindow|pathname|personalbar|pixelDepth|pkcs11|platform|plugins|port|prefix|previous|previousDibling\n |product|productSub|profile|profileend|prompt|prompter|protocol|publicId|readOnly|readyState|referrer|rel|responseText\n |responseXML|rev|right|rightContext|rowIndex|rows|rowSpan|rules|scheme|scope|screen[XY]|screenLeft|screenTop|scripts\n |scrollbars|scrolling|sectionRowIndex|security|securityPolicy|selected|selectedIndex|selection|self|shape|siblingAbove\n |siblingBelow|size|source|specified|standby|start|status|statusbar|statusText|style|styleSheets|suffixes|summary\n |systemId|systemLanguage|tagName|tags|target|tBodies|text|textAlign|textDecoration|textIndent|textTransform|tFoot|tHead\n |title|toolbar|top|type|undefined|uniqueID|updateInterval|URL|URLUnencoded|useMap|userAgent|userLanguage|userProfile\n |vAlign|value|valueType|vendor|vendorSub|version|visibility|vspace|whiteSpace|width|X[MS]LDocument|zIndex))\\b(?!\\$|\\s*(<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)?\\()", + "match": "(?x) (\\.) \\s* (?:\n (ATTRIBUTE_NODE|CDATA_SECTION_NODE|COMMENT_NODE|DOCUMENT_FRAGMENT_NODE|DOCUMENT_NODE|DOCUMENT_TYPE_NODE\n |DOMSTRING_SIZE_ERR|ELEMENT_NODE|ENTITY_NODE|ENTITY_REFERENCE_NODE|HIERARCHY_REQUEST_ERR|INDEX_SIZE_ERR\n |INUSE_ATTRIBUTE_ERR|INVALID_CHARACTER_ERR|NO_DATA_ALLOWED_ERR|NO_MODIFICATION_ALLOWED_ERR|NOT_FOUND_ERR\n |NOT_SUPPORTED_ERR|NOTATION_NODE|PROCESSING_INSTRUCTION_NODE|TEXT_NODE|WRONG_DOCUMENT_ERR)\n |\n (_content|[xyz]|abbr|above|accept|acceptCharset|accessKey|action|align|[av]Link(?:color)?|all|alt|anchors|appCodeName\n |appCore|applets|appMinorVersion|appName|appVersion|archive|areas|arguments|attributes|availHeight|availLeft|availTop\n |availWidth|axis|background|backgroundColor|backgroundImage|below|bgColor|body|border|borderBottomWidth|borderColor\n |borderLeftWidth|borderRightWidth|borderStyle|borderTopWidth|borderWidth|bottom|bufferDepth|callee|caller|caption\n |cellPadding|cells|cellSpacing|ch|characterSet|charset|checked|childNodes|chOff|cite|classes|className|clear\n |clientInformation|clip|clipBoardData|closed|code|codeBase|codeType|color|colorDepth|cols|colSpan|compact|complete\n |components|content|controllers|cookie|cookieEnabled|cords|cpuClass|crypto|current|data|dateTime|declare|defaultCharset\n |defaultChecked|defaultSelected|defaultStatus|defaultValue|defaultView|defer|description|dialogArguments|dialogHeight\n |dialogLeft|dialogTop|dialogWidth|dir|directories|disabled|display|docmain|doctype|documentElement|elements|embeds\n |enabledPlugin|encoding|enctype|entities|event|expando|external|face|fgColor|filename|firstChild|fontFamily|fontSize\n |fontWeight|form|formName|forms|frame|frameBorder|frameElement|frames|hasFocus|hash|headers|height|history|host\n |hostname|href|hreflang|hspace|htmlFor|httpEquiv|id|ids|ignoreCase|images|implementation|index|innerHeight|innerWidth\n |input|isMap|label|lang|language|lastChild|lastIndex|lastMatch|lastModified|lastParen|layer[sXY]|left|leftContext\n |lineHeight|link|linkColor|links|listStyleType|localName|location|locationbar|longDesc|lowsrc|lowSrc|marginBottom\n |marginHeight|marginLeft|marginRight|marginTop|marginWidth|maxLength|media|menubar|method|mimeTypes|multiline|multiple\n |name|nameProp|namespaces|namespaceURI|next|nextSibling|nodeName|nodeType|nodeValue|noHref|noResize|noShade|notationName\n |notations|noWrap|object|offscreenBuffering|onLine|onreadystatechange|opener|opsProfile|options|oscpu|outerHeight\n |outerWidth|ownerDocument|paddingBottom|paddingLeft|paddingRight|paddingTop|page[XY]|page[XY]Offset|parent|parentLayer\n |parentNode|parentWindow|pathname|personalbar|pixelDepth|pkcs11|platform|plugins|port|prefix|previous|previousDibling\n |product|productSub|profile|profileend|prompt|prompter|protocol|publicId|readOnly|readyState|referrer|rel|responseText\n |responseXML|rev|right|rightContext|rowIndex|rows|rowSpan|rules|scheme|scope|screen[XY]|screenLeft|screenTop|scripts\n |scrollbars|scrolling|sectionRowIndex|security|securityPolicy|selected|selectedIndex|selection|self|shape|siblingAbove\n |siblingBelow|size|source|specified|standby|start|status|statusbar|statusText|style|styleSheets|suffixes|summary\n |systemId|systemLanguage|tagName|tags|target|tBodies|text|textAlign|textDecoration|textIndent|textTransform|tFoot|tHead\n |title|toolbar|top|type|undefined|uniqueID|updateInterval|URL|URLUnencoded|useMap|userAgent|userLanguage|userProfile\n |vAlign|value|valueType|vendor|vendorSub|version|visibility|vspace|whiteSpace|width|X[MS]LDocument|zIndex))\\b(?!\\$|\\s*(<\\s*[_$[:alpha:]\\{\\(\\[]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(\\[]([^=<>]|=[^<])+\\>)+>\\s*)?\\()", "captures": { "1": { "name": "punctuation.accessor.tsx" @@ -2573,7 +2569,7 @@ "include": "#object-identifiers" }, { - "match": "(?x)(?:(\\.)\\s*)?([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n # sure shot arrow functions even if => is on new line\n(\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*[_$[:alpha:]\\{\\(]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(]([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\((\\s*[_$[:alpha:]\\{\\(]([^()]|\\((\\s*[_$[:alpha:]\\{\\(]\\{\\(][^()]*)?\\))*)?\\) # parameteres\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n))", + "match": "(?x)(?:(\\.)\\s*)?([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n # sure shot arrow functions even if => is on new line\n(\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*[_$[:alpha:]\\{\\(\\[]([^<>=]|=[^<]|\\<\\s*[_$[:alpha:]\\{\\(\\[]([^=<>]|=[^<])+\\>)+>\\s*)? # typeparameters\n \\((\\s*[_$[:alpha:]\\{\\(]([^()]|\\((\\s*[_$[:alpha:]\\{\\(]\\{\\(][^()]*)?\\))*)?\\) # parameteres\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n))", "captures": { "1": { "name": "punctuation.accessor.tsx" @@ -4101,6 +4097,7 @@ } }, "end": "(?=[/]?>)", + "contentName": "meta.tag.attributes.tsx", "patterns": [ { "include": "#comment" diff --git a/i18n/chs/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json b/i18n/chs/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json index f1f362da927..4b77210ee8f 100644 --- a/i18n/chs/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json +++ b/i18n/chs/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json @@ -4,13 +4,13 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "activeEditorShort": "例如 myFile.txt", - "activeEditorMedium": "例如 myFolder/myFile.txt", - "activeEditorLong": "例如 /Users/Development/myProject/myFolder/myFile.txt", - "rootName": "例如 myFolder1、myFolder2、myFolder3", - "rootPath": "例如 /Users/Development/myProject", - "folderName": "例如 myFolder", - "folderPath": "例如 /Users/Development/myFolder", + "activeEditorShort": "文件名 (例如 myFile.txt)", + "activeEditorMedium": "相对于工作区文件夹的文件路径 (例如 myFolder/myFile.txt)", + "activeEditorLong": "文件的完整路径 (例如 /Users/Development/myProject/myFolder/myFile.txt)", + "rootName": "工作区名称 (例如 myFolder 或 myWorkspace)", + "rootPath": "工作区路径 (例如 /Users/Development/myWorkspace)", + "folderName": "文件所在工作区文件夹的名称 (例如 myFolder)", + "folderPath": "文件所在工作区文件夹的路径 (例如 /Users/Development/myFolder)", "appName": "例如 VS Code", "dirty": "一个更新的指示器,指示活动编辑器是否更新", "separator": "一个条件分隔符(\"-\"),仅在左右是具有值的变量时才显示", diff --git a/i18n/chs/extensions/emmet/package.i18n.json b/i18n/chs/extensions/emmet/package.i18n.json index fe933ada874..f5fda686eff 100644 --- a/i18n/chs/extensions/emmet/package.i18n.json +++ b/i18n/chs/extensions/emmet/package.i18n.json @@ -28,13 +28,6 @@ "command.incrementNumberByTen": "增加 10", "command.decrementNumberByTen": "减少 10", "emmetSyntaxProfiles": "为指定的语法定义配置文件或使用带有特定规则的配置文件。", - "emmetExclude": "不应展开 Emmet 缩写的语言数组。", - "emmetExtensionsPath": "含有 Emmet 配置文件和片段的文件夹路径。", - "emmetShowExpandedAbbreviation": "显示扩展的 Emmet 缩写作为建议。\n\"inMarkupAndStylesheetFilesOnly\" 选项适用于 html、haml、jade、slim、xml、xsl、css、scss、sass、less 和 stylus。\n\"always\" 选项适用于 markup/css 文件的所有部分。", - "emmetShowAbbreviationSuggestions": "显示可能的 Emmet 缩写作为建议。在风格表中或将 emmet.showExpandedAbbreviation 设置为 \"never\" 时不适用。", - "emmetIncludeLanguages": "启用使用默认不支持的语言的 Emmet 缩写。在此添加该语言与支持 Emmet 的语言之间的映射。示例: {\"vue-html\": \"html\", \"javascript\": \"javascriptreact\"}", - "emmetVariables": "要用于 Emmet 代码片段的变量", - "emmetTriggerExpansionOnTab": "启用后,按 TAB 键时,将展开 Emmet 缩写。", "emmetPreferences": "用于修改 Emmet 某些操作和解析程序的行为的首选项。", "emmetPreferencesIntUnit": "整数值的默认单位", "emmetPreferencesFloatUnit": "浮点数值的默认单位", @@ -44,5 +37,7 @@ "emmetPreferencesCssBetween": "展开 CSS 缩写时在 CSS 属性之间放置的符号", "emmetPreferencesSassBetween": "在 Sass 文件中展开 CSS 缩写时在 CSS 属性之间放置的符号", "emmetPreferencesStylusBetween": "在 Stylus 文件中展开 CSS 缩写时在 CSS 属性之间放置的符号", - "emmetShowSuggestionsAsSnippets": "若为 \"true\",Emmet 建议将会显示为代码片段,根据editor.snippetSuggestions 设置进行排列。" + "emmetPreferencesFilterCommentBefore": "使用注释过滤器时,应置于匹配元素前注释的定义。", + "emmetPreferencesFilterCommentAfter": "使用注释过滤器时,应置于匹配元素后注释的定义。", + "emmetPreferencesFilterCommentTrigger": "用半角逗号 (\",\") 隔开的属性名缩写的数组,将由注释筛选器应用" } \ No newline at end of file diff --git a/i18n/chs/extensions/git/out/commands.i18n.json b/i18n/chs/extensions/git/out/commands.i18n.json index 5b826945eab..c28138c3ddb 100644 --- a/i18n/chs/extensions/git/out/commands.i18n.json +++ b/i18n/chs/extensions/git/out/commands.i18n.json @@ -12,8 +12,9 @@ "cloning": "正在克隆 GIT 存储库...", "openrepo": "打开存储库", "proposeopen": "是否要打开已克隆存储库?", - "path to init": "文件夹路径", - "provide path": "请提供用于初始化 Git 存储库的文件夹路径", + "init repo": "初始化存储库", + "create repo": "初始化存储库", + "are you sure": "将在“{0}”中创建 Git 存储库。确定要继续吗?", "HEAD not available": "“{0}”的 HEAD 版本不可用。", "confirm stage files with merge conflicts": "确定要暂存含有合并冲突的 {0} 个文件吗?", "confirm stage file with merge conflicts": "确定要暂存含有合并冲突的 {0} 吗?", @@ -60,7 +61,7 @@ "push with tags success": "已成功带标签进行推送。", "nobranch": "请签出一个分支以推送到远程。", "pick remote": "选取要将分支“{0}”发布到的远程:", - "sync is unpredictable": "此操作从“{0}”推送和拉取提交。", + "sync is unpredictable": "此操作将推送提交至“{0}”,并从中拉取提交。", "ok": "确定", "never again": "好,永不再显示", "no remotes to publish": "存储库未配置任何要发布到的远程存储库。", diff --git a/i18n/chs/extensions/git/out/repository.i18n.json b/i18n/chs/extensions/git/out/repository.i18n.json index 8b700b3abe3..130408fe060 100644 --- a/i18n/chs/extensions/git/out/repository.i18n.json +++ b/i18n/chs/extensions/git/out/repository.i18n.json @@ -21,6 +21,8 @@ "deleted by us": "已被我们删除", "both added": "两者均已添加", "both modified": "二者均已修改", + "untracked, short": "U", + "modified, short": "M", "commit": "提交", "merge changes": "合并更改", "staged changes": "暂存的更改", diff --git a/i18n/chs/extensions/git/package.i18n.json b/i18n/chs/extensions/git/package.i18n.json index 35849ac0289..319483d9eeb 100644 --- a/i18n/chs/extensions/git/package.i18n.json +++ b/i18n/chs/extensions/git/package.i18n.json @@ -15,6 +15,8 @@ "command.stageAll": "暂存所有更改", "command.stageSelectedRanges": "暂存所选范围", "command.revertSelectedRanges": "还原所选更改", + "command.stageChange": "暂存更改", + "command.revertChange": "还原更改", "command.unstage": "取消暂存更改", "command.unstageAll": "取消暂存所有更改", "command.unstageSelectedRanges": "取消暂存所选范围", @@ -53,11 +55,11 @@ "config.enableLongCommitWarning": "是否针对长段提交消息进行警告", "config.confirmSync": "同步 Git 存储库前进行确认", "config.countBadge": "控制 Git 徽章计数器。“all”计算所有更改。“tracked”只计算跟踪的更改。“off”关闭此功能。", - "config.checkoutType": "控制运行“签出到...”时列出的分支的类型。“all”显示所有 refs,“local”只显示本地分支,“tags”只显示标签,“remote”只显示远程分支。", + "config.checkoutType": "控制运行“签出到...”命令时列出的分支的类型。\"all\" 显示所有 refs,\"local\" 只显示本地分支,\"tags\" 只显示标记,\"remote\" 只显示远程分支。", "config.ignoreLegacyWarning": "忽略旧版 Git 警告", "config.ignoreLimitWarning": "忽略“存储库中存在大量更改”的警告", "config.defaultCloneDirectory": "克隆 Git 存储库的默认位置", "config.enableSmartCommit": "在没有暂存的更改时提交所有更改。", "config.enableCommitSigning": "启用使用 GPG 签名的提交", - "config.discardAllScope": "控制运行\"放弃所有更改\"命令时放弃的更改。\"all\" 放弃所有更改。 \"tracked\" 只放弃跟踪的文件。 \"prompt\" 每次运行此操作时显示提示对话框。" + "config.discardAllScope": "控制运行“放弃所有更改”命令时放弃的更改类型。\"all\" 放弃所有更改。\"tracked\" 只放弃跟踪的文件。\"prompt\" 表示在每次运行此操作时显示提示对话框。" } \ No newline at end of file diff --git a/i18n/chs/extensions/typescript/package.i18n.json b/i18n/chs/extensions/typescript/package.i18n.json index 03246f64123..2fcc5d4f672 100644 --- a/i18n/chs/extensions/typescript/package.i18n.json +++ b/i18n/chs/extensions/typescript/package.i18n.json @@ -44,7 +44,8 @@ "typescript.npm": "指定用于自动获取类型的 NPM 可执行文件的路径。要求 TypeScript >= 2.3.4。", "typescript.check.npmIsInstalled": "检查是否安装了 NPM 以自动获取类型。", "javascript.nameSuggestions": "启用/禁用在 JavaScript 建议列表中包含文件中的唯一名称。", - "typescript.tsc.autoDetect": "控制自动检测 tsc 任务是否打开。", + "typescript.tsc.autoDetect": "控制 tsc 任务的自动检测。\"off\" 关闭此功能。\"build\" 仅创建单次运行编译任务。\"watch\" 仅创建编译及监视任务。\"on\" 创建构建及监视任务。默认值为 \"on\"。", "typescript.problemMatchers.tsc.label": "TypeScript 问题", - "typescript.problemMatchers.tscWatch.label": "TypeScript 问题(观看模式)" + "typescript.problemMatchers.tscWatch.label": "TypeScript 问题(观看模式)", + "typescript.quickSuggestionsForPaths": "当输入导入路径时启用或禁用快速建议。" } \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/contrib/find/browser/findWidget.i18n.json b/i18n/chs/src/vs/editor/contrib/find/browser/findWidget.i18n.json index 214239c0607..8cd6a9a0ef4 100644 --- a/i18n/chs/src/vs/editor/contrib/find/browser/findWidget.i18n.json +++ b/i18n/chs/src/vs/editor/contrib/find/browser/findWidget.i18n.json @@ -15,7 +15,6 @@ "label.replaceButton": "替换", "label.replaceAllButton": "全部替换", "label.toggleReplaceButton": "切换替换模式", - "title.matchesCountLimit": "仅前 999 个结果突出显示,但所有查找操作均针对整个文本。", "label.matchesLocation": "第 {0} 个(共 {1} 个)", "label.noResults": "无结果" } \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/contrib/find/common/findController.i18n.json b/i18n/chs/src/vs/editor/contrib/find/common/findController.i18n.json index 8f77be77375..385ba32bfeb 100644 --- a/i18n/chs/src/vs/editor/contrib/find/common/findController.i18n.json +++ b/i18n/chs/src/vs/editor/contrib/find/common/findController.i18n.json @@ -10,12 +10,6 @@ "nextSelectionMatchFindAction": "查找下一个选择", "previousSelectionMatchFindAction": "查找上一个选择", "startReplace": "替换", - "addSelectionToNextFindMatch": "将选择添加到下一个查找匹配项", - "addSelectionToPreviousFindMatch": "将选择内容添加到上一查找匹配项", - "moveSelectionToNextFindMatch": "将上次选择移动到下一个查找匹配项", - "moveSelectionToPreviousFindMatch": "将上个选择内容移动到上一查找匹配项", - "selectAllOccurrencesOfFindMatch": "选择所有找到的查找匹配项", - "changeAll.label": "更改所有匹配项", "showNextFindTermAction": "显示下一个搜索结果", "showPreviousFindTermAction": "显示上一个搜索结果" } \ No newline at end of file diff --git a/i18n/chs/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json b/i18n/chs/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json index 88bbf6c3a12..3c402b850dd 100644 --- a/i18n/chs/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json +++ b/i18n/chs/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json @@ -6,5 +6,11 @@ { "mutlicursor.insertAbove": "在上面添加光标", "mutlicursor.insertBelow": "在下面添加光标", - "mutlicursor.insertAtEndOfEachLineSelected": "在行尾添加光标" + "mutlicursor.insertAtEndOfEachLineSelected": "在行尾添加光标", + "addSelectionToNextFindMatch": "将选择添加到下一个查找匹配项", + "addSelectionToPreviousFindMatch": "将选择内容添加到上一查找匹配项", + "moveSelectionToNextFindMatch": "将上次选择移动到下一个查找匹配项", + "moveSelectionToPreviousFindMatch": "将上个选择内容移动到上一查找匹配项", + "selectAllOccurrencesOfFindMatch": "选择所有找到的查找匹配项", + "changeAll.label": "更改所有匹配项" } \ No newline at end of file diff --git a/i18n/chs/src/vs/platform/theme/common/colorRegistry.i18n.json b/i18n/chs/src/vs/platform/theme/common/colorRegistry.i18n.json index 0784d4fbc11..6f4c25c20dd 100644 --- a/i18n/chs/src/vs/platform/theme/common/colorRegistry.i18n.json +++ b/i18n/chs/src/vs/platform/theme/common/colorRegistry.i18n.json @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "invalid.color": "颜色格式无效。请使用 #RGB、#RGBA、#RRGGBB 或 #RRGGBBAA", "schema.colors": "工作台中使用的颜色。", "foreground": "整体前景色。此颜色仅在不被组件覆盖时适用。", "errorForeground": "错误信息的整体前景色。此颜色仅在不被组件覆盖时适用。", 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 47a9c28f390..16082cd725c 100644 --- a/i18n/chs/src/vs/workbench/browser/actions/workspaceActions.i18n.json +++ b/i18n/chs/src/vs/workbench/browser/actions/workspaceActions.i18n.json @@ -9,14 +9,17 @@ "addFolderToWorkspace": "将文件夹添加到工作区...", "add": "添加(&&A)", "addFolderToWorkspaceTitle": "将文件夹添加到工作区", + "globalRemoveFolderFromWorkspace": "将文件夹从工作区删除…", "newWorkspace": "新建工作区...", "select": "选择(&&S)", "selectWorkspace": "选择文件夹作为工作区", "removeFolderFromWorkspace": "将文件夹从工作区删除", + "openFolderSettings": "打开文件夹设置", "saveWorkspaceAsAction": "将工作区另存为...", "save": "保存(&&S)", "saveWorkspace": "保存工作区", "openWorkspaceAction": "打开工作区...", "openWorkspaceConfigFile": "打开工作区配置文件", + "openFolderAsWorkspaceInNewWindow": "在新窗口中将文件夹作为工作区打开", "workspaceFolderPickerPlaceholder": "选择工作区文件夹" } \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json b/i18n/chs/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json index 9b17b8e6572..ab430ed5a0d 100644 --- a/i18n/chs/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json +++ b/i18n/chs/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json @@ -5,6 +5,5 @@ // Do not edit this file. It is machine generated. { "hideActivitBar": "隐藏活动栏", - "activityBarAriaLabel": "活动视图切换器", "globalActions": "全局动作" } \ No newline at end of file diff --git a/src/vs/editor/contrib/folding/common/folding.ts b/i18n/chs/src/vs/workbench/browser/parts/compositebar/compositeBar.i18n.json similarity index 61% rename from src/vs/editor/contrib/folding/common/folding.ts rename to i18n/chs/src/vs/workbench/browser/parts/compositebar/compositeBar.i18n.json index c0f677cb95c..738f1c78726 100644 --- a/src/vs/editor/contrib/folding/common/folding.ts +++ b/i18n/chs/src/vs/workbench/browser/parts/compositebar/compositeBar.i18n.json @@ -2,14 +2,7 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ - -import { IEditorContribution } from 'vs/editor/common/editorCommon'; - -export const ID = 'editor.contrib.folding'; - -export interface IFoldingController extends IEditorContribution { - - foldAll(): void; - unfoldAll(): void; - +// Do not edit this file. It is machine generated. +{ + "activityBarAriaLabel": "活动视图切换器" } \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/browser/parts/compositebar/compositeBarActions.i18n.json b/i18n/chs/src/vs/workbench/browser/parts/compositebar/compositeBarActions.i18n.json new file mode 100644 index 00000000000..bcd8d15ce40 --- /dev/null +++ b/i18n/chs/src/vs/workbench/browser/parts/compositebar/compositeBarActions.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "badgeTitle": "{0} - {1}", + "additionalViews": "其他视图", + "numberBadge": "{0} ({1})", + "manageExtension": "管理扩展", + "titleKeybinding": "{0} ({1})", + "hide": "隐藏", + "toggle": "切换已固定的视图" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json b/i18n/chs/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json index 96695e518e9..a2f0727f579 100644 --- a/i18n/chs/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json +++ b/i18n/chs/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json @@ -12,5 +12,6 @@ "groupTwoPicker": "在第二组中显示编辑器", "groupThreePicker": "在第三组中显示编辑器", "allEditorsPicker": "显示所有已打开的编辑器", - "view": "查看" + "view": "查看", + "file": "文件" } \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json b/i18n/chs/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json index 11e4c9243b7..fbfbe4e5483 100644 --- a/i18n/chs/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json +++ b/i18n/chs/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json @@ -4,6 +4,5 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "canNotRun": "命令“{0}”当前未启用,无法运行。", "manageExtension": "管理扩展" } \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/electron-browser/main.contribution.i18n.json b/i18n/chs/src/vs/workbench/electron-browser/main.contribution.i18n.json index b2ac4ce4c5a..dd88bf14419 100644 --- a/i18n/chs/src/vs/workbench/electron-browser/main.contribution.i18n.json +++ b/i18n/chs/src/vs/workbench/electron-browser/main.contribution.i18n.json @@ -10,18 +10,15 @@ "workspaces": "工作区", "developer": "开发者", "showEditorTabs": "控制打开的编辑器是否显示在选项卡中。", - "workbench.editor.labelFormat.default": "显示文件名。当启用选项卡且在同一组内有两个相同名称的文件时,将添加每个文件路径中可以用于区分的部分。在选项卡被禁用且编辑器活动时,将显示相对于工作区根目录的路径。", "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": "控制是否将打开的编辑器显示为预览。预览编辑器将会重用至其被保留(例如,通过双击或编辑),且其字体样式将为斜体。", "enablePreviewFromQuickOpen": "控制 Quick Open 中打开的编辑器是否显示为预览。预览编辑器可以重新使用,直到将其保留(例如,通过双击或编辑)。", - "editorOpenPositioning": "控制打开编辑器的位置。选择“左侧”或“右侧”以在当前活动位置的左侧或右侧打开编辑器。选择“第一个”或“最后一个”以从当前活动位置独立打开编辑器。", "revealIfOpen": "控制打开时编辑器是否显示在任何可见组中。如果禁用,编辑器会优先在当前活动编辑器组中打开。如果启用,会显示已打开的编辑器而不是在当前活动编辑器组中再次打开。请注意,有些情况下会忽略此设置,例如强制编辑器在特定组中或在当前活动组的边侧打开时。", - "commandHistory": "控制命令面板中保留最近使用命令的数量。设置为 0 则禁用命令历史记录。", + "commandHistory": "控制命令面板中保留最近使用命令的数量。设置为 0 时禁用命令历史功能。", "preserveInput": "控制是否在再次打开命令面板时恢复上一次的输入内容。", "closeOnFocusLost": "控制 Quick Open 是否应在失去焦点时自动关闭。", "openDefaultSettings": "控制打开设置时是否打开显示所有默认设置的编辑器。", @@ -50,7 +47,7 @@ "restoreWindows": "控制重启后重新打开窗口的方式。选择 \"none\" 则永远在启动时打开一个空工作区,\"one\" 则重新打开最后使用的窗口,\"folders\" 则重新打开所有含有文件夹的窗口,\"all\" 则重新打开上次会话的所有窗口。", "restoreFullscreen": "如果窗口已退出全屏模式,控制其是否应还原为全屏模式。", "zoomLevel": "调整窗口的缩放级别。原始大小是 0,每次递增(例如 1)或递减(例如 -1)表示放大或缩小 20%。也可以输入小数以便以更精细的粒度调整缩放级别。", - "title": "基于活动编辑器控制窗口标题。变量基于上下文进行替换:\n${activeEditorShort}: 例如 myFile.txt\n${activeEditorMedium}: 例如 myFolder/myFile.txt\n${activeEditorLong}: 例如 /Users/Development/myProject/myFolder/myFile.txt\n${folderName}: 例如 myFolder\n${folderPath}: 例如 /Users/Development/myFolder\n${rootName}: 例如 myFolder1, myFolder2, myFolder3\n${rootPath}: 例如 /Users/Development/myWorkspace\n${appName}: 例如 VS Code\n${dirty}: 更新的指示器(如果活动编辑器已更新)\n${separator}: 仅在被带值的变量包围时显示的条件分隔符(\" - \")", + "title": "根据活动编辑器控制窗口标题。变量基于上下文进行替换:\n${activeEditorShort}: 文件名 (如 myFile.txt)\n${activeEditorMedium}: 相对于工作区文件夹的文件路径 (如 myFolder/myFile.txt)\n${activeEditorLong}: 文件的完整路径 (如 /Users/Development/myProject/myFolder/myFile.txt)\n${folderName}: 文件所在工作区文件夹名称 (如 myFolder)\n${folderPath}: 文件所在工作区文件夹路径 (如 /Users/Development/myFolder)\n${rootName}: 工作区名称 (如 myFolder 或 myWorkspace)\n${rootPath}: 工作区路径 (如 /Users/Development/myWorkspace)\n${appName}: 如 VS Code\n${dirty}: 活动编辑器有更新时显示的更新指示器\n${separator}: 仅在被有值变量包围时显示的分隔符 (\" - \")", "window.newWindowDimensions.default": "在屏幕中心打开新窗口。", "window.newWindowDimensions.inherit": "以与上一个活动窗口相同的尺寸打开新窗口。", "window.newWindowDimensions.maximized": "打开最大化的新窗口。", diff --git a/i18n/chs/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json b/i18n/chs/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json index 1b158fedfe7..6c03799a8b6 100644 --- a/i18n/chs/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json +++ b/i18n/chs/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json @@ -6,6 +6,8 @@ { "entryAriaLabel": "{0},调试", "debugAriaLabel": "键入启动配置的名称以运行。", + "addConfigTo": "添加配置 ({0})...", + "addConfiguration": "添加配置...", "noConfigurationsMatching": "无任何调试配置匹配", "noConfigurationsFound": "找不到任何调试配置。请创建一个 \"launch.json\" 文件。" } \ No newline at end of file diff --git a/test/smoke/src/areas/window.ts b/i18n/chs/src/vs/workbench/parts/debug/browser/debugStatus.i18n.json similarity index 59% rename from test/smoke/src/areas/window.ts rename to i18n/chs/src/vs/workbench/parts/debug/browser/debugStatus.i18n.json index 69182b4e537..d8dee095caa 100644 --- a/test/smoke/src/areas/window.ts +++ b/i18n/chs/src/vs/workbench/parts/debug/browser/debugStatus.i18n.json @@ -2,17 +2,7 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ - -import { SpectronApplication } from '../spectron/application'; - -export class Window { - - constructor(private spectron: SpectronApplication) { - - } - - public async getTitle(): Promise { - return this.spectron.client.getTitle(); - } - -} +// Do not edit this file. It is machine generated. +{ + "debug": "调试" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/debug/browser/debugViewlet.i18n.json b/i18n/chs/src/vs/workbench/parts/debug/browser/debugViewlet.i18n.json new file mode 100644 index 00000000000..fcbe0434d7e --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/debug/browser/debugViewlet.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "debugFocusVariablesView": "聚焦于变量视图", + "debugFocusWatchView": "聚焦于监视视图", + "debugFocusCallStackView": "聚焦于调用堆栈视图", + "debugFocusBreakpointsView": "聚焦于断点视图" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json b/i18n/chs/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json index 327f1b6fd82..fd6211c4e06 100644 --- a/i18n/chs/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json +++ b/i18n/chs/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json @@ -15,7 +15,6 @@ "vscode.extension.contributes.debuggers.initialConfigurations": "用于生成初始 \"launch.json\" 的配置。", "vscode.extension.contributes.debuggers.languages": "可能被视为“默认调试程序”的调试扩展的语言列表。", "vscode.extension.contributes.debuggers.adapterExecutableCommand": "如果指定的 VS Code 将调用此命令以确定调试适配器的可执行路径和要传递的参数。", - "vscode.extension.contributes.debuggers.startSessionCommand": "如果指定的 VS Code 将为针对此扩展的“调试”或“运行”操作调用此命令。", "vscode.extension.contributes.debuggers.configurationSnippets": "用于在 \"launch.json\" 中添加新配置的代码段。", "vscode.extension.contributes.debuggers.configurationAttributes": "用于验证 \"launch.json\" 的 JSON 架构配置。", "vscode.extension.contributes.debuggers.windows": "Windows 特定的设置。", 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 0a2caf6c161..8a42e364b44 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,8 +12,8 @@ "breakpointRemoved": "已删除断点,行 {0},文件 {1}", "compoundMustHaveConfigurations": "复合项必须拥有 \"configurations\" 属性集,才能启动多个配置。", "configMissing": "\"launch.json\" 中缺少配置“{0}”。", - "debugRequestNotSupported": "所选的调试配置有一个不支持的属性值“{0}”: “{1}”。", - "debugRequesMissing": "所选的调试配置缺少属性“{0}”。", + "debugRequestNotSupported": "所选调试配置的属性“{0}”的值“{1}”不受支持。", + "debugRequesMissing": "在所选的调试配置中缺少属性 '{0}' 。", "debugTypeNotSupported": "配置的类型“{0}”不受支持。", "debugTypeMissing": "所选的启动配置缺少属性 \"type\"。", "preLaunchTaskErrors": "preLaunchTask“{0}”期间检测到多个生成错误。", diff --git a/i18n/chs/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json b/i18n/chs/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json index 16828d670d2..1de1dd184f1 100644 --- a/i18n/chs/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json +++ b/i18n/chs/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json @@ -8,5 +8,5 @@ "replVariableAriaLabel": "变量 {0} 具有值 {1}、读取 Eval 打印循环,调试", "replExpressionAriaLabel": "表达式 {0} 具有值 {1},读取 Eval 打印循环,调试", "replValueOutputAriaLabel": "{0},读取 Eval 打印循环,调试", - "replKeyValueOutputAriaLabel": "输出变量 {0} 具有值 {1},读取 Eval 打印循环,调试" + "replRawObjectAriaLabel": "Repl(交互式解释器)变量 {0} 具有值 {1},读取 求值 输出 循环,调试" } \ No newline at end of file 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 e137dd70e6b..eb4a6b8039f 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 @@ -44,7 +44,7 @@ "showOutdatedExtensions": "显示过时扩展", "showPopularExtensions": "显示常用的扩展", "showRecommendedExtensions": "显示推荐的扩展", - "showWorkspaceRecommendedExtensions": "显示工作区建议的扩展名", + "showWorkspaceRecommendedExtensions": "显示根据工作区推荐的扩展", "showRecommendedKeymapExtensions": "显示推荐键映射", "showRecommendedKeymapExtensionsShort": "键映射", "showLanguageExtensions": "显示语言扩展", diff --git a/i18n/chs/src/vs/workbench/parts/files/browser/fileActions.i18n.json b/i18n/chs/src/vs/workbench/parts/files/browser/fileActions.i18n.json index 95ca006c03e..9e7feeb37b4 100644 --- a/i18n/chs/src/vs/workbench/parts/files/browser/fileActions.i18n.json +++ b/i18n/chs/src/vs/workbench/parts/files/browser/fileActions.i18n.json @@ -23,6 +23,7 @@ "confirmMoveTrashMessageFile": "是否确实要删除“{0}”?", "undoBin": "可以从回收站还原。", "undoTrash": "可以从回收站还原。", + "doNotAskAgain": "不再询问", "confirmDeleteMessageFolder": "是否确定要永久删除“{0}”及其内容?", "confirmDeleteMessageFile": "是否确定要永久删除“{0}”?", "irreversible": "此操作不可逆!", @@ -37,8 +38,6 @@ "openToSide": "打开到侧边", "compareSource": "选择以进行比较", "globalCompareFile": "比较活动文件与...", - "pickHistory": "选择要进行比较的之前已打开的文件", - "unableToFileToCompare": "无法将所选文件与“{0}”进行比较。", "openFileToCompare": "首先打开文件以将其与另外一个文件比较。", "compareWith": "将“{0}”与“{1}”比较", "compareFiles": "比较文件", @@ -47,7 +46,7 @@ "saveAs": "另存为...", "saveAll": "全部保存", "saveAllInGroup": "保存组中的全部内容", - "saveFiles": "保存已更新文件", + "saveFiles": "保存所有文件", "revert": "还原文件", "focusOpenEditors": "专注于“打开的编辑器”视图", "focusFilesExplorer": "关注文件资源浏览器", diff --git a/i18n/chs/src/vs/workbench/parts/files/browser/files.contribution.i18n.json b/i18n/chs/src/vs/workbench/parts/files/browser/files.contribution.i18n.json index 7f0b0600897..32f442dc95c 100644 --- a/i18n/chs/src/vs/workbench/parts/files/browser/files.contribution.i18n.json +++ b/i18n/chs/src/vs/workbench/parts/files/browser/files.contribution.i18n.json @@ -40,10 +40,14 @@ "dynamicHeight": "控制打开的编辑器部分的高度是否应动态适应元素数量。", "autoReveal": "控制资源管理器是否应在打开文件时自动显示并选择它们。", "enableDragAndDrop": "控制资源管理器是否应该允许通过拖放移动文件和文件夹。", + "confirmDragAndDrop": "控制资源管理器是否应在拖拽移动文件或文件夹时进行确认。", + "confirmDelete": "控制资源管理器是否应在删除文件到回收站时进行确认。", "sortOrder.default": "按名称的字母顺序排列文件和文件夹。文件夹显示在文件前。", "sortOrder.mixed": "按名称的字母顺序排列文件和文件夹。两者穿插显示。", "sortOrder.filesFirst": "按名称的字母顺序排列文件和文件夹。文件显示在文件夹前。", "sortOrder.type": "按扩展名的字母顺序排列文件和文件夹。文件夹显示在文件前。", "sortOrder.modified": "按最后修改日期降序排列文件和文件夹。文件夹显示在文件前。", - "sortOrder": "控制资源管理器文件和文件夹的排列顺序。除了默认排列顺序,你也可以设置为 \"mixed\" (文件和文件夹一起排序)、\"type\" (按文件类型排)、\"modified\" (按最后修改日期排)或是 \"filesFirst\" (将文件排在文件夹前)。" + "sortOrder": "控制资源管理器文件和文件夹的排列顺序。除了默认排列顺序,你也可以设置为 \"mixed\" (文件和文件夹一起排序)、\"type\" (按文件类型排)、\"modified\" (按最后修改日期排)或是 \"filesFirst\" (将文件排在文件夹前)。", + "explorer.decorations.colors": "控制文件修饰是否用颜色。", + "explorer.decorations.badges": "控制文件修饰是否用徽章。" } \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json b/i18n/chs/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json index 51ca4525bf3..5939052419b 100644 --- a/i18n/chs/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json +++ b/i18n/chs/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json @@ -6,6 +6,8 @@ { "noWorkspace": "无打开的文件夹", "explorerSection": "文件资源管理器部分", - "noWorkspaceHelp": "尚未打开文件夹。", + "noWorkspaceHelp": "你还没有在工作区中添加文件夹。", + "addFolder": "添加文件夹", + "noFolderHelp": "尚未打开文件夹。", "openFolder": "打开文件夹" } \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json b/i18n/chs/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json index cd38244eb23..02057ebfda8 100644 --- a/i18n/chs/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json +++ b/i18n/chs/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json @@ -4,12 +4,15 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { + "canNotResolve": "无法解析文件夹 {0}", "fileInputAriaLabel": "键入文件名。按 Enter 以确认或按 Esc 以取消。", "filesExplorerViewerAriaLabel": "{0},文件资源管理器", "dropFolders": "你是否要将文件夹添加到工作区?", "dropFolder": "你是否要将文件夹添加到工作区?", "addFolders": "添加文件夹(&&A)", "addFolder": "添加文件夹(&&A)", + "confirmMove": "是否确实要移动“{0}”?", + "doNotAskAgain": "不再询问", "confirmOverwriteMessage": "目标文件夹中已存在“{0}”。是否要将其替换?", "irreversible": "此操作不可逆!", "replaceButtonLabel": "替换(&&R)" diff --git a/i18n/chs/src/vs/workbench/parts/markers/browser/markersFileDecorations.i18n.json b/i18n/chs/src/vs/workbench/parts/markers/browser/markersFileDecorations.i18n.json new file mode 100644 index 00000000000..6536d821661 --- /dev/null +++ b/i18n/chs/src/vs/workbench/parts/markers/browser/markersFileDecorations.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "label": "问题", + "tooltip.1": "此文件存在 1 个问题", + "tooltip.N": "此文件存在 {0} 个问题", + "markers.showOnFile": "显示关于文件与文件夹的错误与警告。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json b/i18n/chs/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json index c70f6d72be0..123be43ab7b 100644 --- a/i18n/chs/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json +++ b/i18n/chs/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json @@ -5,7 +5,6 @@ // Do not edit this file. It is machine generated. { "emptyUserSettingsHeader": "将设置放入此处以覆盖\"默认设置\"。", - "errorInvalidConfiguration": "无法写入设置。请更正文件中的错误/警告,然后重试。", "emptyWorkspaceSettingsHeader": "将设置放入此处以覆盖\"用户设置\"。", "emptyFolderSettingsHeader": "将文件夹设置放入此处以覆盖\"工作区设置\"。", "defaultFolderSettingsTitle": "默认文件夹设置", diff --git a/i18n/chs/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json b/i18n/chs/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json index e66ea5624b1..30778096b47 100644 --- a/i18n/chs/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json +++ b/i18n/chs/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json @@ -13,7 +13,6 @@ "actionNotEnabled": "在当前上下文中没有启用命令“{0}”。", "recentlyUsed": "最近使用", "morecCommands": "其他命令", - "commandLabel": "{0}: {1}", "cat.title": "{0}: {1}", "noCommandsMatching": "没有匹配的命令" } \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json b/i18n/chs/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json index c4aae56e0d9..839def0b2a6 100644 --- a/i18n/chs/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json +++ b/i18n/chs/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json @@ -4,6 +4,10 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { + "changes": "第 {0} 个更改 (共 {1} 个)", + "change": "第 {0} 个更改 (共 {1} 个)", + "show previous change": "显示上一个更改", + "show next change": "显示下一个更改", "editorGutterModifiedBackground": "编辑器导航线中被修改行的背景颜色。", "editorGutterAddedBackground": "编辑器导航线中已插入行的背景颜色。", "editorGutterDeletedBackground": "编辑器导航线中被删除行的背景颜色。", diff --git a/i18n/chs/src/vs/workbench/parts/search/browser/search.contribution.i18n.json b/i18n/chs/src/vs/workbench/parts/search/browser/search.contribution.i18n.json index ad7c43ade10..3fbefcc2302 100644 --- a/i18n/chs/src/vs/workbench/parts/search/browser/search.contribution.i18n.json +++ b/i18n/chs/src/vs/workbench/parts/search/browser/search.contribution.i18n.json @@ -17,7 +17,7 @@ "exclude": "配置 glob 模式以在搜索中排除文件和文件夹。从 files.exclude 设置中继承所有 glob 模式。", "exclude.boolean": "匹配文件路径所依据的 glob 模式。设置为 true 或 false 可启用或禁用该模式。", "exclude.when": "对匹配文件的同级文件的其他检查。使用 $(basename) 作为匹配文件名的变量。", - "useRipgrep": "控制是否在文本搜索中使用 ripgrep", + "useRipgrep": "控制是否在文本和文件搜索中使用 ripgrep", "useIgnoreFilesByDefault": "控制在新工作区中搜索时是否默认使用 .gitignore 和 .ignore 文件。", "search.quickOpen.includeSymbols": "配置为在 Quick Open 文件结果中包括全局符号搜索的结果。" } \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/search/browser/searchActions.i18n.json b/i18n/chs/src/vs/workbench/parts/search/browser/searchActions.i18n.json index 6af8f4d7574..9b159465788 100644 --- a/i18n/chs/src/vs/workbench/parts/search/browser/searchActions.i18n.json +++ b/i18n/chs/src/vs/workbench/parts/search/browser/searchActions.i18n.json @@ -19,7 +19,6 @@ "ClearSearchResultsAction.label": "清除搜索结果", "FocusNextSearchResult.label": "聚焦下一搜索结果", "FocusPreviousSearchResult.label": "聚焦上一搜索结果", - "RemoveAction.label": "删除", "file.replaceAll.label": "全部替换", "match.replace.label": "替换" } \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json b/i18n/chs/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json index d2110b961fc..9da8ac35792 100644 --- a/i18n/chs/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json +++ b/i18n/chs/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json @@ -10,8 +10,8 @@ "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。", + "badFile": "无法读取代码片段文件“{0}”。", "source.snippet": "用户代码片段", "detail.snippet": "{0} ({1})", "snippetSuggest.longLabel": "{0},{1}" 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 925312c210a..9e55d0c22e6 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 @@ -14,6 +14,7 @@ "runningTasks": "显示运行中的任务", "tasks": "任务", "TaskSystem.noHotSwap": "在有活动任务运行时更换任务执行引擎需要重新加载窗口", + "TaskServer.folderIgnored": "由于使用任务版本 0.1.0,文件夹 {0} 将被忽略", "TaskService.noBuildTask1": "未定义任何生成任务。使用 \"isBuildCommand\" 在 tasks.json 文件中标记任务。", "TaskService.noBuildTask2": "未定义任何生成任务。在 tasks.json 文件中将任务标记为 \"build\" 组。", "TaskService.noTestTask1": "未定义任何测试任务。使用 \"isTestCommand\" 在 tasks.json 文件中标记任务。", @@ -30,6 +31,7 @@ "TaskSystem.activeSame.noBackground": "任务 \"{0}\" 已处于活动状态。若要终止任务,请选择“任务”菜单中的“终止任务...”。", "TaskSystem.active": "当前已有任务正在运行。请先终止它,然后再执行另一项任务。", "TaskSystem.restartFailed": "未能终止并重启任务 {0}", + "TaskService.noConfiguration": "错误: {0} 任务检测没有提供拥有下列配置的任务:\n{1}\n将忽略此任务。", "TaskSystem.configurationErrors": "错误: 提供的任务配置具有验证错误,无法使用。请首先改正错误。", "taskService.ignoreingFolder": "将忽略工作区文件夹 {0} 的任务配置。多文件夹工作区任务支持要求所有文件夹使用任务版本 2.0.0\n", "TaskSystem.invalidTaskJson": "错误: tasks.json 文件的内容具有语法错误。请先更正错误然后再执行任务。\n", diff --git a/i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json b/i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json index ab9987787d1..892b51d9473 100644 --- a/i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json +++ b/i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json @@ -17,6 +17,7 @@ "terminal.integrated.fontFamily": "控制终端的字体系列,这在编辑器中是默认的。fontFamily 的值。", "terminal.integrated.fontSize": "控制终端的字号(以像素为单位)。", "terminal.integrated.lineHeight": "控制终端的行高,此数字乘以终端字号得到实际行高(以像素表示)。", + "terminal.integrated.enableBold": "是否在终端内启用粗体文本,注意这需要终端命令行的支持。", "terminal.integrated.cursorBlinking": "控制终端光标是否闪烁。", "terminal.integrated.cursorStyle": "控制终端游标的样式。", "terminal.integrated.scrollback": "控制终端保持在缓冲区的最大行数。", diff --git a/i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json b/i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json index 61a809c2168..d96d23dca0e 100644 --- a/i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json +++ b/i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json @@ -16,7 +16,6 @@ "workbench.action.terminal.new.short": "新的终端", "workbench.action.terminal.focus": "聚焦于终端", "workbench.action.terminal.focusNext": "聚焦于下一终端", - "workbench.action.terminal.focusAtIndex": "焦点终端 {0}", "workbench.action.terminal.focusPrevious": "聚焦于上一终端", "workbench.action.terminal.paste": "粘贴到活动终端中", "workbench.action.terminal.DefaultShell": "选择默认 Shell", diff --git a/i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json b/i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json index 204cfda7f73..6c965409e92 100644 --- a/i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json +++ b/i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.i18n.json @@ -6,5 +6,5 @@ { "terminalLinkHandler.followLinkAlt": "Alt + 单击以访问链接", "terminalLinkHandler.followLinkCmd": "Cmd + 单击以跟踪链接", - "terminalLinkHandler.followLinkCtrl": "Ctrl + 单击以跟踪链接" + "terminalLinkHandler.followLinkCtrl": "按住 Ctrl 并单击可访问链接" } \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json b/i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json index d3e2c12d9a6..837bb66bacc 100644 --- a/i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json +++ b/i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json @@ -5,7 +5,6 @@ // Do not edit this file. It is machine generated. { "copy": "复制", - "createNewTerminal": "新的终端", "paste": "粘贴", "selectAll": "全选", "clear": "清除" diff --git a/i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json b/i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json index 27b7ce1e9c3..6b431f37d7f 100644 --- a/i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json +++ b/i18n/chs/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json @@ -10,6 +10,5 @@ "never again": "好,永不再显示", "terminal.integrated.chooseWindowsShell": "选择首选的终端 shell,你可稍后在设置中进行更改", "terminalService.terminalCloseConfirmationSingular": "存在一个活动的终端会话,是否要终止此会话?", - "terminalService.terminalCloseConfirmationPlural": "存在 {0} 个活动的终端会话,是否要终止这些会话?", - "yes": "是" + "terminalService.terminalCloseConfirmationPlural": "存在 {0} 个活动的终端会话,是否要终止这些会话?" } \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/services/configuration/common/configurationExtensionPoint.i18n.json b/i18n/chs/src/vs/workbench/services/configuration/common/configurationExtensionPoint.i18n.json new file mode 100644 index 00000000000..2eb80022de4 --- /dev/null +++ b/i18n/chs/src/vs/workbench/services/configuration/common/configurationExtensionPoint.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "vscode.extension.contributes.configuration.title": "设置摘要。此标签将在设置文件中用作分隔注释。", + "vscode.extension.contributes.configuration.properties": "配置属性的描述。", + "scope.window.description": "特定于窗口的配置,可在“用户”或“工作区”设置中配置。", + "scope.resource.description": "特定于资源的配置,可在“用户”、“工作区”或“文件夹”设置中配置。", + "scope.description": "配置适用的范围。可用范围有“窗口”和“资源”。", + "vscode.extension.contributes.configuration": "用于配置字符串。", + "invalid.title": "configuration.title 必须是字符串", + "vscode.extension.contributes.defaultConfiguration": "按语言提供默认编辑器配置设置。", + "invalid.properties": "configuration.properties 必须是对象", + "invalid.allOf": "\"configuration.allOf\" 已被弃用且不应被使用。你可以将多个配置单元作为数组传递给 \"configuration\" 参与点。", + "workspaceConfig.folders.description": "将载入到工作区的文件夹列表。", + "workspaceConfig.path.description": "文件路径。例如 \"/root/folderA\" 或 \"./folderA\"。后者表示根据工作区文件位置进行解析的相对路径。", + "workspaceConfig.name.description": "文件夹的可选名称。", + "workspaceConfig.uri.description": "文件夹的 URI", + "workspaceConfig.settings.description": "工作区设置", + "workspaceConfig.extensions.description": "工作区扩展", + "unknownWorkspaceProperty": "未知的工作区配置属性" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/services/editor/common/editorService.i18n.json b/i18n/chs/src/vs/workbench/services/editor/common/editorService.i18n.json new file mode 100644 index 00000000000..50e968f8ee3 --- /dev/null +++ b/i18n/chs/src/vs/workbench/services/editor/common/editorService.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. +{ + "compareLabels": "{0} ↔ {1}" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json b/i18n/chs/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json index 3006c199103..1c27ab2249d 100644 --- a/i18n/chs/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json +++ b/i18n/chs/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json @@ -6,6 +6,7 @@ { "schema.token.settings": "标记的颜色和样式。", "schema.token.foreground": "标记的前景色。", + "schema.token.background.warning": "暂不支持标记背景色。", "schema.token.fontStyle": "规则字体样式:“斜体”、“粗体”和“下划线”中的一种或者组合", "schema.fontStyle.error": "字体样式必须为 \"italic\"(斜体)、 \"bold\"(粗体)和 \"underline\"(下划线)的组合。", "schema.properties.name": "规则的描述。", diff --git a/i18n/chs/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json b/i18n/chs/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json index 182830f549e..47cfacc0457 100644 --- a/i18n/chs/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json +++ b/i18n/chs/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json @@ -33,5 +33,6 @@ "schema.fontSize": "使用某种字体时: 文本字体的字体大小(以百分比表示)。如果未设置,则默认为字体定义中的大小。", "schema.fontId": "使用某种字体时: 字体的 ID。如果未设置,则默认为第一个字体定义。", "schema.light": "浅色主题中文件图标的可选关联。", - "schema.highContrast": "高对比度颜色主题中文件图标的可选关联。" + "schema.highContrast": "高对比度颜色主题中文件图标的可选关联。", + "schema.hidesExplorerArrows": "配置文件资源管理器的箭头是否应在此主题启用时隐藏。" } \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/services/themes/electron-browser/colorThemeStore.i18n.json b/i18n/chs/src/vs/workbench/services/themes/electron-browser/colorThemeStore.i18n.json new file mode 100644 index 00000000000..61d59fe560d --- /dev/null +++ b/i18n/chs/src/vs/workbench/services/themes/electron-browser/colorThemeStore.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "vscode.extension.contributes.themes": "请提供 TextMate 颜色主题。", + "vscode.extension.contributes.themes.id": "用户设置中使用的图标主题的 ID。", + "vscode.extension.contributes.themes.label": "显示在 UI 中的颜色主题标签。", + "vscode.extension.contributes.themes.uiTheme": "用于定义编辑器周围颜色的基本主题: \"vs\" 是浅色主题,\"vs-dark\" 是深色主题。\"hc-black\" 是深色高对比度主题。", + "vscode.extension.contributes.themes.path": "tmTheme 文件的路径。该路径相对于扩展文件夹,通常为 \"./themes/themeFile.tmTheme\"。", + "reqarray": "扩展点“{0}”必须是一个数组。", + "reqpath": "“contributes.{0}.path”中应为字符串。提供的值: {1}", + "invalid.path.1": "“contributes.{0}.path”({1})应包含在扩展的文件夹({2})内。这可能会使扩展不可移植。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.i18n.json b/i18n/chs/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.i18n.json new file mode 100644 index 00000000000..0ccb5d7ff4d --- /dev/null +++ b/i18n/chs/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.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. +{ + "error.cannotparseicontheme": "分析文件图标文件时出现问题: {0}" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.i18n.json b/i18n/chs/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.i18n.json new file mode 100644 index 00000000000..239f4582a9f --- /dev/null +++ b/i18n/chs/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "vscode.extension.contributes.iconThemes": "提供文件图标主题。", + "vscode.extension.contributes.iconThemes.id": "用户设置中使用的图标主题的 ID。", + "vscode.extension.contributes.iconThemes.label": "UI 中显示的图标主题的标签。", + "vscode.extension.contributes.iconThemes.path": "图标主题定义文件的路径。该路径相对于扩展文件夹,通常是 \"./icons/awesome-icon-theme.json\"。", + "reqarray": "扩展点“{0}”必须是一个数组。", + "reqpath": "“contributes.{0}.path”中应为字符串。提供的值: {1}", + "reqid": "contributes.{0}.id\" 中的预期字符串。提供的值: {1}", + "invalid.path.1": "“contributes.{0}.path”({1})应包含在扩展的文件夹({2})内。这可能会使扩展不可移植。" +} \ No newline at end of file diff --git a/i18n/chs/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json b/i18n/chs/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json index fae9207b8f3..2a7c7dc77d5 100644 --- a/i18n/chs/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json +++ b/i18n/chs/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json @@ -4,31 +4,15 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "vscode.extension.contributes.themes": "Contributes textmate color themes.", - "vscode.extension.contributes.themes.id": "用户设置中使用的图标主题的 ID。", - "vscode.extension.contributes.themes.label": "显示在 UI 中的颜色主题标签。", - "vscode.extension.contributes.themes.uiTheme": "用于定义编辑器周围颜色的基本主题: \"vs\" 是浅色主题,\"vs-dark\" 是深色主题。\"hc-black\" 是深色高对比度主题。", - "vscode.extension.contributes.themes.path": "tmTheme 文件的路径。该路径相对于扩展文件夹,通常为 \"./themes/themeFile.tmTheme\"。", - "vscode.extension.contributes.iconThemes": "Contributes file icon themes.", - "vscode.extension.contributes.iconThemes.id": "用户设置中使用的图标主题的 ID。", - "vscode.extension.contributes.iconThemes.label": "UI 中显示的图标主题的标签。", - "vscode.extension.contributes.iconThemes.path": "图标主题定义文件的路径。该路径相对于扩展文件夹,通常是 \"./icons/awesome-icon-theme.json\"。", "migration.completed": "已向用户设置添加了新的主题设置。{0} 中可备份。", "error.cannotloadtheme": "无法加载 {0}: {1}", - "reqarray": "扩展点“{0}”必须是一个数组。", - "reqpath": "“contributes.{0}.path”中应为字符串。提供的值: {1}", - "invalid.path.1": "“contributes.{0}.path”({1})应包含在扩展的文件夹({2})内。这可能会使扩展不可移植。", - "reqid": "“contributes.{0}.id”中应为字符串。提供的值: {1}", "error.cannotloadicontheme": "Unable to load {0}", - "error.cannotparseicontheme": "Problems parsing file icons file: {0}", "colorTheme": "指定工作台中使用的颜色主题。", "colorThemeError": "主题未知或未安装。", "iconTheme": "指定在工作台中使用的图标主题,或指定 \"null\" 以不显示任何文件图标。", "noIconThemeDesc": "No file icons", "iconThemeError": "文件图标主题未知或未安装。", "workbenchColors": "覆盖当前所选颜色主题的颜色。", - "workbenchColors.deprecated": "该设置不再是实验性设置,并已重命名为“workbench.colorCustomizations”", - "workbenchColors.deprecatedDescription": "改用“workbench.colorCustomizations”", "editorColors": "覆盖当前所选颜色主题中的编辑器颜色和字体样式。", "editorColors.comments": "设置注释的颜色和样式", "editorColors.strings": "设置字符串文本的颜色和样式", diff --git a/i18n/chs/src/vs/workbench/services/workspace/node/workspaceEditingService.i18n.json b/i18n/chs/src/vs/workbench/services/workspace/node/workspaceEditingService.i18n.json new file mode 100644 index 00000000000..9db48716791 --- /dev/null +++ b/i18n/chs/src/vs/workbench/services/workspace/node/workspaceEditingService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "openWorkspaceConfigurationFile": "打开工作区配置文件", + "close": "关闭" +} \ No newline at end of file diff --git a/i18n/cht/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json b/i18n/cht/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json index 54a0b2aaf23..885fb5847ec 100644 --- a/i18n/cht/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json +++ b/i18n/cht/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json @@ -4,13 +4,8 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "activeEditorShort": "例如 myFile.txt", - "activeEditorMedium": "例如 myFolder/myFile.txt", - "activeEditorLong": "例如 /Users/Development/myProject/myFolder/myFile.txt", - "rootName": "例如 myFolder1, , myFolder2, myFolder3", - "rootPath": "例如 /Users/Development/myProject", - "folderName": "例如 myFolder", - "folderPath": "例如 /Users/Development/myFolder", + "activeEditorShort": "檔案名稱(例如:myFile.txt)", + "activeEditorMedium": "檔案相對於工作區資料夾的相對路徑(例如:myFolder/myFile.txt)", "appName": "例如 VS Code", "dirty": "若使用中的編輯器已變更,即為已變更的指示區", "separator": "條件式分隔符號 (' - '),只會在前後為具有值的變數時顯示", diff --git a/i18n/cht/extensions/emmet/package.i18n.json b/i18n/cht/extensions/emmet/package.i18n.json index 392c4b2278a..84abd814d8a 100644 --- a/i18n/cht/extensions/emmet/package.i18n.json +++ b/i18n/cht/extensions/emmet/package.i18n.json @@ -28,13 +28,6 @@ "command.incrementNumberByTen": "依 10 遞增", "command.decrementNumberByTen": "依 10 遞減", "emmetSyntaxProfiles": "為指定的語法定義設定檔,或透過特定規則使用自己的設定檔。", - "emmetExclude": "不應展開 Emmet 縮寫的語言陣列。", - "emmetExtensionsPath": "包含 Emmet 設定檔與程式碼片段的資料夾路徑。」", - "emmetShowExpandedAbbreviation": "顯示建議之展開的 Emmet 縮寫\n\"inMarkupAndStylesheetFilesOnly\" 選項適用於 html、haml、jade、slim、xml、xsl、css、scss、sass、less 和 stylus。\n不論標記/css 為何,該選項「一律」適用於檔案的所有部分。", - "emmetShowAbbreviationSuggestions": "顯示建議之可能的 Emmet 縮寫。但在樣式表內,或若 emmet.showExpandedAbbreviation 設為「永不」,則不適用。", - "emmetIncludeLanguages": "以預設未支援的語言啟用 Emmet 縮寫。在此新增該語言和 Emmet 支援的語言間之對應。例如: {\"vue-html\": \"html\", \"javascript\": \"javascriptreact\"} ", - "emmetVariables": "要在 Emmet 程式碼片段中使用的變數", - "emmetTriggerExpansionOnTab": "如有啟用,只要按 Tab 鍵就能展開 Emmet 縮寫。", "emmetPreferences": "喜好設定,用以修改某些動作的行為及 Emmet 的解析程式。", "emmetPreferencesIntUnit": "整數值的預設單位", "emmetPreferencesFloatUnit": "浮點值的預設單位", @@ -43,6 +36,5 @@ "emmetPreferencesStylusAfter": "在手寫筆檔案中展開 CSS 縮寫時,要放在 CSS 屬性結尾的符號", "emmetPreferencesCssBetween": "展開 CSS 縮寫時,要放在 CSS 屬性與值之間的符號", "emmetPreferencesSassBetween": "在 SASS 檔案中展開 CSS 縮寫時,要放在 CSS 屬性與值之間的符號", - "emmetPreferencesStylusBetween": "在手寫筆檔案中展開 CSS 縮寫時,要放在 CSS 屬性與值之間的符號", - "emmetShowSuggestionsAsSnippets": "若為 true,則 Emmet 建議會顯示為程式碼片段,可讓您在每個 editor.snippetSuggestions 設定為其排序。" + "emmetPreferencesStylusBetween": "在手寫筆檔案中展開 CSS 縮寫時,要放在 CSS 屬性與值之間的符號" } \ No newline at end of file diff --git a/i18n/cht/extensions/git/out/commands.i18n.json b/i18n/cht/extensions/git/out/commands.i18n.json index d7ef0b15a46..6c7a6f8972a 100644 --- a/i18n/cht/extensions/git/out/commands.i18n.json +++ b/i18n/cht/extensions/git/out/commands.i18n.json @@ -12,8 +12,8 @@ "cloning": "正在複製 Git 儲存庫...", "openrepo": "開啟儲存庫", "proposeopen": "要開啟複製的儲存庫嗎?", - "path to init": "資料夾路徑", - "provide path": "請提供資料夾路徑以初始化 Git 存放庫", + "init repo": "初始化儲存庫", + "create repo": "初始化儲存庫", "HEAD not available": "'{0}' 的 HEAD 版本無法使用。", "confirm stage files with merge conflicts": "確定要暫存 {0} 個有合併衝突的檔案嗎?", "confirm stage file with merge conflicts": "確定要暫存有合併衝突的 {0} 嗎?", diff --git a/i18n/cht/extensions/git/out/repository.i18n.json b/i18n/cht/extensions/git/out/repository.i18n.json index c07485bdb63..9cbc280931f 100644 --- a/i18n/cht/extensions/git/out/repository.i18n.json +++ b/i18n/cht/extensions/git/out/repository.i18n.json @@ -21,6 +21,8 @@ "deleted by us": "已受到我們刪除", "both added": "皆已新增", "both modified": "皆已修改", + "untracked, short": "U", + "modified, short": "M", "commit": "認可", "merge changes": "合併變更", "staged changes": "已分段的變更", diff --git a/i18n/cht/extensions/typescript/package.i18n.json b/i18n/cht/extensions/typescript/package.i18n.json index e56e400373b..a881af4077d 100644 --- a/i18n/cht/extensions/typescript/package.i18n.json +++ b/i18n/cht/extensions/typescript/package.i18n.json @@ -44,7 +44,6 @@ "typescript.npm": "指定用於自動類型取得的 NPM 可執行檔路徑。TypeScript 必須 >= 2.3.4.", "typescript.check.npmIsInstalled": "檢查是否已安裝NPM用以取得自動類型擷取.", "javascript.nameSuggestions": "從JavaScript推薦表檔案中啟用/停用包含唯一檔名", - "typescript.tsc.autoDetect": "控制 tsc 工作的自動偵測為開啟或關閉。", "typescript.problemMatchers.tsc.label": "TypeScript 問題", "typescript.problemMatchers.tscWatch.label": " TypeScript 問題 (監看模式)" } \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/common/view/editorColorRegistry.i18n.json b/i18n/cht/src/vs/editor/common/view/editorColorRegistry.i18n.json index 4cdf779ce91..85ff9ee951e 100644 --- a/i18n/cht/src/vs/editor/common/view/editorColorRegistry.i18n.json +++ b/i18n/cht/src/vs/editor/common/view/editorColorRegistry.i18n.json @@ -6,7 +6,7 @@ { "lineHighlight": "目前游標位置行的反白顯示背景色彩。", "lineHighlightBorderBox": "目前游標位置行之周圍框線的背景色彩。", - "rangeHighlight": "反白顯示範圍的背景色彩,例如 Quick Open 與尋找功能。", + "rangeHighlight": "反白顯示範圍的背景色彩,例如快速開啟與尋找功能。", "caret": "編輯器游標的色彩。", "editorCursorBackground": "編輯器游標的背景色彩。允許自訂區塊游標重疊的字元色彩。", "editorWhitespaces": "編輯器中空白字元的色彩。", diff --git a/i18n/cht/src/vs/editor/contrib/find/browser/findWidget.i18n.json b/i18n/cht/src/vs/editor/contrib/find/browser/findWidget.i18n.json index 32ac75ef725..19132dbfd98 100644 --- a/i18n/cht/src/vs/editor/contrib/find/browser/findWidget.i18n.json +++ b/i18n/cht/src/vs/editor/contrib/find/browser/findWidget.i18n.json @@ -15,7 +15,6 @@ "label.replaceButton": "取代", "label.replaceAllButton": "全部取代", "label.toggleReplaceButton": "切換取代模式", - "title.matchesCountLimit": "只會將前 999 筆結果醒目提示,但所有尋找作業會在完整文字上執行。", "label.matchesLocation": "{0} / {1}", "label.noResults": "沒有結果" } \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/contrib/find/common/findController.i18n.json b/i18n/cht/src/vs/editor/contrib/find/common/findController.i18n.json index c75e923d0b7..cbbf89b710f 100644 --- a/i18n/cht/src/vs/editor/contrib/find/common/findController.i18n.json +++ b/i18n/cht/src/vs/editor/contrib/find/common/findController.i18n.json @@ -10,12 +10,6 @@ "nextSelectionMatchFindAction": "尋找下一個選取項目", "previousSelectionMatchFindAction": "尋找上一個選取項目", "startReplace": "取代", - "addSelectionToNextFindMatch": "將選取項目加入下一個找到的相符項", - "addSelectionToPreviousFindMatch": "將選取項目加入前一個找到的相符項中", - "moveSelectionToNextFindMatch": "將最後一個選擇項目移至下一個找到的相符項", - "moveSelectionToPreviousFindMatch": "將最後一個選擇項目移至前一個找到的相符項", - "selectAllOccurrencesOfFindMatch": "選取所有找到的相符項目", - "changeAll.label": "變更所有發生次數", "showNextFindTermAction": "顯示下一個尋找字詞", "showPreviousFindTermAction": "顯示上一個尋找字詞" } \ No newline at end of file diff --git a/i18n/cht/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json b/i18n/cht/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json index 722b3100d2e..381e4cfcb4c 100644 --- a/i18n/cht/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json +++ b/i18n/cht/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json @@ -6,5 +6,11 @@ { "mutlicursor.insertAbove": "在上方加入游標", "mutlicursor.insertBelow": "在下方加入游標", - "mutlicursor.insertAtEndOfEachLineSelected": "在行尾新增游標" + "mutlicursor.insertAtEndOfEachLineSelected": "在行尾新增游標", + "addSelectionToNextFindMatch": "將選取項目加入下一個找到的相符項", + "addSelectionToPreviousFindMatch": "將選取項目加入前一個找到的相符項中", + "moveSelectionToNextFindMatch": "將最後一個選擇項目移至下一個找到的相符項", + "moveSelectionToPreviousFindMatch": "將最後一個選擇項目移至前一個找到的相符項", + "selectAllOccurrencesOfFindMatch": "選取所有找到的相符項目", + "changeAll.label": "變更所有發生次數" } \ No newline at end of file diff --git a/i18n/cht/src/vs/platform/theme/common/colorRegistry.i18n.json b/i18n/cht/src/vs/platform/theme/common/colorRegistry.i18n.json index 5f7d8743b2b..1ec0770a363 100644 --- a/i18n/cht/src/vs/platform/theme/common/colorRegistry.i18n.json +++ b/i18n/cht/src/vs/platform/theme/common/colorRegistry.i18n.json @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "invalid.color": "色彩格式無效。請使用 #RGB、#RGBA、#RRGGBB 或 #RRGGBBAA", "schema.colors": "工作台中使用的色彩。", "foreground": "整體的前景色彩。僅當未被任何元件覆疊時,才會使用此色彩。", "errorForeground": "整體錯誤訊息的前景色彩。僅當未被任何元件覆蓋時,才會使用此色彩。", 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 18a591310bd..604755744c9 100644 --- a/i18n/cht/src/vs/workbench/browser/actions/workspaceActions.i18n.json +++ b/i18n/cht/src/vs/workbench/browser/actions/workspaceActions.i18n.json @@ -13,6 +13,7 @@ "select": "選取(&&S)", "selectWorkspace": "為工作區選取資料夾", "removeFolderFromWorkspace": "將資料夾從工作區移除", + "openFolderSettings": "開啟資料夾設定", "saveWorkspaceAsAction": "另存工作區為...", "save": "儲存(&&S)", "saveWorkspace": "儲存工作區", diff --git a/i18n/cht/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json b/i18n/cht/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json index e2c33864cd6..1ca947dd5de 100644 --- a/i18n/cht/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json +++ b/i18n/cht/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json @@ -5,6 +5,5 @@ // Do not edit this file. It is machine generated. { "hideActivitBar": "隱藏活動列", - "activityBarAriaLabel": "即時檢視切換器", "globalActions": "全域動作" } \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/browser/parts/compositebar/compositeBar.i18n.json b/i18n/cht/src/vs/workbench/browser/parts/compositebar/compositeBar.i18n.json new file mode 100644 index 00000000000..313c717e038 --- /dev/null +++ b/i18n/cht/src/vs/workbench/browser/parts/compositebar/compositeBar.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. +{ + "activityBarAriaLabel": "即時檢視切換器" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/browser/parts/compositebar/compositeBarActions.i18n.json b/i18n/cht/src/vs/workbench/browser/parts/compositebar/compositeBarActions.i18n.json new file mode 100644 index 00000000000..0ab2723d455 --- /dev/null +++ b/i18n/cht/src/vs/workbench/browser/parts/compositebar/compositeBarActions.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "badgeTitle": "{0} - {1}", + "additionalViews": "其他檢視", + "numberBadge": "{0} ({1})", + "manageExtension": "管理延伸模組", + "titleKeybinding": "{0} ({1})", + "hide": "隱藏", + "toggle": "切換釘選的檢視" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json b/i18n/cht/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json index 156d3f5bd02..fc25ce13ad0 100644 --- a/i18n/cht/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json +++ b/i18n/cht/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json @@ -12,5 +12,6 @@ "groupTwoPicker": "在第二個群組顯示編輯器", "groupThreePicker": "在第三個群組顯示編輯器", "allEditorsPicker": "顯示所有開啟的編輯器", - "view": "檢視" + "view": "檢視", + "file": "檔案" } \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json b/i18n/cht/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json index d59f4257e4e..05f1e85eb7b 100644 --- a/i18n/cht/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json +++ b/i18n/cht/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json @@ -4,6 +4,5 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "canNotRun": "命令 '{0}' 目前未啟用,因此無法執行。", "manageExtension": "管理延伸模組" } \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/electron-browser/main.contribution.i18n.json b/i18n/cht/src/vs/workbench/electron-browser/main.contribution.i18n.json index 8b6818a6c02..af13c8df70b 100644 --- a/i18n/cht/src/vs/workbench/electron-browser/main.contribution.i18n.json +++ b/i18n/cht/src/vs/workbench/electron-browser/main.contribution.i18n.json @@ -10,18 +10,14 @@ "workspaces": "工作區", "developer": "開發人員", "showEditorTabs": "控制已開啟的編輯器是否應顯示在索引標籤中。", - "workbench.editor.labelFormat.default": "顯示檔案的名稱。當啟用索引標籤時,若在一個群組中有兩個檔案使用相同的名稱,就會新增每個檔案路徑具有辨識度的區段。當索引標籤停用時,若編輯器在使用中,則會顯示工作區根目錄的相關路徑。", "workbench.editor.labelFormat.short": "顯示檔案的名稱,並在名稱後接著該檔案的目錄名稱。", - "workbench.editor.labelFormat.medium": "顯示檔案的名稱,並在名稱後接著該檔案與工作區根目錄有關的路徑。", "workbench.editor.labelFormat.long": "顯示檔案的名稱,並在名稱後接著該檔案的絕對路徑。", "tabDescription": "控制編輯器的標籤格式。變更此設定具有多項優點,例如可讓檔案的位置更加清楚:\n-簡短: 'parent'\n-中等: 'workspace/src/parent'\n-完整: '/home/user/workspace/src/parent'\n-預設: '.../parent',當另一個索引標籤共用相同的標題,或若路徑停用,卻共用相關的工作區路徑時", "editorTabCloseButton": "控制編輯器的索引標籤關閉按鈕位置,或在設為 'off' 時將其停用。", "showIcons": "控制開啟的編輯器是否搭配圖示顯示。這需要同時啟用圖示佈景主題。", "enablePreview": "控制已開啟的編輯器是否顯示為預覽。預覽編輯器會重複使用到被保留 (例如按兩下或進行編輯) 並以斜體字型樣式顯示為止。", "enablePreviewFromQuickOpen": "控制透過 Quick Open 所開啟的編輯器是否顯示為預覽。預覽編輯器會重複使用到被保留 (例如按兩下或進行編輯) 為止。", - "editorOpenPositioning": "控制要在何處開啟編輯器。選取 [左] 或 [右] 在目前使用中編輯器的左側或右側開啟編輯器。選取 [先] 或 [後] 從目前使用中編輯器另外開啟編輯器。", "revealIfOpen": "控制編輯器是否要在任何顯示的群組開啟時,在其中顯示。若啟用此選項,已經開啟的編輯器將會繼續顯示,而不會在目前使用中的編輯器群組中再開啟一次。請注意,有一些情況會忽略此設定,例如當強制編輯器在特定群組中開啟,或強制編輯器在目前使用中的群組旁開啟等情況。", - "commandHistory": "控制是否要保留在命令選擇區歷程記錄中最近所使用的命令數目。設定為 0 以停用命令歷程記錄。", "preserveInput": "控制下次開啟命令選擇區時,最後鍵入的輸入是否應該還原。", "closeOnFocusLost": "控制是否在 Quick Open 失去焦點時自動關閉。", "openDefaultSettings": "控制開啟設定時是否也會開啟顯示所有預設設定的編輯器。", @@ -50,7 +46,6 @@ "restoreWindows": "控制重新啟動後視窗重新開啟的方式。選取 [無] 一律以空白工作區開始、選取 [一] 從上一個編輯的視窗重新開啟、選取 [資料夾] 重新開啟所有資料夾曾經開啟的視窗,或選取 [全部] 重新開啟上一個工作階段的所有視窗。", "restoreFullscreen": "控制當視窗在全螢幕模式下結束後,下次是否仍以全螢幕模式開啟。", "zoomLevel": "調整視窗的縮放比例。原始大小為 0,而且每個向上增量 (例如 1) 或向下增量 (例如 -1) 代表放大或縮小 20%。您也可以輸入小數,更細微地調整縮放比例。", - "title": "依據使用中的編輯器控制視窗標題。變數會依據內容替換:\n${activeEditorShort}: 例如 myFile.txt\n${activeEditorMedium}: 例如 myFolder/myFile.txt\n${activeEditorLong}: 例如 /Users/Development/myProject/myFolder/myFile.txt\n${folderName}: 例如 myFolder\n${folderPath}: 例如 /Users/Development/myFolder\n${rootName}: 例如 myFolder1,myFolder2,myFolder3\n${rootPath}: 例如 /Users/Development/myWorkspace\n${appName}: 例如 VS Code\n${dirty}: 用以指出編輯器經過變更的指標\n${separator}: 條件式分隔符號 (' - '),只會在前後為具有值的變數時顯示", "window.newWindowDimensions.default": "在螢幕中央開啟新視窗。", "window.newWindowDimensions.inherit": "以相同於上一個使用中之視窗的維度開啟新視窗。", "window.newWindowDimensions.maximized": "開啟並最大化新視窗。", diff --git a/i18n/cht/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json b/i18n/cht/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json index b6a825d8df7..86768e58c63 100644 --- a/i18n/cht/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json +++ b/i18n/cht/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json @@ -6,6 +6,8 @@ { "entryAriaLabel": "{0},偵錯", "debugAriaLabel": "輸入要執行之啟動組態的名稱。", + "addConfigTo": "新增組態 ({0})...", + "addConfiguration": "新增組態...", "noConfigurationsMatching": "沒有任何相符的偵錯組態", "noConfigurationsFound": "找不到任何偵錯組態。請建立'launch.json' 檔案。" } \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/debug/browser/debugStatus.i18n.json b/i18n/cht/src/vs/workbench/parts/debug/browser/debugStatus.i18n.json new file mode 100644 index 00000000000..24c491ad8e0 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/debug/browser/debugStatus.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. +{ + "debug": "偵錯" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/debug/browser/debugViewlet.i18n.json b/i18n/cht/src/vs/workbench/parts/debug/browser/debugViewlet.i18n.json new file mode 100644 index 00000000000..8b6ad71cd4e --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/debug/browser/debugViewlet.i18n.json @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json b/i18n/cht/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json index d0e90daa9e8..19f502a82ef 100644 --- a/i18n/cht/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json +++ b/i18n/cht/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json @@ -15,7 +15,6 @@ "vscode.extension.contributes.debuggers.initialConfigurations": "組態,用於產生初始 'launch.json'。", "vscode.extension.contributes.debuggers.languages": "可將偵錯延伸模組視為「預設偵錯工具」的語言清單。", "vscode.extension.contributes.debuggers.adapterExecutableCommand": "如有指定,VS Code 會呼叫此命令以決定偵錯配接器的可執行檔路徑及要傳遞的引數。", - "vscode.extension.contributes.debuggers.startSessionCommand": "如有指定,VS Code 會為以此延伸模組為目標的「偵錯」或「執行」動作呼叫此命令。", "vscode.extension.contributes.debuggers.configurationSnippets": "用於在 'launch.json' 中新增組態的程式碼片段。", "vscode.extension.contributes.debuggers.configurationAttributes": "JSON 結構描述組態,用於驗證 'launch.json'。", "vscode.extension.contributes.debuggers.windows": "Windows 特定設定。", diff --git a/i18n/cht/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json b/i18n/cht/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json index 4bdc5fb94f2..c949e5dbf8b 100644 --- a/i18n/cht/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json +++ b/i18n/cht/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json @@ -12,10 +12,7 @@ "breakpointRemoved": "已移除中斷點,行 {0},檔案 {1}", "compoundMustHaveConfigurations": "複合必須設有 \"configurations\" 屬性,才能啟動多個組態。", "configMissing": "'launch.json' 中遺漏組態 '{0}'。", - "debugRequestNotSupported": "所選的偵錯組態具有不支援的屬性值 '{0}': '{1}'。", - "debugRequesMissing": "所選的偵錯組態遺漏屬性 '{0}'。", "debugTypeNotSupported": "不支援設定的偵錯類型 '{0}'。", - "debugTypeMissing": "遺漏所選啟動設定的屬性 'type'。", "preLaunchTaskErrors": "執行 preLaunchTask '{0}' 期間偵測到建置錯誤。", "preLaunchTaskError": "執行 preLaunchTask '{0}' 期間偵測到建置錯誤。", "preLaunchTaskExitCode": "preLaunchTask '{0}' 已終止,結束代碼為 {1}。", diff --git a/i18n/cht/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json b/i18n/cht/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json index e7982d93dfd..39f259be4a4 100644 --- a/i18n/cht/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json +++ b/i18n/cht/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json @@ -7,6 +7,5 @@ "stateCapture": "第一次評估會擷取物件狀態", "replVariableAriaLabel": "變數 {0} 具有值 {1},「讀取、求值、輸出」迴圈,偵錯", "replExpressionAriaLabel": "運算式 {0} 具有值 {1},「讀取、求值、輸出」迴圈,偵錯", - "replValueOutputAriaLabel": "{0},「讀取、求值、輸出」迴圈,偵錯", - "replKeyValueOutputAriaLabel": "輸出變數 {0} 具有值 {1},「讀取、求值、輸出」迴圈,偵錯" + "replValueOutputAriaLabel": "{0},「讀取、求值、輸出」迴圈,偵錯" } \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/files/browser/fileActions.i18n.json b/i18n/cht/src/vs/workbench/parts/files/browser/fileActions.i18n.json index 0ce10edb281..141887ab75f 100644 --- a/i18n/cht/src/vs/workbench/parts/files/browser/fileActions.i18n.json +++ b/i18n/cht/src/vs/workbench/parts/files/browser/fileActions.i18n.json @@ -37,8 +37,6 @@ "openToSide": "開至側邊", "compareSource": "選取用以比較", "globalCompareFile": "使用中檔案的比較對象...", - "pickHistory": "選取先前開啟的檔案以相比較", - "unableToFileToCompare": "選取的檔案無法與 '{0}' 進行比較。", "openFileToCompare": "先開啟檔案以與其他檔案進行比較", "compareWith": "比較 '{0}' 與 '{1}'", "compareFiles": "比較檔案", @@ -47,7 +45,6 @@ "saveAs": "另存新檔...", "saveAll": "全部儲存", "saveAllInGroup": "全部儲存在群組中", - "saveFiles": "儲存已變更的檔案", "revert": "還原檔案", "focusOpenEditors": "聚焦在 [開放式編輯器] 檢視", "focusFilesExplorer": "將焦點設在檔案總管上", diff --git a/i18n/cht/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json b/i18n/cht/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json index 22aa2fcc887..19ea692c744 100644 --- a/i18n/cht/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json +++ b/i18n/cht/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json @@ -6,6 +6,6 @@ { "noWorkspace": "沒有開啟的資料夾", "explorerSection": "檔案總管區段", - "noWorkspaceHelp": "您尚未開啟資料夾。", + "noFolderHelp": "您尚未開啟資料夾。", "openFolder": "開啟資料夾" } \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/markers/browser/markersFileDecorations.i18n.json b/i18n/cht/src/vs/workbench/parts/markers/browser/markersFileDecorations.i18n.json new file mode 100644 index 00000000000..79cb63cbd79 --- /dev/null +++ b/i18n/cht/src/vs/workbench/parts/markers/browser/markersFileDecorations.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. +{ + "label": "問題" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json b/i18n/cht/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json index 12df0eb0474..fdeeb3c2a27 100644 --- a/i18n/cht/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json +++ b/i18n/cht/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json @@ -5,7 +5,6 @@ // Do not edit this file. It is machine generated. { "emptyUserSettingsHeader": "將您的設定放置在此以覆寫預設設定。", - "errorInvalidConfiguration": "無法寫入設定.請開啟使用者設定並修正檔案中的錯誤/警告後再試一次.", "emptyWorkspaceSettingsHeader": "將您的設定放置在此以覆寫使用者設定。", "emptyFolderSettingsHeader": "將您的資料夾設定放置在此以覆寫工作區設定的資料夾設定。", "defaultFolderSettingsTitle": "預設資料夾設定", diff --git a/i18n/cht/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json b/i18n/cht/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json index 6b7c584d6f2..510d9ec3ad3 100644 --- a/i18n/cht/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json +++ b/i18n/cht/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json @@ -13,7 +13,6 @@ "actionNotEnabled": "目前內容中未啟用命令 '{0}'。", "recentlyUsed": "最近使用的", "morecCommands": "其他命令", - "commandLabel": "{0}: {1}", "cat.title": "{0}: {1}", "noCommandsMatching": "沒有相符的命令" } \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/search/browser/search.contribution.i18n.json b/i18n/cht/src/vs/workbench/parts/search/browser/search.contribution.i18n.json index 533ad58e7aa..5bd2e68d516 100644 --- a/i18n/cht/src/vs/workbench/parts/search/browser/search.contribution.i18n.json +++ b/i18n/cht/src/vs/workbench/parts/search/browser/search.contribution.i18n.json @@ -17,7 +17,6 @@ "exclude": "設定 Glob 模式,以排除不要搜尋的檔案及資料夾。請從 file.exclude 設定繼承所有的 Glob 模式。", "exclude.boolean": "要符合檔案路徑的 Glob 模式。設為 True 或 False 可啟用或停用模式。", "exclude.when": "在相符檔案同層級上額外的檢查。請使用 $(basename) 作為相符檔案名稱的變數。", - "useRipgrep": "控制是否要在文字搜尋中使用 ripgrep", "useIgnoreFilesByDefault": "控制在搜尋新的工作區時,是否要根據預設使用 .gitignore 及 .ignore 檔案。", "search.quickOpen.includeSymbols": "設定以將全域符號搜尋的結果納入 Quick Open 的檔案結果中。" } \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/search/browser/searchActions.i18n.json b/i18n/cht/src/vs/workbench/parts/search/browser/searchActions.i18n.json index 52826c8f8d8..c638d855adf 100644 --- a/i18n/cht/src/vs/workbench/parts/search/browser/searchActions.i18n.json +++ b/i18n/cht/src/vs/workbench/parts/search/browser/searchActions.i18n.json @@ -19,7 +19,6 @@ "ClearSearchResultsAction.label": "清除搜尋結果", "FocusNextSearchResult.label": "聚焦於下一個搜尋結果", "FocusPreviousSearchResult.label": "聚焦於上一個搜尋結果", - "RemoveAction.label": "移除", "file.replaceAll.label": "全部取代", "match.replace.label": "取代" } \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json b/i18n/cht/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json index 8f1cdc4c641..a7b095ad28a 100644 --- a/i18n/cht/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json +++ b/i18n/cht/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json @@ -10,8 +10,8 @@ "vscode.extension.contributes.snippets": "提供程式碼片段。", "vscode.extension.contributes.snippets-language": "要予以提供此程式碼片段的語言識別碼。", "vscode.extension.contributes.snippets-path": "程式碼片段檔案的路徑。此路徑是擴充功能資料夾的相對路徑,而且一般會以 './snippets/' 開頭。", - "badFile": "無法讀取程式碼片段檔案 \"{0}\"。", "badVariableUse": "程式碼片段 \"{0}\" 很可能會混淆 snippet-variables 及 snippet-placeholders。如需詳細資料,請參閱 https://code.visualstudio.com/docs/editor/userdefinedsnippets#_snippet-syntax。", + "badFile": "無法讀取程式碼片段檔案 \"{0}\"。", "source.snippet": "使用者程式碼片段", "detail.snippet": "{0} ({1})", "snippetSuggest.longLabel": "{0},{1}" diff --git a/i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json b/i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json index db8c0bb2809..40342458179 100644 --- a/i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json +++ b/i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json @@ -16,7 +16,6 @@ "workbench.action.terminal.new.short": "新增終端機", "workbench.action.terminal.focus": "聚焦終端機", "workbench.action.terminal.focusNext": "聚焦下一個終端機", - "workbench.action.terminal.focusAtIndex": "聚焦終端機 {0}", "workbench.action.terminal.focusPrevious": "聚焦上一個終端機", "workbench.action.terminal.paste": "貼入使用中的終端機", "workbench.action.terminal.DefaultShell": "選取預設殼層", diff --git a/i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json b/i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json index f9f8c2b8d9e..4c35ec5d698 100644 --- a/i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json +++ b/i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json @@ -5,7 +5,6 @@ // Do not edit this file. It is machine generated. { "copy": "複製", - "createNewTerminal": "新增終端機", "paste": "貼上", "selectAll": "全選", "clear": "清除" diff --git a/i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json b/i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json index 19e71491bf0..fc6b0624a08 100644 --- a/i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json +++ b/i18n/cht/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json @@ -10,6 +10,5 @@ "never again": "確定,不要再顯示", "terminal.integrated.chooseWindowsShell": "請選取所需的終端機殼層。您之後可以在設定中變更此選擇", "terminalService.terminalCloseConfirmationSingular": "仍有一個使用中的終端機工作階段。要予以終止嗎?", - "terminalService.terminalCloseConfirmationPlural": "目前共有 {0} 個使用中的終端機工作階段。要予以終止嗎?", - "yes": "是" + "terminalService.terminalCloseConfirmationPlural": "目前共有 {0} 個使用中的終端機工作階段。要予以終止嗎?" } \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/services/configuration/common/configurationExtensionPoint.i18n.json b/i18n/cht/src/vs/workbench/services/configuration/common/configurationExtensionPoint.i18n.json new file mode 100644 index 00000000000..2bc73e75628 --- /dev/null +++ b/i18n/cht/src/vs/workbench/services/configuration/common/configurationExtensionPoint.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "vscode.extension.contributes.configuration.title": "設定的摘要。此標籤將會在設定檔中作為分隔註解使用。", + "vscode.extension.contributes.configuration.properties": "組態屬性的描述。", + "scope.window.description": "視窗特定組態,可在使用者或工作區設定中予以設定。", + "scope.resource.description": "資源特定設定,可在使用者、工作區或資料夾設定中予以設定。", + "scope.description": "組態適用的範圍。可用的範圍為「視窗」和「資源」。", + "vscode.extension.contributes.configuration": "提供組態設定。", + "invalid.title": "'configuration.title' 必須是字串", + "vscode.extension.contributes.defaultConfiguration": "依語言貢獻預設編輯器組態設定。", + "invalid.properties": "'configuration.properties' 必須是物件", + "invalid.allOf": "'configuration.allOf' 已取代而不應再使用。請改為將多個組態區段作為陣列,傳遞至「組態」貢獻點。", + "workspaceConfig.folders.description": "要載入工作區之資料夾的清單。", + "workspaceConfig.path.description": "檔案路徑,例如 `/root/folderA` 或 `./folderA` 即為會對工作區檔案位置解析的相關路徑。", + "workspaceConfig.name.description": "資料夾的選用名稱。", + "workspaceConfig.uri.description": "資料夾的 URI", + "workspaceConfig.settings.description": "工作區設定", + "workspaceConfig.extensions.description": "工作區延伸模組", + "unknownWorkspaceProperty": "未知的工作區組態屬性" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/services/editor/common/editorService.i18n.json b/i18n/cht/src/vs/workbench/services/editor/common/editorService.i18n.json new file mode 100644 index 00000000000..50e968f8ee3 --- /dev/null +++ b/i18n/cht/src/vs/workbench/services/editor/common/editorService.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. +{ + "compareLabels": "{0} ↔ {1}" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/services/themes/electron-browser/colorThemeStore.i18n.json b/i18n/cht/src/vs/workbench/services/themes/electron-browser/colorThemeStore.i18n.json new file mode 100644 index 00000000000..f1b7981878d --- /dev/null +++ b/i18n/cht/src/vs/workbench/services/themes/electron-browser/colorThemeStore.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "vscode.extension.contributes.themes": "提供 Textmate 彩色佈景主題。", + "vscode.extension.contributes.themes.id": "用在使用者設定中的圖示佈景主題識別碼。", + "vscode.extension.contributes.themes.label": "如 UI 中所示的彩色佈景主題標籤。", + "vscode.extension.contributes.themes.uiTheme": "基底佈景主題定義編輯器的色彩: 'vs' 是淺色佈景主題,'vs-dark' 是深色佈景主題。'hc-black' 是深色高對比佈景主題。", + "vscode.extension.contributes.themes.path": "tmTheme 檔案的路徑。此路徑是擴充功能資料夾的相對路徑,通常為 './themes/themeFile.tmTheme'。", + "reqarray": "擴充點 '{0}' 必須是陣列。", + "reqpath": "'contributes.{0}.path' 中應有字串。提供的值: {1}", + "invalid.path.1": "擴充功能資料夾 ({2}) 應包含 'contributes.{0}.path' ({1})。這可能會導致擴充功能無法移植。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.i18n.json b/i18n/cht/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.i18n.json new file mode 100644 index 00000000000..e7493947e49 --- /dev/null +++ b/i18n/cht/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.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. +{ + "error.cannotparseicontheme": "剖析檔案的圖示檔時發生問題: {0}" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.i18n.json b/i18n/cht/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.i18n.json new file mode 100644 index 00000000000..dd6a68fb722 --- /dev/null +++ b/i18n/cht/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "vscode.extension.contributes.iconThemes": "貢獻檔案圖示佈景主題。", + "vscode.extension.contributes.iconThemes.id": "用在使用者設定中的圖示佈景主題識別碼。", + "vscode.extension.contributes.iconThemes.label": "以 UI 顯示的圖示佈景主題標籤。", + "vscode.extension.contributes.iconThemes.path": "圖示佈景主題定義檔案。路徑相對於擴充功能資料夾,通常為 './icons/awesome-icon-theme.json'。", + "reqarray": "擴充點 '{0}' 必須是陣列。", + "reqpath": "'contributes.{0}.path' 中應有字串。提供的值: {1}", + "reqid": "`contributes.{0}.id` 中應有字串。提供的值: {1}", + "invalid.path.1": "擴充功能資料夾 ({2}) 應包含 'contributes.{0}.path' ({1})。這可能會導致擴充功能無法移植。" +} \ No newline at end of file diff --git a/i18n/cht/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json b/i18n/cht/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json index 34021b15c0a..5ab16a62ce2 100644 --- a/i18n/cht/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json +++ b/i18n/cht/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json @@ -4,31 +4,15 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "vscode.extension.contributes.themes": "Contributes textmate color themes.", - "vscode.extension.contributes.themes.id": "用在使用者設定中的圖示佈景主題識別碼。", - "vscode.extension.contributes.themes.label": "如 UI 中所示的彩色佈景主題標籤。", - "vscode.extension.contributes.themes.uiTheme": "基底佈景主題定義編輯器的色彩: 'vs' 是淺色佈景主題,'vs-dark' 是深色佈景主題。'hc-black' 是深色高對比佈景主題。", - "vscode.extension.contributes.themes.path": "tmTheme 檔案的路徑。此路徑是擴充功能資料夾的相對路徑,通常為 './themes/themeFile.tmTheme'。", - "vscode.extension.contributes.iconThemes": "Contributes file icon themes.", - "vscode.extension.contributes.iconThemes.id": "用在使用者設定中的圖示佈景主題識別碼。", - "vscode.extension.contributes.iconThemes.label": "以 UI 顯示的圖示佈景主題標籤。", - "vscode.extension.contributes.iconThemes.path": "圖示佈景主題定義檔案。路徑相對於擴充功能資料夾,通常為 './icons/awesome-icon-theme.json'。", "migration.completed": "已將新的佈景主題設定新增到使用者設定。備份位於 {0}。", "error.cannotloadtheme": "Unable to load {0}: {1}", - "reqarray": "Extension point `{0}` must be an array.", - "reqpath": "`contributes.{0}.path` 中的預期字串。提供的值: {1}", - "invalid.path.1": "要包含在擴充功能資料夾 ({2}) 中的預期 `contributes.{0}.path` ({1})。這可能會使擴充功能無法移植。", - "reqid": "`contributes.{0}.id` 中的預期字串。提供的值: {1}", "error.cannotloadicontheme": "Unable to load {0}", - "error.cannotparseicontheme": "Problems parsing file icons file: {0}", "colorTheme": "Specifies the color theme used in the workbench.", "colorThemeError": "Theme is unknown or not installed.", "iconTheme": "指定在工作台中使用的圖示主題,或設定為 'null' 不顯示任何檔案圖示。", "noIconThemeDesc": "No file icons", "iconThemeError": "File icon theme is unknown or not installed.", "workbenchColors": "依目前選擇的彩色佈景主題覆寫顏色", - "workbenchColors.deprecated": "此設定不再為實驗性,且已被重新命名為 'workbench.colorCustomizations'", - "workbenchColors.deprecatedDescription": "改用'workbench.colorCustomizations'", "editorColors": "依目前選取的色彩佈景主題覆寫編輯器色彩與字型樣式。", "editorColors.comments": "設定註解的色彩與樣式", "editorColors.strings": "設定字串常值的色彩與樣式。", diff --git a/i18n/cht/src/vs/workbench/services/workspace/node/workspaceEditingService.i18n.json b/i18n/cht/src/vs/workbench/services/workspace/node/workspaceEditingService.i18n.json new file mode 100644 index 00000000000..e3d8abe8ffe --- /dev/null +++ b/i18n/cht/src/vs/workbench/services/workspace/node/workspaceEditingService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "openWorkspaceConfigurationFile": "開啟工作區組態檔", + "close": "關閉" +} \ No newline at end of file diff --git a/i18n/deu/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json b/i18n/deu/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json index 7dc4d240d04..b68dd3a3b2a 100644 --- a/i18n/deu/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json +++ b/i18n/deu/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json @@ -4,13 +4,10 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "activeEditorShort": "z. B. myFile.txt", - "activeEditorMedium": "e.g. myFolder/myFile.txt", - "activeEditorLong": "e.g. /Users/Development/myProject/myFolder/myFile.txt", - "rootName": "z. B. meinOrdner1, meinOrdner2, meinOrdner3", - "rootPath": "z. B. /Users/Development/myProject", - "folderName": "z. B. meinOrdner", - "folderPath": "z. B. /Users/Development/meinOrdner", + "activeEditorShort": "Der Dateiname (z.B. meineDatei.txt)", + "activeEditorLong": "Der vollständige Pfad der Datei (z.B. /Benutzer/Entwicklung/meinProjekt/meinOrdner/meineDatei.txt)", + "rootName": "Name des Arbeitsbereichs (z.B. meinOrdner oder meinArbeitsberech)", + "rootPath": "Dateipfad des Arbeitsbereichs (z.B. /Benutzer/Entwicklung/meinArbeitsbereich)", "appName": "z. B. VS Code", "dirty": "Ein geänderter Indikator, wenn der aktive Editor geändert wurde", "separator": "Ein bedingtes Trennzeichen (' - '), das nur in der Umgebung von Variablen mit Werten angezeigt wird", diff --git a/i18n/deu/extensions/emmet/package.i18n.json b/i18n/deu/extensions/emmet/package.i18n.json index aef483c7df2..7c71a6c1109 100644 --- a/i18n/deu/extensions/emmet/package.i18n.json +++ b/i18n/deu/extensions/emmet/package.i18n.json @@ -28,13 +28,6 @@ "command.incrementNumberByTen": "Um 10 erhöhen", "command.decrementNumberByTen": "Um 10 verringern", "emmetSyntaxProfiles": "Definieren Sie das Profil für die angegebene Syntax, oder verwenden Sie Ihr eigenes Profil mit bestimmten Regeln.", - "emmetExclude": "Ein Array von Sprachen, in dem Emmet-Abkürzungen nicht erweitert werden sollen.", - "emmetExtensionsPath": "Pfad zu einem Ordner mit Emmet-Profilen und Ausschnitten.", - "emmetShowExpandedAbbreviation": "Zeigt erweiterte Emmet-Abkürzungen als Vorschläge an.\nDie Option inMarkupAndStylesheetFilesOnly gilt für HTML, HAML, Jade, Slim, XML, XSL, CSS, SCSS, Sass, Less und Stylus.\nDie Option \"always\" gilt unabhängig vom Markup/CSS für alle Teile der Datei.", - "emmetShowAbbreviationSuggestions": "Zeigt mögliche Emmet-Abkürzungen als Vorschläge an. Diese Option gilt nicht in Stylesheets oder wenn emmet.showExpandedAbbreviation auf \"never\" festgelegt ist.", - "emmetIncludeLanguages": "Aktivieren Sie Emmet-Abkürzungen in Sprachen, die nicht standardmäßig unterstützt werden. Fügen Sie hier ein Mapping zwischen der Sprache und der von Emmet unterstützten Sprache hinzu.\nBeispiel: {\"vue-html\": \"html\", \"javascript\": \"javascriptreact\"}", - "emmetVariables": "In Emmet-Ausschnitten zu verwendende Variablen", - "emmetTriggerExpansionOnTab": "Wenn aktiviert, werden Emmet-Abkürzungen beim Drücken der TAB-TASTE erweitert.", "emmetPreferences": "Einstellungen, die zum Ändern des Verhaltens einiger Aktionen und Konfliktlöser von Emmet verwendet werden.", "emmetPreferencesIntUnit": "Standardeinheit für Integerwerte", "emmetPreferencesFloatUnit": "Standardeinheit für Floatwerte", @@ -43,6 +36,5 @@ "emmetPreferencesStylusAfter": "Symbol, das beim Erweitern von CSS-Abkürzungen in Stylus-Dateien am Ende der CSS-Eigenschaft eingefügt werden soll", "emmetPreferencesCssBetween": "Symbol, das beim Erweitern von CSS-Abkürzungen zwischen der CSS-Eigenschaft und dem Wert eingefügt werden soll", "emmetPreferencesSassBetween": "Symbol, das beim Erweitern von CSS-Abkürzungen in Sass-Dateien zwischen der CSS-Eigenschaft und dem Wert eingefügt werden soll", - "emmetPreferencesStylusBetween": "Symbol, das beim Erweitern von CSS-Abkürzungen in Stylus-Dateien zwischen der CSS-Eigenschaft und dem Wert eingefügt werden soll", - "emmetShowSuggestionsAsSnippets": "Bei TRUE werden die Emmet-Vorschläge als Ausschnitte angezeigt, mit denen Sie sie der editor.snippetSuggestions-Einstellung entsprechend anordnen können." + "emmetPreferencesStylusBetween": "Symbol, das beim Erweitern von CSS-Abkürzungen in Stylus-Dateien zwischen der CSS-Eigenschaft und dem Wert eingefügt werden soll" } \ No newline at end of file diff --git a/i18n/deu/extensions/git/out/commands.i18n.json b/i18n/deu/extensions/git/out/commands.i18n.json index c3d3a5519b7..3f2fcad2989 100644 --- a/i18n/deu/extensions/git/out/commands.i18n.json +++ b/i18n/deu/extensions/git/out/commands.i18n.json @@ -12,8 +12,9 @@ "cloning": "Git-Repository wird geklont...", "openrepo": "Repository öffnen", "proposeopen": "Möchten Sie das geklonte Repository öffnen?", - "path to init": "Ordnerpfad", - "provide path": "Geben Sie einen Ordnerpfad zum Initialisieren eines Git-Repositorys an.", + "init repo": "Repository initialisieren", + "create repo": "Repository initialisieren", + "are you sure": "Erstellt ein Git-Repository unter '{0}'. Sind Sie sicher das Sie weiterfahren möchten?", "HEAD not available": "Es ist keine HEAD-Version von \"{0}\" verfügbar.", "confirm stage files with merge conflicts": "Möchten Sie {0} Dateien mit Mergingkonflikten bereitstellen?", "confirm stage file with merge conflicts": "Möchten Sie {0} mit Mergingkonflikten bereitstellen?", diff --git a/i18n/deu/extensions/git/out/repository.i18n.json b/i18n/deu/extensions/git/out/repository.i18n.json index eeff60b713c..aa81d1a2b07 100644 --- a/i18n/deu/extensions/git/out/repository.i18n.json +++ b/i18n/deu/extensions/git/out/repository.i18n.json @@ -21,6 +21,8 @@ "deleted by us": "Gelöscht von uns", "both added": "Beide hinzugefügt", "both modified": "Beide geändert", + "untracked, short": "U", + "modified, short": "M", "commit": "Commit", "merge changes": "Änderungen zusammenführen", "staged changes": "Bereitgestellte Änderungen", diff --git a/i18n/deu/extensions/git/package.i18n.json b/i18n/deu/extensions/git/package.i18n.json index 4b4b197df1a..42eb8374ef1 100644 --- a/i18n/deu/extensions/git/package.i18n.json +++ b/i18n/deu/extensions/git/package.i18n.json @@ -15,6 +15,8 @@ "command.stageAll": "Alle Änderungen bereitstellen", "command.stageSelectedRanges": "Gewählte Bereiche bereitstellen", "command.revertSelectedRanges": "Ausgewählte Bereiche zurücksetzen", + "command.stageChange": "Änderung bereitstellen", + "command.revertChange": "Änderung zurücksetzen", "command.unstage": "Bereitstellung der Änderungen aufheben", "command.unstageAll": "Bereitstellung aller Änderungen aufheben", "command.unstageSelectedRanges": "Bereitstellung gewählter Bereiche aufheben", diff --git a/i18n/deu/extensions/typescript/package.i18n.json b/i18n/deu/extensions/typescript/package.i18n.json index 227e220881d..3725f9400c0 100644 --- a/i18n/deu/extensions/typescript/package.i18n.json +++ b/i18n/deu/extensions/typescript/package.i18n.json @@ -44,7 +44,6 @@ "typescript.npm": "Gibt den Pfad zur ausführbaren NPM-Datei an, die für die automatische Typerfassung verwendet wird. Hierfür ist TypeScript 2.3.4 oder höher erforderlich.", "typescript.check.npmIsInstalled": "Überprüfen Sie, ob NPM für die automatische Typerfassung installiert ist.", "javascript.nameSuggestions": "Das Einbeziehen eindeutiger Namen von der Datei in der JavaScript-Vorschlagsliste aktivieren/deaktivieren.", - "typescript.tsc.autoDetect": "Steuert, ob die automatische Erkennung von tsc-Tasks aktiviert oder deaktiviert ist.\n", "typescript.problemMatchers.tsc.label": "TypeScript-Probleme", "typescript.problemMatchers.tscWatch.label": "TypeScript-Probleme (Überwachungsmodus)" } \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/contrib/find/browser/findWidget.i18n.json b/i18n/deu/src/vs/editor/contrib/find/browser/findWidget.i18n.json index bbffc013c9e..1a791fe2241 100644 --- a/i18n/deu/src/vs/editor/contrib/find/browser/findWidget.i18n.json +++ b/i18n/deu/src/vs/editor/contrib/find/browser/findWidget.i18n.json @@ -15,7 +15,6 @@ "label.replaceButton": "Ersetzen", "label.replaceAllButton": "Alle ersetzen", "label.toggleReplaceButton": "Ersetzen-Modus wechseln", - "title.matchesCountLimit": "Nur die ersten 999 Ergebnisse werden hervorgehoben, alle Suchvorgänge beziehen sich aber auf den gesamten Text.", "label.matchesLocation": "{0} von {1}", "label.noResults": "Keine Ergebnisse" } \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/contrib/find/common/findController.i18n.json b/i18n/deu/src/vs/editor/contrib/find/common/findController.i18n.json index a704a485374..966c9eafe93 100644 --- a/i18n/deu/src/vs/editor/contrib/find/common/findController.i18n.json +++ b/i18n/deu/src/vs/editor/contrib/find/common/findController.i18n.json @@ -10,12 +10,6 @@ "nextSelectionMatchFindAction": "Nächste Auswahl suchen", "previousSelectionMatchFindAction": "Vorherige Auswahl suchen", "startReplace": "Ersetzen", - "addSelectionToNextFindMatch": "Auswahl zur nächsten Übereinstimmungssuche hinzufügen", - "addSelectionToPreviousFindMatch": "Letzte Auswahl zu vorheriger Übereinstimmungssuche hinzufügen", - "moveSelectionToNextFindMatch": "Letzte Auswahl in nächste Übereinstimmungssuche verschieben", - "moveSelectionToPreviousFindMatch": "Letzte Auswahl in vorherige Übereinstimmungssuche verschieben", - "selectAllOccurrencesOfFindMatch": "Alle Vorkommen auswählen und Übereinstimmung suchen", - "changeAll.label": "Alle Vorkommen ändern", "showNextFindTermAction": "Nächsten Suchbegriff anzeigen", "showPreviousFindTermAction": "Vorherigen Suchbegriff anzeigen" } \ No newline at end of file diff --git a/i18n/deu/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json b/i18n/deu/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json index 58edd06f8f7..66385866668 100644 --- a/i18n/deu/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json +++ b/i18n/deu/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json @@ -6,5 +6,11 @@ { "mutlicursor.insertAbove": "Cursor oberhalb hinzufügen", "mutlicursor.insertBelow": "Cursor unterhalb hinzufügen", - "mutlicursor.insertAtEndOfEachLineSelected": "Cursor an Zeilenenden hinzufügen" + "mutlicursor.insertAtEndOfEachLineSelected": "Cursor an Zeilenenden hinzufügen", + "addSelectionToNextFindMatch": "Auswahl zur nächsten Übereinstimmungssuche hinzufügen", + "addSelectionToPreviousFindMatch": "Letzte Auswahl zu vorheriger Übereinstimmungssuche hinzufügen", + "moveSelectionToNextFindMatch": "Letzte Auswahl in nächste Übereinstimmungssuche verschieben", + "moveSelectionToPreviousFindMatch": "Letzte Auswahl in vorherige Übereinstimmungssuche verschieben", + "selectAllOccurrencesOfFindMatch": "Alle Vorkommen auswählen und Übereinstimmung suchen", + "changeAll.label": "Alle Vorkommen ändern" } \ No newline at end of file diff --git a/i18n/deu/src/vs/platform/theme/common/colorRegistry.i18n.json b/i18n/deu/src/vs/platform/theme/common/colorRegistry.i18n.json index a26d1d4b935..b1b17e724eb 100644 --- a/i18n/deu/src/vs/platform/theme/common/colorRegistry.i18n.json +++ b/i18n/deu/src/vs/platform/theme/common/colorRegistry.i18n.json @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "invalid.color": "Ungültiges Farbformat. Verwenden Sie #RGB, #RGBA, #RRGGBB oder #RRGGBBAA.", "schema.colors": "In der Workbench verwendete Farben.", "foreground": "Allgemeine Vordergrundfarbe. Diese Farbe wird nur verwendet, wenn sie nicht durch eine Komponente überschrieben wird.", "errorForeground": "Allgemeine Vordergrundfarbe. Diese Farbe wird nur verwendet, wenn sie nicht durch eine Komponente überschrieben wird.", 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 d630b8be8f2..fd7afc2d998 100644 --- a/i18n/deu/src/vs/workbench/browser/actions/workspaceActions.i18n.json +++ b/i18n/deu/src/vs/workbench/browser/actions/workspaceActions.i18n.json @@ -9,14 +9,17 @@ "addFolderToWorkspace": "Ordner zum Arbeitsbereich hinzufügen...", "add": "&&Hinzufügen", "addFolderToWorkspaceTitle": "Ordner zum Arbeitsbereich hinzufügen", + "globalRemoveFolderFromWorkspace": "Ordner aus dem Arbeitsbereich entfernen...", "newWorkspace": "Neuer Arbeitsbereich...", "select": "Au&&swählen", "selectWorkspace": "Ordner für den Arbeitsbereich auswählen", "removeFolderFromWorkspace": "Ordner aus dem Arbeitsbereich entfernen", + "openFolderSettings": "Ordnereinstellungen öffnen", "saveWorkspaceAsAction": "Arbeitsbereich speichern unter...", "save": "&&Speichern", "saveWorkspace": "Arbeitsbereich speichern", "openWorkspaceAction": "Arbeitsbereich öffnen...", "openWorkspaceConfigFile": "Konfigurationsdatei des Arbeitsbereichs öffnen", + "openFolderAsWorkspaceInNewWindow": "Ordner als Arbeitsbereich in neuem Fenster öffnen", "workspaceFolderPickerPlaceholder": "Arbeitsbereichsordner auswählen" } \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json b/i18n/deu/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json index 419bdb09927..4bf40f11e3b 100644 --- a/i18n/deu/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json +++ b/i18n/deu/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json @@ -5,6 +5,5 @@ // Do not edit this file. It is machine generated. { "hideActivitBar": "Aktivitätsleiste ausblenden", - "activityBarAriaLabel": "Umschaltung der aktiven Ansicht", "globalActions": "Globale Aktionen" } \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/browser/parts/compositebar/compositeBar.i18n.json b/i18n/deu/src/vs/workbench/browser/parts/compositebar/compositeBar.i18n.json new file mode 100644 index 00000000000..a2d382d743c --- /dev/null +++ b/i18n/deu/src/vs/workbench/browser/parts/compositebar/compositeBar.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. +{ + "activityBarAriaLabel": "Umschaltung der aktiven Ansicht" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/browser/parts/compositebar/compositeBarActions.i18n.json b/i18n/deu/src/vs/workbench/browser/parts/compositebar/compositeBarActions.i18n.json new file mode 100644 index 00000000000..3e71b2d6ba4 --- /dev/null +++ b/i18n/deu/src/vs/workbench/browser/parts/compositebar/compositeBarActions.i18n.json @@ -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. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "badgeTitle": "{0} - {1}", + "additionalViews": "Zusätzliche Ansichten", + "numberBadge": "{0} ({1})", + "manageExtension": "Erweiterung verwalten", + "titleKeybinding": "{0} ({1})", + "toggle": "Ansichtsfixierung umschalten" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json b/i18n/deu/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json index 7ce49db8855..0812b9299a4 100644 --- a/i18n/deu/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json +++ b/i18n/deu/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json @@ -12,5 +12,6 @@ "groupTwoPicker": "Editoren in zweiter Gruppe anzeigen", "groupThreePicker": "Editoren in dritter Gruppe anzeigen", "allEditorsPicker": "Alle geöffneten Editoren anzeigen", - "view": "Anzeigen" + "view": "Anzeigen", + "file": "Datei" } \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json b/i18n/deu/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json index f86613230bf..cda94be8348 100644 --- a/i18n/deu/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json +++ b/i18n/deu/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json @@ -4,6 +4,5 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "canNotRun": "Der Befehl \"{0}\" ist zurzeit nicht aktiviert und kann nicht ausgeführt werden.", "manageExtension": "Erweiterung verwalten" } \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/electron-browser/main.contribution.i18n.json b/i18n/deu/src/vs/workbench/electron-browser/main.contribution.i18n.json index ac447a68175..3cc4c47add8 100644 --- a/i18n/deu/src/vs/workbench/electron-browser/main.contribution.i18n.json +++ b/i18n/deu/src/vs/workbench/electron-browser/main.contribution.i18n.json @@ -10,18 +10,14 @@ "workspaces": "Arbeitsbereiche", "developer": "Entwickler", "showEditorTabs": "Steuert, ob geöffnete Editoren auf Registerkarten angezeigt werden sollen.", - "workbench.editor.labelFormat.default": "Zeigt den Namen der Datei an. Wenn Registerkarten aktiviert sind und zwei Dateien den gleichen Namen in einer Gruppe haben, werden die besonderen Abschnitte des Pfads jeder Datei hinzugefügt. Wenn Registerkarten deaktiviert sind, wird der Pfad zum Arbeitsbereich-Stammverzeichnis angezeigt, wenn der Editor aktiv ist.", "workbench.editor.labelFormat.short": "Den Namen der Datei anzeigen, gefolgt von dessen Verzeichnisnamen.", - "workbench.editor.labelFormat.medium": "Zeigt den Namen der Datei an, gefolgt vom Pfad zum Arbeitsbereich-Stammverzeichnis.", "workbench.editor.labelFormat.long": "Zeigt den Namen der Datei an, gefolgt von ihrem absoluten Pfad.", "tabDescription": "Steuert das Format der Beschriftung für einen Editor. Wenn Sie diese Einstellung ändern, ist beispielsweise der Speicherort einer Datei besser ersichtlich:\n- kurz: \"parent\"\n- mittel: \"workspace/src/parent\"\n- lang: \"/home/user/workspace/src/parent\"\n- Standard: \".../parent\", wenn eine andere Registerkarte denselben Titel hat, oder den relativen Arbeitsbereichspfad, wenn Registerkarten deaktiviert sind.", "editorTabCloseButton": "Steuert die Position der Schließen-Schaltflächen der Editor-Registerkarten oder deaktiviert sie bei der Einstellung \"off\".", "showIcons": "Steuert, ob geöffnete Editoren mit einem Symbol angezeigt werden sollen. Hierzu muss auch ein Symboldesign aktiviert werden.", "enablePreview": "Steuert, ob geöffnete Editoren als Vorschau angezeigt werden. Vorschau-Editoren werden wiederverwendet, bis sie gespeichert werden (z. B. über Doppelklicken oder Bearbeiten), und sie werden mit kursivem Schriftschnitt angezeigt.", "enablePreviewFromQuickOpen": "Steuert, ob geöffnete Editoren aus Quick Open als Vorschau angezeigt werden. Vorschau-Editoren werden wiederverwendet, bis sie gespeichert werden (z. B. über Doppelklicken oder Bearbeiten).", - "editorOpenPositioning": "Steuert, wo Editoren geöffnet werden. Wählen Sie \"Links\" oder \"Rechts\" aus, um Editoren links oder rechts vom aktuellen aktiven Editor zu öffnen. Wählen Sie \"Erster\" oder \"Letzter\" aus, um Editoren unabhängig vom aktuell aktiven Editor zu öffnen.", "revealIfOpen": "Steuert, ob ein geöffneter Editor in einer der sichtbaren Gruppen angezeigt wird. Ist diese Option deaktiviert, wird ein Editor vorzugsweise in der aktuell aktiven Editorgruppe geöffnet. Ist diese Option aktiviert, wird ein bereits geöffneter Editor angezeigt und nicht in der aktuell aktiven Editorgruppe erneut geöffnet. In einigen Fällen wird diese Einstellung ignoriert, z. B. wenn das Öffnen eines Editors in einer bestimmten Gruppe oder neben der aktuell aktiven Gruppe erzwungen wird.", - "commandHistory": "Steuert, ob die Anzahl zuletzt verwendeter Befehle im Verlauf für die Befehlspalette gespeichert wird. Legen Sie diese Option auf 0 fest, um den Befehlsverlauf zu deaktivieren.", "preserveInput": "Steuert, ob die letzte typisierte Eingabe in die Befehlspalette beim nächsten Öffnen wiederhergestellt wird.", "closeOnFocusLost": "Steuert, ob Quick Open automatisch geschlossen werden soll, sobald das Feature den Fokus verliert.", "openDefaultSettings": "Steuert, ob beim Öffnen der Einstellungen auch ein Editor geöffnet wird, der alle Standardeinstellungen anzeigt.", @@ -50,7 +46,6 @@ "restoreWindows": "Steuert, wie Fenster nach einem Neustart erneut geöffnet werden. Wählen Sie \"none\", um immer mit einem leeren Arbeitsbereich zu beginnen, \"one\", um das zuletzt verwendete Fenster erneut zu öffnen, \"folders\", um alle Fenster, in denen Ordner geöffnet waren, erneut zu öffnen, oder \"all\", um alle Fenster der letzten Sitzung erneut zu öffnen.", "restoreFullscreen": "Steuert, ob ein Fenster im Vollbildmodus wiederhergestellt wird, wenn es im Vollbildmodus beendet wurde.", "zoomLevel": "Passen Sie den Zoomfaktor des Fensters an. Die ursprüngliche Größe ist 0. Jede Inkrementierung nach oben (z. B. 1) oder unten (z. B. -1) stellt eine Vergrößerung bzw. Verkleinerung um 20 % dar. Sie können auch Dezimalwerte eingeben, um den Zoomfaktor genauer anzupassen.", - "title": "Steuert den Fenstertitel basierend auf dem aktiven Editor. Variablen werden abhängig vom Kontext ersetzt:\n${activeEditorShort}: z. B. meineDatei.txt\n${activeEditorMedium}: z. B. meinOrdner/meineDatei.txt\n${activeEditorLong}: z. B. /Users/Development/meinProjekt/meinOrdner/meineDatei.txt\n${folderName}: z. B. \nmeinOrdner${folderPath}: z. B. /Users/Development/meinOrdner\n${rootName}: z. B. meinOrdner1, meinOrdner2, meinOrdner3\n${rootPath}: z. B. /Users/Development/meinArbeitsbereich\n${appName}: z. B. VS Code\n${dirty}: ein Änderungsindikator, wenn der aktive Editor geändert wurde\n${separator}: ein bedingtes Trennzeichen (\" - \"), das nur angezeigt wird, wenn es zwischen Variablen mit Werten steht", "window.newWindowDimensions.default": "Öffnet neue Fenster in der Mitte des Bildschirms.", "window.newWindowDimensions.inherit": "Öffnet neue Fenster mit den gleichen Abmessungen wie das letzte aktive Fenster.", "window.newWindowDimensions.maximized": "Öffnet neue Fenster maximiert.", diff --git a/i18n/deu/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json b/i18n/deu/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json index be338b43ea2..b06443ae9ef 100644 --- a/i18n/deu/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json +++ b/i18n/deu/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json @@ -6,6 +6,8 @@ { "entryAriaLabel": "{0}, Debugging", "debugAriaLabel": "Geben Sie den Namen einer auszuführenden Startkonfiguration ein.", + "addConfigTo": "Konfiguration hinzufügen ({0})...", + "addConfiguration": "Konfiguration hinzufügen...", "noConfigurationsMatching": "Keine übereinstimmenden Debugkonfigurationen", "noConfigurationsFound": "Keine Debugkonfiguration gefunden. Erstellen Sie die Datei \"launch.json\"." } \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/debug/browser/debugStatus.i18n.json b/i18n/deu/src/vs/workbench/parts/debug/browser/debugStatus.i18n.json new file mode 100644 index 00000000000..5345ba65476 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/debug/browser/debugStatus.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. +{ + "debug": "Debuggen" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/debug/browser/debugViewlet.i18n.json b/i18n/deu/src/vs/workbench/parts/debug/browser/debugViewlet.i18n.json new file mode 100644 index 00000000000..8b6ad71cd4e --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/debug/browser/debugViewlet.i18n.json @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json b/i18n/deu/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json index 427b79c92de..1c098b69622 100644 --- a/i18n/deu/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json +++ b/i18n/deu/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json @@ -15,7 +15,6 @@ "vscode.extension.contributes.debuggers.initialConfigurations": "Konfigurationen zum Generieren der anfänglichen Datei \"launch.json\".", "vscode.extension.contributes.debuggers.languages": "Liste der Sprachen, für die die Debugerweiterung als \"Standarddebugger\" angesehen werden kann", "vscode.extension.contributes.debuggers.adapterExecutableCommand": "Wenn dies festgelegt ist, ruft der VS Code diesen Befehl auf, um den ausführbaren Pfad des Debugadapters und die zu übergebenden Argumente zu bestimmen.", - "vscode.extension.contributes.debuggers.startSessionCommand": "Wenn dies festgelegt ist, ruft der VS Code diesen Befehl für die Debug- oder Ausführungsaktionen aus, die für diese Erweiterung bestimmt sind.", "vscode.extension.contributes.debuggers.configurationSnippets": "Snippets zum Hinzufügen neuer Konfigurationen in \"launch.json\".", "vscode.extension.contributes.debuggers.configurationAttributes": "JSON-Schemakonfigurationen zum Überprüfen von \"launch.json\".", "vscode.extension.contributes.debuggers.windows": "Windows-spezifische Einstellungen.", diff --git a/i18n/deu/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json b/i18n/deu/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json index 85809743b85..2cbcf3ae529 100644 --- a/i18n/deu/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json +++ b/i18n/deu/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json @@ -12,10 +12,7 @@ "breakpointRemoved": "Der Haltepunkt wurde entfernt. Zeile {0}, Datei \"{1}\".", "compoundMustHaveConfigurations": "Für den Verbund muss das Attribut \"configurations\" festgelegt werden, damit mehrere Konfigurationen gestartet werden können.", "configMissing": "Konfiguration \"{0}\" fehlt in \"launch.json\".", - "debugRequestNotSupported": "Die ausgewählte Debugkonfiguration verfügt über einen nicht unterstützten Attributwert \"{0}\": \"{1}\".", - "debugRequesMissing": "Das Attribut \"{0}\" fehlt in der ausgewählten Debugkonfiguration.", "debugTypeNotSupported": "Der konfigurierte Debugtyp \"{0}\" wird nicht unterstützt.", - "debugTypeMissing": "Fehlende Eigenschaft \"type\" für die ausgewählte Startkonfiguration.", "preLaunchTaskErrors": "Buildfehler während preLaunchTask \"{0}\".", "preLaunchTaskError": "Buildfehler während preLaunchTask \"{0}\".", "preLaunchTaskExitCode": "Der preLaunchTask \"{0}\" wurde mit dem Exitcode {1} beendet.", diff --git a/i18n/deu/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json b/i18n/deu/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json index dded802b64c..fc3e3434fee 100644 --- a/i18n/deu/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json +++ b/i18n/deu/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json @@ -7,6 +7,5 @@ "stateCapture": "Der Objektstatus wird aus der ersten Auswertung erfasst.", "replVariableAriaLabel": "Variable {0} besitzt den Wert {1}, Read Eval Print-Loop, Debuggen", "replExpressionAriaLabel": "Ausdruck {0} besitzt den Wert {1}, Read Eval Print-Loop, Debuggen", - "replValueOutputAriaLabel": "{0}, Read Eval Print-Loop, Debuggen", - "replKeyValueOutputAriaLabel": "Ausgabevariable {0} besitzt den Wert {1}, Read Eval Print-Loop, Debuggen" + "replValueOutputAriaLabel": "{0}, Read Eval Print-Loop, Debuggen" } \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json b/i18n/deu/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json index bc53701d4ae..46de1e8541a 100644 --- a/i18n/deu/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json +++ b/i18n/deu/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "filesCategory": "Dateien", + "filesCategory": "Datei", "revealInSideBar": "In Seitenleiste anzeigen", "acceptLocalChanges": "Änderungen verwenden und Datenträgerinhalte überschreiben", "revertLocalChanges": "Änderungen verwerfen und Datenträgerinhalte wiederherstellen" diff --git a/i18n/deu/src/vs/workbench/parts/files/browser/fileActions.i18n.json b/i18n/deu/src/vs/workbench/parts/files/browser/fileActions.i18n.json index 9bbadf5a16f..241386f0b5c 100644 --- a/i18n/deu/src/vs/workbench/parts/files/browser/fileActions.i18n.json +++ b/i18n/deu/src/vs/workbench/parts/files/browser/fileActions.i18n.json @@ -23,6 +23,7 @@ "confirmMoveTrashMessageFile": "Möchten Sie \"{0}\" wirklich löschen?", "undoBin": "Die Wiederherstellung kann aus dem Papierkorb erfolgen.", "undoTrash": "Die Wiederherstellung kann aus dem Papierkorb erfolgen.", + "doNotAskAgain": "Nicht erneut fragen", "confirmDeleteMessageFolder": "Möchten Sie \"{0}\" samt Inhalt wirklich endgültig löschen?", "confirmDeleteMessageFile": "Möchten Sie \"{0}\" wirklich endgültig löschen?", "irreversible": "Diese Aktion kann nicht rückgängig gemacht werden.", @@ -37,8 +38,6 @@ "openToSide": "Zur Seite öffnen", "compareSource": "Für Vergleich auswählen", "globalCompareFile": "Aktive Datei vergleichen mit...", - "pickHistory": "Zuvor geöffnete Datei für den Vergleich auswählen", - "unableToFileToCompare": "Die ausgewählte Datei kann nicht mit \"{0}\" verglichen werden.", "openFileToCompare": "Zuerst eine Datei öffnen, um diese mit einer anderen Datei zu vergleichen", "compareWith": "'{0}' mit '{1}' vergleichen", "compareFiles": "Dateien vergleichen", @@ -47,7 +46,7 @@ "saveAs": "Speichern unter...", "saveAll": "Alle speichern", "saveAllInGroup": "Alle in der Gruppe speichern", - "saveFiles": "Geänderte Dateien speichern", + "saveFiles": "Alle Dateien speichern", "revert": "Datei wiederherstellen", "focusOpenEditors": "Fokus auf Ansicht \"Geöffnete Editoren\"", "focusFilesExplorer": "Fokus auf Datei-Explorer", diff --git a/i18n/deu/src/vs/workbench/parts/files/browser/files.contribution.i18n.json b/i18n/deu/src/vs/workbench/parts/files/browser/files.contribution.i18n.json index 93f045f545a..e396ef8d753 100644 --- a/i18n/deu/src/vs/workbench/parts/files/browser/files.contribution.i18n.json +++ b/i18n/deu/src/vs/workbench/parts/files/browser/files.contribution.i18n.json @@ -40,6 +40,8 @@ "dynamicHeight": "Steuert, ob sich die Höhe des Abschnitts \"Geöffnete Editoren\" dynamisch an die Anzahl der Elemente anpassen soll.", "autoReveal": "Steuert, ob der Explorer Dateien beim Öffnen automatisch anzeigen und auswählen soll.", "enableDragAndDrop": "Steuert, ob der Explorer das Verschieben von Dateien und Ordnern mithilfe von Drag Drop zulassen soll.", + "confirmDragAndDrop": "Steuert, ob der Explorer um Bestätigung bitten soll, beim Verschieben von Dateien oder Ordnern per Ziehen und Ablegen.", + "confirmDelete": "Steuert, ob der Explorer um Bestätigung bitten soll, wenn Sie eine Datei über den Papierkorb löschen.", "sortOrder.default": "Dateien und Ordner werden nach ihren Namen in alphabetischer Reihenfolge sortiert. Ordner werden vor Dateien angezeigt. ", "sortOrder.mixed": "Dateien und Ordner werden nach ihren Namen in alphabetischer Reihenfolge sortiert. Dateien und Ordner werden vermischt angezeigt.", "sortOrder.filesFirst": "Dateien und Ordner werden nach ihren Namen in alphabetischer Reihenfolge sortiert. Dateien werden vor Ordnern angezeigt.", diff --git a/i18n/deu/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json b/i18n/deu/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json index e04313a8208..e3de54fe532 100644 --- a/i18n/deu/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json +++ b/i18n/deu/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json @@ -6,6 +6,8 @@ { "noWorkspace": "Es ist kein Ordner geöffnet.", "explorerSection": "Datei-Explorer-Abschnitt", - "noWorkspaceHelp": "Sie haben noch keinen Ordner geöffnet.", + "noWorkspaceHelp": "Sie haben noch keinen Ordner zum Arbeitsbereich hinzugefügt.", + "addFolder": "Ordner hinzufügen", + "noFolderHelp": "Sie haben noch keinen Ordner geöffnet.", "openFolder": "Ordner öffnen" } \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json b/i18n/deu/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json index 31a534a16fa..972dbbe86f2 100644 --- a/i18n/deu/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json +++ b/i18n/deu/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json @@ -4,12 +4,15 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { + "canNotResolve": "Ordner {0} kann nicht aufgelöst werden.", "fileInputAriaLabel": "Geben Sie den Dateinamen ein. Drücken Sie zur Bestätigung die EINGABETASTE oder ESC, um den Vorgang abzubrechen.", "filesExplorerViewerAriaLabel": "{0}, Datei-Explorer", "dropFolders": "Möchten Sie die Ordner zum Arbeitsbereich hinzufügen?", "dropFolder": "Möchten Sie den Ordner zum Arbeitsbereich hinzufügen?", "addFolders": "&&Ordner hinzufügen", "addFolder": "&&Ordner hinzufügen", + "confirmMove": "Sind Sie sicher, dass Sie \"{0}\" verschieben möchten?", + "doNotAskAgain": "Nicht erneut fragen", "confirmOverwriteMessage": "{0} ist im Zielordner bereits vorhanden. Möchten Sie das Element ersetzen?", "irreversible": "Diese Aktion kann nicht rückgängig gemacht werden.", "replaceButtonLabel": "&&Ersetzen" diff --git a/i18n/deu/src/vs/workbench/parts/markers/browser/markersFileDecorations.i18n.json b/i18n/deu/src/vs/workbench/parts/markers/browser/markersFileDecorations.i18n.json new file mode 100644 index 00000000000..cf58a240413 --- /dev/null +++ b/i18n/deu/src/vs/workbench/parts/markers/browser/markersFileDecorations.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "label": "Probleme", + "tooltip.1": "1 Problem in dieser Datei", + "tooltip.N": "{0} Probleme in dieser Datei", + "markers.showOnFile": "Fehler & Warnungen auf Dateien und Ordnern anzeigen." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json b/i18n/deu/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json index c7f0245455c..b100f612513 100644 --- a/i18n/deu/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json +++ b/i18n/deu/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json @@ -5,7 +5,6 @@ // Do not edit this file. It is machine generated. { "emptyUserSettingsHeader": "Platzieren Sie Ihre Einstellungen hier, um die Standardeinstellungen zu überschreiben.", - "errorInvalidConfiguration": "Einstellungen können nicht geschrieben werden. Öffnen Sie die Datei, um Fehler/Warnungen in der Datei zu korrigieren. Versuchen Sie es anschließend noch mal.", "emptyWorkspaceSettingsHeader": "Platzieren Sie Ihre Einstellungen hier, um die Benutzereinstellungen zu überschreiben.", "emptyFolderSettingsHeader": "Platzieren Sie Ihre Ordnereinstellungen hier, um die Einstellungen in den Arbeitsbereichseinstellungen zu überschreiben.", "defaultFolderSettingsTitle": "Standardordnereinstellungen", diff --git a/i18n/deu/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json b/i18n/deu/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json index 321ccab5284..77769778060 100644 --- a/i18n/deu/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json +++ b/i18n/deu/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json @@ -13,7 +13,6 @@ "actionNotEnabled": "Der Befehl \"{0}\" ist im aktuellen Kontext nicht aktiviert.", "recentlyUsed": "zuletzt verwendet", "morecCommands": "andere Befehle", - "commandLabel": "{0}: {1}", "cat.title": "{0}: {1}", "noCommandsMatching": "Keine übereinstimmenden Befehle." } \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json b/i18n/deu/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json index 96756d61e40..bd63f3fb9cd 100644 --- a/i18n/deu/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json +++ b/i18n/deu/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json @@ -4,6 +4,10 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { + "changes": "{0} von {1} Änderungen", + "change": "{0} von {1} Änderung", + "show previous change": "Vorherige Änderung anzeigen", + "show next change": "Nächste Änderung anzeigen", "editorGutterModifiedBackground": "Hintergrundfarbe für die Editor-Leiste für Zeilen, die geändert wurden.", "editorGutterAddedBackground": "Hintergrundfarbe für die Editor-Leiste für Zeilen, die hinzugefügt wurden.", "editorGutterDeletedBackground": "Hintergrundfarbe für die Editor-Leiste für Zeilen, die gelöscht wurden.", diff --git a/i18n/deu/src/vs/workbench/parts/search/browser/search.contribution.i18n.json b/i18n/deu/src/vs/workbench/parts/search/browser/search.contribution.i18n.json index 44f81eb9c7f..b36930b05c6 100644 --- a/i18n/deu/src/vs/workbench/parts/search/browser/search.contribution.i18n.json +++ b/i18n/deu/src/vs/workbench/parts/search/browser/search.contribution.i18n.json @@ -17,7 +17,6 @@ "exclude": "Konfigurieren Sie Globmuster zum Ausschließen von Dateien und Ordnern in Suchvorgängen. Alle Globmuster werden von der files.exclude-Einstellung geerbt.", "exclude.boolean": "Das Globmuster, mit dem Dateipfade verglichen werden sollen. Legen Sie diesen Wert auf \"true\" oder \"false\" fest, um das Muster zu aktivieren bzw. zu deaktivieren.", "exclude.when": "Zusätzliche Überprüfung der gleichgeordneten Elemente einer entsprechenden Datei. Verwenden Sie \"$(basename)\" als Variable für den entsprechenden Dateinamen.", - "useRipgrep": "Steuert, ob \"ripgrep\" in der Textsuche verwendet wird", "useIgnoreFilesByDefault": "Steuert, ob bei der Suche in einem neuen Arbeitsbereich standardmäßig GITIGNORE- und IGNORE-Dateien verwendet werden sollen.", "search.quickOpen.includeSymbols": "Konfigurieren Sie diese Option, um Ergebnisse aus einer globalen Symbolsuche in die Dateiergebnisse für Quick Open einzuschließen." } \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/search/browser/searchActions.i18n.json b/i18n/deu/src/vs/workbench/parts/search/browser/searchActions.i18n.json index b16c9eacc2a..94d13d8f553 100644 --- a/i18n/deu/src/vs/workbench/parts/search/browser/searchActions.i18n.json +++ b/i18n/deu/src/vs/workbench/parts/search/browser/searchActions.i18n.json @@ -19,7 +19,6 @@ "ClearSearchResultsAction.label": "Suchergebnisse löschen", "FocusNextSearchResult.label": "Fokus auf nächstes Suchergebnis", "FocusPreviousSearchResult.label": "Fokus auf vorheriges Suchergebnis", - "RemoveAction.label": "Entfernen", "file.replaceAll.label": "Alle ersetzen", "match.replace.label": "Ersetzen" } \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json b/i18n/deu/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json index 016b1854df2..1d0d27e52b0 100644 --- a/i18n/deu/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json +++ b/i18n/deu/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json @@ -10,8 +10,8 @@ "vscode.extension.contributes.snippets": "Trägt Codeausschnitte bei.", "vscode.extension.contributes.snippets-language": "Der Sprachbezeichner, für den dieser Codeausschnitt beigetragen wird.", "vscode.extension.contributes.snippets-path": "Der Pfad der Codeausschnittdatei. Der Pfad ist relativ zum Erweiterungsordner und beginnt normalerweise mit \". /snippets/\".", - "badFile": "Die Ausschnittsdatei \"{0}\" konnte nicht gelesen werden.", "badVariableUse": "Das \"{0}\"-Snippet verwirrt wahrscheinlich Snippet-Variablen und Snippet-Paltzhalter. Schaue https://code.visualstudio.com/docs/editor/userdefinedsnippets#_snippet-syntax für weitere Informationen.", + "badFile": "Die Ausschnittsdatei \"{0}\" konnte nicht gelesen werden.", "source.snippet": "Benutzercodeausschnitt", "detail.snippet": "{0} ({1})", "snippetSuggest.longLabel": "{0}, {1}" 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 5f9f1928ae6..09fd787097a 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 @@ -14,6 +14,7 @@ "runningTasks": "Aktive Aufgaben anzeigen", "tasks": "Aufgaben", "TaskSystem.noHotSwap": "Zum Ändern des Aufgabenausführungsmoduls mit einem aktiven Task muss das Fenster erneut geladen werden.", + "TaskServer.folderIgnored": "Der Ordner {0} wird ignoriert, da er Aufgabenversion 0.1.0 verwendet", "TaskService.noBuildTask1": "Keine Buildaufgabe definiert. Markieren Sie eine Aufgabe mit 'isBuildCommand' in der tasks.json-Datei.", "TaskService.noBuildTask2": "Es ist keine Buildaufgabe definiert. Markieren Sie eine Aufgabe in der Datei \"tasks.json\" als \"Buildgruppe\". ", "TaskService.noTestTask1": "Keine Testaufgabe definiert. Markieren Sie eine Aufgabe mit 'isTestCommand' in der tasks.json-Datei.", diff --git a/i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json b/i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json index 7d62688f024..93a8b04286a 100644 --- a/i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json +++ b/i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json @@ -16,7 +16,6 @@ "workbench.action.terminal.new.short": "Neues Terminal", "workbench.action.terminal.focus": "Fokus im Terminal", "workbench.action.terminal.focusNext": "Fokus im nächsten Terminal", - "workbench.action.terminal.focusAtIndex": "Terminal {0} im Fokus", "workbench.action.terminal.focusPrevious": "Fokus im vorherigen Terminal", "workbench.action.terminal.paste": "In aktives Terminal einfügen", "workbench.action.terminal.DefaultShell": "Standardshell auswählen", diff --git a/i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json b/i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json index 558f228c1f1..cf9ef31e248 100644 --- a/i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json +++ b/i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json @@ -5,7 +5,6 @@ // Do not edit this file. It is machine generated. { "copy": "Kopieren", - "createNewTerminal": "Neues Terminal", "paste": "Einfügen", "selectAll": "Alles auswählen", "clear": "Löschen" diff --git a/i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json b/i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json index e01f30f61a5..ac6f1dfc78c 100644 --- a/i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json +++ b/i18n/deu/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json @@ -10,6 +10,5 @@ "never again": "OK, nicht mehr anzeigen", "terminal.integrated.chooseWindowsShell": "Wählen Sie Ihre bevorzugte Terminalshell. Sie können diese später in Ihren Einstellungen ändern.", "terminalService.terminalCloseConfirmationSingular": "Eine aktive Terminalsitzung ist vorhanden. Möchten Sie sie beenden?", - "terminalService.terminalCloseConfirmationPlural": "{0} aktive Terminalsitzungen sind vorhanden. Möchten Sie sie beenden?", - "yes": "Ja" + "terminalService.terminalCloseConfirmationPlural": "{0} aktive Terminalsitzungen sind vorhanden. Möchten Sie sie beenden?" } \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/services/configuration/common/configurationExtensionPoint.i18n.json b/i18n/deu/src/vs/workbench/services/configuration/common/configurationExtensionPoint.i18n.json new file mode 100644 index 00000000000..ce08826d007 --- /dev/null +++ b/i18n/deu/src/vs/workbench/services/configuration/common/configurationExtensionPoint.i18n.json @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.configuration.title": "Eine Zusammenfassung der Einstellungen. Diese Bezeichnung wird in der Einstellungsdatei als trennender Kommentar verwendet.", + "vscode.extension.contributes.configuration.properties": "Die Beschreibung der Konfigurationseigenschaften.", + "scope.window.description": "Fensterspezifische Konfiguration, die in den Benutzer- oder Arbeitsbereichseinstellungen konfiguriert werden kann.", + "scope.resource.description": "Ressourcenspezifische Konfiguration, die in den Benutzer-, Arbeitsbereichs- oder Ordnereinstellungen konfiguriert werden kann.", + "scope.description": "Bereich, in dem die Konfiguration gültig ist. Verfügbare Gültigkeitsbereiche sind \"window\" und \"resource\".", + "vscode.extension.contributes.configuration": "Trägt Konfigurationseigenschaften bei.", + "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.", + "invalid.allOf": "\"configuration.allOf\" ist veraltet und sollte nicht mehr verwendet werden. Übergeben Sie stattdessen mehrere Konfigurationsabschnitte als Array an den Beitragspunkt \"configuration\".", + "workspaceConfig.folders.description": "Liste von Ordnern, die in den Arbeitsbereich geladen werden.", + "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.name.description": "Ein optionaler Name für den Ordner.", + "workspaceConfig.extensions.description": "Arbeitsbereichserweiterungen", + "unknownWorkspaceProperty": "Unbekannte Arbeitsbereichs-Konfigurationseigenschaft" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/services/editor/common/editorService.i18n.json b/i18n/deu/src/vs/workbench/services/editor/common/editorService.i18n.json new file mode 100644 index 00000000000..50e968f8ee3 --- /dev/null +++ b/i18n/deu/src/vs/workbench/services/editor/common/editorService.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. +{ + "compareLabels": "{0} ↔ {1}" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json b/i18n/deu/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json index 3628bed2f06..d13ca944626 100644 --- a/i18n/deu/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json +++ b/i18n/deu/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json @@ -6,6 +6,7 @@ { "schema.token.settings": "Farben und Stile für das Token.", "schema.token.foreground": "Vordergrundfarbe für das Token.", + "schema.token.background.warning": "Token Hintergrundfarben werden derzeit nicht unterstützt.", "schema.token.fontStyle": "Schriftschnitt der Regel: kursiv, fett und unterstrichen (einzeln oder in Kombination)", "schema.fontStyle.error": "Der Schriftschnitt muss eine Kombination aus \"kursiv\", \"fett\" und \"unterstrichen\" sein.", "schema.properties.name": "Beschreibung der Regel.", diff --git a/i18n/deu/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json b/i18n/deu/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json index 86353589189..cce0737bd42 100644 --- a/i18n/deu/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json +++ b/i18n/deu/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json @@ -33,5 +33,6 @@ "schema.fontSize": "Wenn eine Schriftart verwendet wird: der Schriftgrad als Prozentsatz der Textschriftart. Wenn diese Angabe nicht festgelegt wird, wird standardmäßig die Größe in der Schriftartdefinition verwendet.", "schema.fontId": "Bei Verwendung einer Schriftart: die ID der Schriftart. Wenn diese Angabe nicht festgelegt wird, wird standardmäßig die erste Schriftartdefinition verwendet.", "schema.light": "Optionale Zuordnungen für Dateisymbole in hellen Farbdesigns.", - "schema.highContrast": "Optionale Zuordnungen für Dateisymbole in Farbdesigns mit hohem Kontrast." + "schema.highContrast": "Optionale Zuordnungen für Dateisymbole in Farbdesigns mit hohem Kontrast.", + "schema.hidesExplorerArrows": "Konfiguriert, ob die Datei-Explorer Pfeile ausgeblendet werden sollen, wenn dieses Motiv aktiv ist." } \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/services/themes/electron-browser/colorThemeStore.i18n.json b/i18n/deu/src/vs/workbench/services/themes/electron-browser/colorThemeStore.i18n.json new file mode 100644 index 00000000000..667f131a465 --- /dev/null +++ b/i18n/deu/src/vs/workbench/services/themes/electron-browser/colorThemeStore.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "vscode.extension.contributes.themes": "Contributes textmate color themes.", + "vscode.extension.contributes.themes.id": "Die ID des Symboldesigns wie in den Benutzereinstellungen verwendet.", + "vscode.extension.contributes.themes.label": "Die Bezeichnung des Farbdesigns wie in der Benutzeroberfläche angezeigt.", + "vscode.extension.contributes.themes.uiTheme": "Das Basisdesign, das die Farben um den Editor definiert: \"vs\" ist das helle Farbdesign, \"vs-dark\" das dunkle Farbdesign. \"hc-black\" ist das dunkle Design mit hohem Kontrast.", + "vscode.extension.contributes.themes.path": "Der Pfad der TMTHEME-Datei. Der Pfad ist relativ zum Erweiterungsordner und lautet normalerweise \"./themes/themeFile.tmTheme\".", + "reqarray": "Extension point `{0}` must be an array.", + "reqpath": "Expected string in `contributes.{0}.path`. Provided value: {1}", + "invalid.path.1": "Es wurde erwartet, dass \"contributes.{0}.path\" ({1}) im Ordner ({2}) der Erweiterung enthalten ist. Dies führt ggf. dazu, dass die Erweiterung nicht portierbar ist." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.i18n.json b/i18n/deu/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.i18n.json new file mode 100644 index 00000000000..b0f9faf6b52 --- /dev/null +++ b/i18n/deu/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.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. +{ + "error.cannotparseicontheme": "Problems parsing file icons file: {0}" +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.i18n.json b/i18n/deu/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.i18n.json new file mode 100644 index 00000000000..65109048ac5 --- /dev/null +++ b/i18n/deu/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "vscode.extension.contributes.iconThemes": "Contributes file icon themes.", + "vscode.extension.contributes.iconThemes.id": "Die ID des Symboldesigns wie in den Benutzereinstellungen verwendet.", + "vscode.extension.contributes.iconThemes.label": "Die Bezeichnung des Symboldesigns wie in der Benutzeroberfläche angezeigt.", + "vscode.extension.contributes.iconThemes.path": "Der Pfad der Symboldesign-Definitionsdatei. Der Pfad ist relativ zum Erweiterungsordner und lautet normalerweise \"./icons/awesome-icon-theme.json\".", + "reqarray": "Extension point `{0}` must be an array.", + "reqpath": "Expected string in `contributes.{0}.path`. Provided value: {1}", + "reqid": "In \"contributes.{0}.id\" wurde eine Zeichenfolge erwartet. Bereitgestellter Wert: {1}", + "invalid.path.1": "Es wurde erwartet, dass \"contributes.{0}.path\" ({1}) im Ordner ({2}) der Erweiterung enthalten ist. Dies führt ggf. dazu, dass die Erweiterung nicht portierbar ist." +} \ No newline at end of file diff --git a/i18n/deu/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json b/i18n/deu/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json index a3137e30eb6..d551e506d69 100644 --- a/i18n/deu/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json +++ b/i18n/deu/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json @@ -4,31 +4,15 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "vscode.extension.contributes.themes": "Contributes textmate color themes.", - "vscode.extension.contributes.themes.id": "Die ID des Symboldesigns wie in den Benutzereinstellungen verwendet.", - "vscode.extension.contributes.themes.label": "Die Bezeichnung des Farbdesigns wie in der Benutzeroberfläche angezeigt.", - "vscode.extension.contributes.themes.uiTheme": "Das Basisdesign, das die Farben um den Editor definiert: \"vs\" ist das helle Farbdesign, \"vs-dark\" das dunkle Farbdesign. \"hc-black\" ist das dunkle Design mit hohem Kontrast.", - "vscode.extension.contributes.themes.path": "Der Pfad der TMTHEME-Datei. Der Pfad ist relativ zum Erweiterungsordner und lautet normalerweise \"./themes/themeFile.tmTheme\".", - "vscode.extension.contributes.iconThemes": "Contributes file icon themes.", - "vscode.extension.contributes.iconThemes.id": "Die ID des Symboldesigns wie in den Benutzereinstellungen verwendet.", - "vscode.extension.contributes.iconThemes.label": "Die Bezeichnung des Symboldesigns wie in der Benutzeroberfläche angezeigt.", - "vscode.extension.contributes.iconThemes.path": "Der Pfad der Symboldesign-Definitionsdatei. Der Pfad ist relativ zum Erweiterungsordner und lautet normalerweise \"./icons/awesome-icon-theme.json\".", "migration.completed": "Den Benutzereinstellungen wurden neue Designeinstellungen hinzugefügt. Sicherung verfügbar unter {0}.", "error.cannotloadtheme": "Unable to load {0}: {1}", - "reqarray": "Extension point `{0}` must be an array.", - "reqpath": "In \"contributes.{0}.path\" wurde eine Zeichenfolge erwartet. Bereitgestellter Wert: {1}", - "invalid.path.1": "Es wurde erwartet, dass \"contributes.{0}.path\" ({1}) im Ordner ({2}) der Erweiterung enthalten ist. Dies führt ggf. dazu, dass die Erweiterung nicht portierbar ist.", - "reqid": "In \"contributes.{0}.id\" wurde eine Zeichenfolge erwartet. Bereitgestellter Wert: {1}", "error.cannotloadicontheme": "Unable to load {0}", - "error.cannotparseicontheme": "Problems parsing file icons file: {0}", "colorTheme": "Specifies the color theme used in the workbench.", "colorThemeError": "Theme is unknown or not installed.", "iconTheme": "Gibt das in der Workbench verwendete Symboldesign oder \"null\", um keine Dateisymbole anzuzeigen, an.", "noIconThemeDesc": "No file icons", "iconThemeError": "File icon theme is unknown or not installed.", "workbenchColors": "Überschreibt Farben aus dem derzeit ausgewählte Farbdesign.", - "workbenchColors.deprecated": "Diese Einstellung ist nicht mehr experimentell und wurde in \"workbench.colorCustomizations\" umbenannt.", - "workbenchColors.deprecatedDescription": "Verwenden Sie stattdessen \"workbench.colorCustomizations\".", "editorColors": "Überschreibt Editorfarben und den Schriftschnitt aus dem momentan ausgewählten Farbdesign.", "editorColors.comments": "Legt die Farben und Stile für Kommentare fest.", "editorColors.strings": "Legt die Farben und Stile für Zeichenfolgenliterale fest.", diff --git a/i18n/deu/src/vs/workbench/services/workspace/node/workspaceEditingService.i18n.json b/i18n/deu/src/vs/workbench/services/workspace/node/workspaceEditingService.i18n.json new file mode 100644 index 00000000000..9bb276f44ac --- /dev/null +++ b/i18n/deu/src/vs/workbench/services/workspace/node/workspaceEditingService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "openWorkspaceConfigurationFile": "Konfigurationsdatei des Arbeitsbereichs öffnen", + "close": "Schließen" +} \ No newline at end of file diff --git a/i18n/esn/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json b/i18n/esn/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json index 6f2a10e46de..0fea08772ca 100644 --- a/i18n/esn/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json +++ b/i18n/esn/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json @@ -4,13 +4,13 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "activeEditorShort": "p. ej. myFile.txt", - "activeEditorMedium": "e.g. myFolder/myFile.txt", - "activeEditorLong": "e.g. /Users/Development/myProject/myFolder/myFile.txt", - "rootName": "p. ej. myFolder1, myFolder2, myFolder3", - "rootPath": "p. ej. /Users/Development/myProject", - "folderName": "p. ej. myFolder", - "folderPath": "p. ej. /Users/Development/myFolder", + "activeEditorShort": "el nombre del archivo (por ejemplo miarchivo.txt)", + "activeEditorMedium": "la ruta de acceso del archivo relativa a la carpeta del espacio de trabajo (p. ej. miCarpeta/miArchivo.txt)", + "activeEditorLong": "la ruta de acceso completa del archivo (por ejemplo, /Users/Development/myProject/myFolder/myFile.txt)", + "rootName": "nombre del área de trabajo (p. ej. myFolder o myWorkspace)", + "rootPath": "ruta del archivo del área de trabajo (p. ej. /Users/Development/myWorkspace)", + "folderName": "nombre de la carpeta del área de trabajo en la que el archivo está contenido (p. ej. myFolder)", + "folderPath": "ruta de acceso de archivo de la carpeta del área de trabajo en la que el archivo está contenido (p. ej. /Users/Development/myFolder)", "appName": "p. ej. VS Code", "dirty": "un indicador con modificaciones si el editor activo tiene modificaciones", "separator": "un separador condicional (\"-\") que aparece solo cuando está rodeado de variables con valores", diff --git a/i18n/esn/extensions/emmet/package.i18n.json b/i18n/esn/extensions/emmet/package.i18n.json index 07653dd9567..863e98a95bb 100644 --- a/i18n/esn/extensions/emmet/package.i18n.json +++ b/i18n/esn/extensions/emmet/package.i18n.json @@ -28,13 +28,6 @@ "command.incrementNumberByTen": "Aumentar por 10", "command.decrementNumberByTen": "Disminuir por 10", "emmetSyntaxProfiles": "Defina el perfil de la sintaxis especificada o use su propio perfil con reglas específicas.", - "emmetExclude": "Matriz de lenguajes donde no deben expandirse la abreviación Emmet.", - "emmetExtensionsPath": "Ruta de acceso a una carpeta que contiene los perfiles de emmet y fragmentos.»", - "emmetShowExpandedAbbreviation": "\nMuestra abreviaciones Emmet expandidas como sugerencias. La opción \"inMarkupAndStylesheetFilesOnly\" se aplica a HTML, HAML, Jade, Slim, XML, XSL, CSS, SCSS, SASS, LESS y Stylus. La opción \"always\" se aplica a todas las partes del archivo, independientemente de que sea de marcado o CSS.", - "emmetShowAbbreviationSuggestions": "Muestra posibles abreviaciones Emmet como sugerencias. No se aplica a hojas de estilos ni cuando emmet.showExpandedAbbreviation está establecido en \"never\".", - "emmetIncludeLanguages": "Habilita abreviaciones Emmet en lenguajes que no se admiten de forma predeterminada. Agregue una asignación aquí entre el lenguaje y el lenguaje que admite Emmet.\n Ejemplo: {\"vue-html\": \"html\", \"javascript\": \"javascriptreact\"}", - "emmetVariables": "Variables para usar en fragmentos de código Emmet", - "emmetTriggerExpansionOnTab": "Cuando se habilita, se expande la abreviación Emmet al presionar la tecla TAB.", "emmetPreferences": "Preferencias usadas para modificar el comportamiento de algunas acciones y resoluciones de Emmet.", "emmetPreferencesIntUnit": "Unidad predeterminada para valores enteros", "emmetPreferencesFloatUnit": "Unidad predeterminada para valores float", @@ -43,6 +36,5 @@ "emmetPreferencesStylusAfter": "Símbolo que debe colocarse al final de una propiedad CSS cuando se expanden abreviaturas CSS en archivos Stylus", "emmetPreferencesCssBetween": "Símbolo que debe colocarse entre una propiedad CSS y un valor cuando se expanden abreviaturas CSS", "emmetPreferencesSassBetween": "Símbolo que debe colocarse entre una propiedad CSS y un valor cuando se expanden abreviaturas CSS en archivos SASS", - "emmetPreferencesStylusBetween": "Símbolo que debe colocarse entre una propiedad CSS y un valor cuando se expanden abreviaturas CSS en archivos Stylus", - "emmetShowSuggestionsAsSnippets": "Si es true, las sugerencias Emmet se muestran como fragmentos de código, de modo que puede ordenarlas por el valor editor.snippetSuggestions." + "emmetPreferencesStylusBetween": "Símbolo que debe colocarse entre una propiedad CSS y un valor cuando se expanden abreviaturas CSS en archivos Stylus" } \ No newline at end of file diff --git a/i18n/esn/extensions/git/out/commands.i18n.json b/i18n/esn/extensions/git/out/commands.i18n.json index 0376ca94cc1..69862736c9d 100644 --- a/i18n/esn/extensions/git/out/commands.i18n.json +++ b/i18n/esn/extensions/git/out/commands.i18n.json @@ -12,8 +12,9 @@ "cloning": "Clonando el repositorio GIT...", "openrepo": "Abrir repositorio", "proposeopen": "¿Desea abrir el repositorio clonado?", - "path to init": "Ruta de acceso de la carpeta", - "provide path": "Proporcione una ruta de acceso de carpeta para inicializar un repositorio GIT", + "init repo": "Inicializar el repositorio", + "create repo": "Inicializar el repositorio", + "are you sure": "Esto creará un repositorio Git en '{0}'. ¿Está seguro de que desea continuar?", "HEAD not available": "La versión HEAD de '{0}' no está disponible.", "confirm stage files with merge conflicts": "¿Está seguro de que quiere hacer una copia intermedia de {0} archivos con conflictos de fusión mediante combinación?", "confirm stage file with merge conflicts": "¿Está seguro de que quiere hacer una copia intermedia de {0} con conflictos de fusión mediante combinación? ", diff --git a/i18n/esn/extensions/git/out/repository.i18n.json b/i18n/esn/extensions/git/out/repository.i18n.json index fc557b451ea..b6063166fd7 100644 --- a/i18n/esn/extensions/git/out/repository.i18n.json +++ b/i18n/esn/extensions/git/out/repository.i18n.json @@ -21,6 +21,8 @@ "deleted by us": "Borrado por nosotros", "both added": "Ambos añadidos", "both modified": "Ambos modificados", + "untracked, short": "U", + "modified, short": "M", "commit": "Confirmar", "merge changes": "Fusionar cambios mediante combinación", "staged changes": "Cambios almacenados provisionalmente", diff --git a/i18n/esn/extensions/typescript/package.i18n.json b/i18n/esn/extensions/typescript/package.i18n.json index 2aeb36b7218..61e69211e91 100644 --- a/i18n/esn/extensions/typescript/package.i18n.json +++ b/i18n/esn/extensions/typescript/package.i18n.json @@ -44,7 +44,6 @@ "typescript.npm": "Especifica la ruta de acceso al archivo ejecutable de NPM usada para la adquisición automática de tipos. Requiere TypeScript >= 2.3.4.", "typescript.check.npmIsInstalled": "Compruebe si NPM está instalado para la adquisición automática de tipos.", "javascript.nameSuggestions": "Habilitar/deshabilitar nombres únicos de la lista de sugerencias en los archivos de JavaScript. ", - "typescript.tsc.autoDetect": "Controla si la detección automática de tareas TSC está activada o desactivada.", "typescript.problemMatchers.tsc.label": "Problemas de TypeScript", "typescript.problemMatchers.tscWatch.label": "Problemas de TypeScript (modo de inspección)" } \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/contrib/find/browser/findWidget.i18n.json b/i18n/esn/src/vs/editor/contrib/find/browser/findWidget.i18n.json index 6ffe5c01c29..5b52fbf2852 100644 --- a/i18n/esn/src/vs/editor/contrib/find/browser/findWidget.i18n.json +++ b/i18n/esn/src/vs/editor/contrib/find/browser/findWidget.i18n.json @@ -15,7 +15,6 @@ "label.replaceButton": "Reemplazar", "label.replaceAllButton": "Reemplazar todo", "label.toggleReplaceButton": "Alternar modo de reemplazar", - "title.matchesCountLimit": "Solo se resaltan los primeros 999 resultados, pero todas las operaciones de búsqueda trabajan en todo el texto.", "label.matchesLocation": "{0} de {1}", "label.noResults": "Sin resultados" } \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/contrib/find/common/findController.i18n.json b/i18n/esn/src/vs/editor/contrib/find/common/findController.i18n.json index 2928bececec..27d384a1b68 100644 --- a/i18n/esn/src/vs/editor/contrib/find/common/findController.i18n.json +++ b/i18n/esn/src/vs/editor/contrib/find/common/findController.i18n.json @@ -10,12 +10,6 @@ "nextSelectionMatchFindAction": "Buscar selección siguiente", "previousSelectionMatchFindAction": "Buscar selección anterior", "startReplace": "Reemplazar", - "addSelectionToNextFindMatch": "Agregar selección hasta la siguiente coincidencia de búsqueda", - "addSelectionToPreviousFindMatch": "Agregar selección hasta la anterior coincidencia de búsqueda", - "moveSelectionToNextFindMatch": "Mover última selección hasta la siguiente coincidencia de búsqueda", - "moveSelectionToPreviousFindMatch": "Mover última selección hasta la anterior coincidencia de búsqueda", - "selectAllOccurrencesOfFindMatch": "Seleccionar todas las repeticiones de coincidencia de búsqueda", - "changeAll.label": "Cambiar todas las ocurrencias", "showNextFindTermAction": "Mostrar el siguiente término de búsqueda", "showPreviousFindTermAction": "Mostrar término de búsqueda anterior" } \ No newline at end of file diff --git a/i18n/esn/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json b/i18n/esn/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json index 13f4bac636a..d5994146753 100644 --- a/i18n/esn/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json +++ b/i18n/esn/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json @@ -6,5 +6,11 @@ { "mutlicursor.insertAbove": "Agregar cursor arriba", "mutlicursor.insertBelow": "Agregar cursor debajo", - "mutlicursor.insertAtEndOfEachLineSelected": "Añadir cursores a finales de línea" + "mutlicursor.insertAtEndOfEachLineSelected": "Añadir cursores a finales de línea", + "addSelectionToNextFindMatch": "Agregar selección hasta la siguiente coincidencia de búsqueda", + "addSelectionToPreviousFindMatch": "Agregar selección hasta la anterior coincidencia de búsqueda", + "moveSelectionToNextFindMatch": "Mover última selección hasta la siguiente coincidencia de búsqueda", + "moveSelectionToPreviousFindMatch": "Mover última selección hasta la anterior coincidencia de búsqueda", + "selectAllOccurrencesOfFindMatch": "Seleccionar todas las repeticiones de coincidencia de búsqueda", + "changeAll.label": "Cambiar todas las ocurrencias" } \ No newline at end of file diff --git a/i18n/esn/src/vs/platform/theme/common/colorRegistry.i18n.json b/i18n/esn/src/vs/platform/theme/common/colorRegistry.i18n.json index 91ed718d0ad..354ba1a82a9 100644 --- a/i18n/esn/src/vs/platform/theme/common/colorRegistry.i18n.json +++ b/i18n/esn/src/vs/platform/theme/common/colorRegistry.i18n.json @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "invalid.color": "Formato de color no válido. Use #TGB, #RBGA, #RRGGBB o #RRGGBBAA", "schema.colors": "Colores usados en el área de trabajo.", "foreground": "Color de primer plano general. Este color solo se usa si un componente no lo invalida.", "errorForeground": "Color de primer plano general para los mensajes de erroe. Este color solo se usa si un componente no lo invalida.", 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 81bfd015f9e..fc9101aee18 100644 --- a/i18n/esn/src/vs/workbench/browser/actions/workspaceActions.i18n.json +++ b/i18n/esn/src/vs/workbench/browser/actions/workspaceActions.i18n.json @@ -9,14 +9,17 @@ "addFolderToWorkspace": "Agregar carpeta al área de trabajo...", "add": "&& Agregar", "addFolderToWorkspaceTitle": "Agregar carpeta al área de trabajo", + "globalRemoveFolderFromWorkspace": "Quitar carpeta del Área de trabajo...", "newWorkspace": "Nueva área de trabajo...", "select": "&&Seleccionar", "selectWorkspace": "Seleccionar carpetas para el área de trabajo", "removeFolderFromWorkspace": "Quitar carpeta del área de trabajo", + "openFolderSettings": "Abrir Configuración de carpeta", "saveWorkspaceAsAction": "Guardar área de trabajo como...", "save": "&&Guardar", "saveWorkspace": "Guardar área de trabajo", "openWorkspaceAction": "Abrir área de trabajo...", "openWorkspaceConfigFile": "Abrir archivo de configuración del área de trabajo", + "openFolderAsWorkspaceInNewWindow": "Abrir carpeta como Área de trabajo en una Nueva Ventana", "workspaceFolderPickerPlaceholder": "Seleccionar la carpeta del área de trabajo" } \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json b/i18n/esn/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json index bfe695f895b..66f040cada4 100644 --- a/i18n/esn/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json +++ b/i18n/esn/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json @@ -5,6 +5,5 @@ // Do not edit this file. It is machine generated. { "hideActivitBar": "Ocultar barra de actividades", - "activityBarAriaLabel": "Modificador de vista activa", "globalActions": "Acciones globales" } \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/browser/parts/compositebar/compositeBar.i18n.json b/i18n/esn/src/vs/workbench/browser/parts/compositebar/compositeBar.i18n.json new file mode 100644 index 00000000000..bbaa4095de7 --- /dev/null +++ b/i18n/esn/src/vs/workbench/browser/parts/compositebar/compositeBar.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. +{ + "activityBarAriaLabel": "Modificador de vista activa" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/browser/parts/compositebar/compositeBarActions.i18n.json b/i18n/esn/src/vs/workbench/browser/parts/compositebar/compositeBarActions.i18n.json new file mode 100644 index 00000000000..b8bb426ec85 --- /dev/null +++ b/i18n/esn/src/vs/workbench/browser/parts/compositebar/compositeBarActions.i18n.json @@ -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. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "badgeTitle": "{0} - {1} ", + "additionalViews": "Vistas adicionales", + "numberBadge": "{0} ({1})", + "manageExtension": "Administrar extensión", + "titleKeybinding": "{0} ({1})", + "toggle": "Alternar vista fijada" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json b/i18n/esn/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json index c5c13f08096..ae6d0079e4c 100644 --- a/i18n/esn/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json +++ b/i18n/esn/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json @@ -12,5 +12,6 @@ "groupTwoPicker": "Mostrar editores del segundo grupo", "groupThreePicker": "Mostrar editores del tercer grupo", "allEditorsPicker": "Mostrar todos los editores abiertos", - "view": "Ver" + "view": "Ver", + "file": "Archivo" } \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json b/i18n/esn/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json index 7dd9c672cbe..4f9bdabe15e 100644 --- a/i18n/esn/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json +++ b/i18n/esn/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json @@ -4,6 +4,5 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "canNotRun": "El comando \"{0}\" no está habilitado actualmente y no se puede ejecutar.", "manageExtension": "Administrar extensión" } \ 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 4655a5812be..20663bc3124 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,18 +10,15 @@ "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 área 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) y se muestran en cursiva.", "enablePreviewFromQuickOpen": "Controla si los editores abiertos mediante Quick Open se muestran en modo de vista previa. Los editores en modo de vista previa se reutilizan hasta que se conservan (por ejemplo, mediante doble clic o editándolos).", - "editorOpenPositioning": "Controla dónde se abren los editores. Seleccione 'izquierda' o 'derecha' para abrir los editores situados a la izquierda o la derecha del que está actualmente activo. Seleccione 'primero' o 'último' para abrir los editores con independencia del que esté actualmente activo.", "revealIfOpen": "Controla si un editor se muestra en alguno de los grupos visibles cuando se abre. Si se deshabilita esta opción, un editor preferirá abrirse en el grupo de editores activo en ese momento. Si se habilita, un editor ya abierto se mostrará en lugar de volver a abrirse en el grupo de editores activo. Tenga en cuenta que hay casos en los que esta opción se omite; por ejemplo, cuando se fuerza la apertura de un editor en un grupo específico o junto al grupo activo actual.", - "commandHistory": "Controla el número de comandos usados recientemente a almacenar en el historial para la paleta de comandos. Establezca a 0 para deshabilitar el historial de comandos.", + "commandHistory": "Controla el número de comandos utilizados recientemente que se mantendrán en el historial de la paleta de comandos. Establezca el valor a 0 para desactivar el historial de comandos.", "preserveInput": "Controla si la última entrada introducida en la paleta de comandos debería ser restaurada cuando sea abierta la próxima vez.", "closeOnFocusLost": "Controla si Quick Open debe cerrarse automáticamente cuando pierde el foco.", "openDefaultSettings": "Controla si la configuración de apertura también abre un editor que muestra todos los valores predeterminados.", @@ -50,7 +47,6 @@ "restoreWindows": "Controla cómo se vuelven a abrir las ventanas tras un reinicio. Seleccione \"none\" para comenzar siempre con un área de trabajo vacía, \"one\" para volver a abrir la última ventana en la que trabajó, \"folders\" para volver a abrir todas las ventanas que tenían carpetas abiertas o \"all\" para volver a abrir todas las ventanas de la última sesión.", "restoreFullscreen": "Controla si una ventana se debe restaurar al modo de pantalla completa si se salió de ella en dicho modo.", "zoomLevel": "Ajuste el nivel de zoom de la ventana. El tamaño original es 0 y cada incremento (por ejemplo, 1) o disminución (por ejemplo, -1) representa una aplicación de zoom un 20 % más grande o más pequeño. También puede especificar decimales para ajustar el nivel de zoom con una granularidad más precisa.", - "title": "Controla el título de la ventana según el editor activo. Las variables se sustituyen según el contexto:\n${activeEditorShort}: e.g. myFile.txt\n${activeEditorMedium}: e.g. myFolder/myFile.txt\n${activeEditorLong}: por ejemplo, /Users/Development/myProject/myFolder/myFile.txt\n${folderName}: por ejemplo, myFolder\n${folderPath}: por ejemplo, /Users/Development/myFolder\n${rootName}: por ejemplo, myFolder1, myFolder2, myFolder3\n${rootPath}: por ejemplo, /Users/Development/myWorkspace\n${appName}: por ejemplo, VS Code\n${dirty}: un indicador con modificaciones si el editor activo está desfasado\n${separator}: separador condicional (\" - \") que solo se muestra cuando está rodeado de variables con valores", "window.newWindowDimensions.default": "Abrir las nuevas ventanas en el centro de la pantalla.", "window.newWindowDimensions.inherit": "Abrir las nuevas ventanas con la misma dimensión que la última activa.", "window.newWindowDimensions.maximized": "Abrir las nuevas ventanas maximizadas.", diff --git a/i18n/esn/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json b/i18n/esn/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json index be6974deb6b..e9a982317e2 100644 --- a/i18n/esn/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json +++ b/i18n/esn/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json @@ -6,6 +6,8 @@ { "entryAriaLabel": "{0}, depurar", "debugAriaLabel": "Escriba un nombre de una configuración de inicio para ejecutar.", + "addConfigTo": "Agregar configuración ({0})...", + "addConfiguration": "Agregar configuración...", "noConfigurationsMatching": "No hay ninguna configuración de depuración coincidente", "noConfigurationsFound": "No se encontró ninguna configuración de inicio. Cree un archivo \"launch.json\"." } \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/debug/browser/debugStatus.i18n.json b/i18n/esn/src/vs/workbench/parts/debug/browser/debugStatus.i18n.json new file mode 100644 index 00000000000..7a6db36a29e --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/debug/browser/debugStatus.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. +{ + "debug": "Depurar" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/debug/browser/debugViewlet.i18n.json b/i18n/esn/src/vs/workbench/parts/debug/browser/debugViewlet.i18n.json new file mode 100644 index 00000000000..8b6ad71cd4e --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/debug/browser/debugViewlet.i18n.json @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json b/i18n/esn/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json index f4d25015e28..28c7690312b 100644 --- a/i18n/esn/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json +++ b/i18n/esn/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json @@ -15,7 +15,6 @@ "vscode.extension.contributes.debuggers.initialConfigurations": "Configuraciones para generar el archivo \"launch.json\" inicial.", "vscode.extension.contributes.debuggers.languages": "Lista de lenguajes para los que la extensión de depuración podría considerarse el \"depurador predeterminado\".", "vscode.extension.contributes.debuggers.adapterExecutableCommand": "Si se especifica, VS Code llamará a este comando para determinar la ruta de acceso ejecutable del adaptador de depuración y los argumentos que se deben pasar.", - "vscode.extension.contributes.debuggers.startSessionCommand": "Si se especifica, VS Code llamará a este comando para las acciones de \"depuración\" o \"ejecución\" destinadas para esta extensión.", "vscode.extension.contributes.debuggers.configurationSnippets": "Fragmentos de código para agregar nuevas configuraciones a \"launch.json\".", "vscode.extension.contributes.debuggers.configurationAttributes": "Configuraciones de esquema JSON para validar \"launch.json\".", "vscode.extension.contributes.debuggers.windows": "Configuración específica de Windows.", diff --git a/i18n/esn/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json b/i18n/esn/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json index 39eca78322f..01b6c98e302 100644 --- a/i18n/esn/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json +++ b/i18n/esn/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json @@ -12,10 +12,7 @@ "breakpointRemoved": "Punto de interrupción quitado, línea {0}, archivo {1}", "compoundMustHaveConfigurations": "El compuesto debe tener configurado el atributo \"configurations\" a fin de iniciar varias configuraciones.", "configMissing": "La configuración \"{0}\" falta en \"launch.json\".", - "debugRequestNotSupported": "La configuración de depuración seleccionada tiene un valor de atributo '{0}': '{1}' no compatible.", - "debugRequesMissing": "El atributo '{0}' está ausente en la configuración de depuración elegida.", "debugTypeNotSupported": "El tipo de depuración '{0}' configurado no es compatible.", - "debugTypeMissing": "Falta la propiedad \"type\" en la configuración de inicio seleccionada.", "preLaunchTaskErrors": "Errores de compilación durante la tarea preLaunchTask '{0}'.", "preLaunchTaskError": "Error de compilación durante la tarea preLaunchTask '{0}'.", "preLaunchTaskExitCode": "La tarea preLaunchTask '{0}' finalizó con el código de salida {1}.", diff --git a/i18n/esn/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json b/i18n/esn/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json index c71edc9f51f..29384cb26e7 100644 --- a/i18n/esn/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json +++ b/i18n/esn/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json @@ -7,6 +7,5 @@ "stateCapture": "El estado del objeto se captura desde la primera evaluación", "replVariableAriaLabel": "La variable {0} tiene el valor {1}, read–eval–print loop, depuración", "replExpressionAriaLabel": "La expresión {0} tiene el valor {1}, read–eval–print loop, depuración", - "replValueOutputAriaLabel": "{0}, read–eval–print loop, depuración", - "replKeyValueOutputAriaLabel": "La variable de salida {0} tiene el valor {1}, read–eval–print loop, depuración" + "replValueOutputAriaLabel": "{0}, read–eval–print loop, depuración" } \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json b/i18n/esn/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json index 9b4c0a6fb45..5b4f1d9a867 100644 --- a/i18n/esn/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json +++ b/i18n/esn/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "filesCategory": "Archivos", + "filesCategory": "Archivo", "revealInSideBar": "Mostrar en barra lateral", "acceptLocalChanges": "Usar los cambios y sobrescribir el contenido del disco", "revertLocalChanges": "Descartar los cambios y volver al contenido del disco" diff --git a/i18n/esn/src/vs/workbench/parts/files/browser/fileActions.i18n.json b/i18n/esn/src/vs/workbench/parts/files/browser/fileActions.i18n.json index 836362af71e..5d9def6792e 100644 --- a/i18n/esn/src/vs/workbench/parts/files/browser/fileActions.i18n.json +++ b/i18n/esn/src/vs/workbench/parts/files/browser/fileActions.i18n.json @@ -23,6 +23,7 @@ "confirmMoveTrashMessageFile": "¿Está seguro de que desea eliminar '{0}'?", "undoBin": "Puede restaurar desde la papelera de reciclaje.", "undoTrash": "Puede restaurar desde la papelera.", + "doNotAskAgain": "No volver a preguntarme", "confirmDeleteMessageFolder": "¿Está seguro de que desea eliminar '{0}' y su contenido de forma permanente?", "confirmDeleteMessageFile": "¿Está seguro de que desea eliminar '{0}' de forma permanente?", "irreversible": "Esta acción es irreversible.", @@ -37,8 +38,6 @@ "openToSide": "Abrir en el lateral", "compareSource": "Seleccionar para comparar", "globalCompareFile": "Comparar archivo activo con...", - "pickHistory": "Seleccione un archivo abierto anteriormente para comparar", - "unableToFileToCompare": "El archivo seleccionado no se puede comparar con '{0}'.", "openFileToCompare": "Abrir un archivo antes para compararlo con otro archivo.", "compareWith": "Comparar \"{0}\" con \"{1}\"", "compareFiles": "Comparar archivos", @@ -47,7 +46,7 @@ "saveAs": "Guardar como...", "saveAll": "Guardar todos", "saveAllInGroup": "Guardar todo en el grupo", - "saveFiles": "Guardar archivos con modificaciones", + "saveFiles": "Guardar todos los archivos", "revert": "Revertir archivo", "focusOpenEditors": "Foco sobre la vista de editores abiertos", "focusFilesExplorer": "Enfocar Explorador de archivos", diff --git a/i18n/esn/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json b/i18n/esn/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json index ed00449cce3..8857fa7d07b 100644 --- a/i18n/esn/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json +++ b/i18n/esn/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json @@ -6,6 +6,7 @@ { "noWorkspace": "No hay ninguna carpeta abierta", "explorerSection": "Sección del Explorador de archivos", - "noWorkspaceHelp": "Todavía no ha abierto ninguna carpeta.", + "noWorkspaceHelp": "Todavía no ha agregado una carpeta al espacio de trabajo.", + "noFolderHelp": "Todavía no ha abierto ninguna carpeta.", "openFolder": "Abrir carpeta" } \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json b/i18n/esn/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json index 7903df9c11b..40f9fcc3277 100644 --- a/i18n/esn/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json +++ b/i18n/esn/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json @@ -4,12 +4,14 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { + "canNotResolve": "No se puede resolver la carpeta {0}", "fileInputAriaLabel": "Escriba el nombre de archivo. Presione ENTRAR para confirmar o Esc para cancelar", "filesExplorerViewerAriaLabel": "{0}, Explorador de archivos", "dropFolders": "¿Quiere agregar las carpetas al área de trabajo?", "dropFolder": "¿Quiere agregar la carpeta al área de trabajo?", "addFolders": "&&Agregar carpetas", "addFolder": "&&Agregar carpeta", + "confirmMove": "¿Está seguro de que desea mover '{0}'?", "confirmOverwriteMessage": "'{0}' ya existe en la carpeta de destino. ¿Desea reemplazarlo?", "irreversible": "Esta acción es irreversible.", "replaceButtonLabel": "Reemplazar" diff --git a/i18n/esn/src/vs/workbench/parts/markers/browser/markersFileDecorations.i18n.json b/i18n/esn/src/vs/workbench/parts/markers/browser/markersFileDecorations.i18n.json new file mode 100644 index 00000000000..710f5b027a0 --- /dev/null +++ b/i18n/esn/src/vs/workbench/parts/markers/browser/markersFileDecorations.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "label": "Problemas", + "tooltip.1": "1 problema en este fichero", + "tooltip.N": "{0} problemas en este fichero", + "markers.showOnFile": "Mostrar Errores y Advertencias en la carpeta y ficheros." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json b/i18n/esn/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json index ed5419507d3..da443e5daba 100644 --- a/i18n/esn/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json +++ b/i18n/esn/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json @@ -5,7 +5,6 @@ // Do not edit this file. It is machine generated. { "emptyUserSettingsHeader": "Coloque aquí su configuración para sobrescribir la configuración predeterminada.", - "errorInvalidConfiguration": "No se puede escribir la configuración. Corrija los errores o advertencias del archivo y vuelva a intentarlo.", "emptyWorkspaceSettingsHeader": "Coloque aquí su configuración para sobrescribir la configuración de usuario.", "emptyFolderSettingsHeader": "Coloque aquí su configuración de carpeta para sobrescribir la que se especifica en la configuración de área de trabajo.", "defaultFolderSettingsTitle": "Configuración de carpeta predeterminada", diff --git a/i18n/esn/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json b/i18n/esn/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json index 5b11b46879d..e0012dd14f7 100644 --- a/i18n/esn/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json +++ b/i18n/esn/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json @@ -13,7 +13,6 @@ "actionNotEnabled": "El comando '{0}' no está habilitado en el contexto actual.", "recentlyUsed": "usado recientemente", "morecCommands": "otros comandos", - "commandLabel": "{0}: {1}", "cat.title": "{0}: {1}", "noCommandsMatching": "No hay comandos coincidentes" } \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json b/i18n/esn/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json index c0de845ea64..4c8491b6c87 100644 --- a/i18n/esn/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json +++ b/i18n/esn/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json @@ -4,6 +4,8 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { + "show previous change": "Mostrar el cambio anterior", + "show next change": "Mostrar el cambio siguiente", "editorGutterModifiedBackground": "Color de fondo del medianil del editor para las líneas modificadas.", "editorGutterAddedBackground": "Color de fondo del medianil del editor para las líneas agregadas.", "editorGutterDeletedBackground": "Color de fondo del medianil del editor para las líneas eliminadas.", diff --git a/i18n/esn/src/vs/workbench/parts/search/browser/search.contribution.i18n.json b/i18n/esn/src/vs/workbench/parts/search/browser/search.contribution.i18n.json index a77598c77bc..d0eef49247f 100644 --- a/i18n/esn/src/vs/workbench/parts/search/browser/search.contribution.i18n.json +++ b/i18n/esn/src/vs/workbench/parts/search/browser/search.contribution.i18n.json @@ -17,7 +17,7 @@ "exclude": "Configure patrones globales para excluir archivos y carpetas de las búsquedas. Hereda todos los patrones globales de la configuración files.exclude.", "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.", "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.", - "useRipgrep": "Controla si debe utilizarse ripgrep en la búsqueda de texto", + "useRipgrep": "Controla si se utiliza ripgrep en la búsqueda de texto y ficheros", "useIgnoreFilesByDefault": "Controla si usar los archivos .gitignore y .ignore de forma predeterminada al buscar en una nueva área de trabajo.", "search.quickOpen.includeSymbols": "Configurar para incluir los resultados de una búsqueda global de símbolos en los resultados de archivos de Quick Open." } \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/search/browser/searchActions.i18n.json b/i18n/esn/src/vs/workbench/parts/search/browser/searchActions.i18n.json index 314acd5b6dd..572f519b892 100644 --- a/i18n/esn/src/vs/workbench/parts/search/browser/searchActions.i18n.json +++ b/i18n/esn/src/vs/workbench/parts/search/browser/searchActions.i18n.json @@ -19,7 +19,6 @@ "ClearSearchResultsAction.label": "Borrar resultados de la búsqueda", "FocusNextSearchResult.label": "Centrarse en el siguiente resultado de la búsqueda", "FocusPreviousSearchResult.label": "Centrarse en el anterior resultado de la búsqueda", - "RemoveAction.label": "Quitar", "file.replaceAll.label": "Reemplazar todo", "match.replace.label": "Reemplazar" } \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json b/i18n/esn/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json index 3353f89b68d..bc5073a75cf 100644 --- a/i18n/esn/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json +++ b/i18n/esn/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json @@ -10,8 +10,8 @@ "vscode.extension.contributes.snippets": "Aporta fragmentos de código.", "vscode.extension.contributes.snippets-language": "Identificador del lenguaje al que se aporta este fragmento de código.", "vscode.extension.contributes.snippets-path": "Ruta de acceso del archivo de fragmentos de código. La ruta es relativa a la carpeta de extensión y normalmente empieza por \"./snippets/\".", - "badFile": "No se pudo leer el archivo del fragmento \"{0}\".", "badVariableUse": "Es muy probable que el fragmento de código \"{0}\" confunda las variables de fragmento de código y los marcadores de posición de fragmento de código. Consulte https://code.visualstudio.com/docs/editor/userdefinedsnippets#_snippet-syntax para más informacion.", + "badFile": "No se pudo leer el archivo del fragmento \"{0}\".", "source.snippet": "Fragmento de código del usuario", "detail.snippet": "{0} ({1})", "snippetSuggest.longLabel": "{0}, {1}" 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 ad64a450cf5..50d2ca8b4b1 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 @@ -30,6 +30,7 @@ "TaskSystem.activeSame.noBackground": "La tarea \"{0}\" ya está activa. Para terminarla, use la opción \"Terminar tarea\" del menú Tareas.", "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}", + "TaskService.noConfiguration": "Error: La detección de tarea {0} no encontró una tarea para la siguiente configuración:\n{1}\nLa tarea será omitida.\n", "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 tarea para la carpeta del area de trabajo {0}. El soporte de tarea de area de trabajo multi carpeta requiere que todas las carpetas usen la versión de tarea 2.0.0 ", "TaskSystem.invalidTaskJson": "Error: El contenido del archivo tasks.json tiene errores de sintaxis. Corríjalos antes de ejecutar una tarea.", diff --git a/i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json b/i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json index 2fb25c70d99..c3d0cfd1e37 100644 --- a/i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json +++ b/i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json @@ -16,7 +16,6 @@ "workbench.action.terminal.new.short": "Nuevo terminal", "workbench.action.terminal.focus": "Enfocar terminal", "workbench.action.terminal.focusNext": "Enfocar terminal siguiente", - "workbench.action.terminal.focusAtIndex": "Enfocar terminal {0}", "workbench.action.terminal.focusPrevious": "Enfocar terminal anterior", "workbench.action.terminal.paste": "Pegar en el terminal activo", "workbench.action.terminal.DefaultShell": "Seleccionar el shell predeterminado", diff --git a/i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json b/i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json index 1b04639ccd5..a2ea49d47f1 100644 --- a/i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json +++ b/i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json @@ -5,7 +5,6 @@ // Do not edit this file. It is machine generated. { "copy": "Copiar", - "createNewTerminal": "Nuevo terminal", "paste": "Pegar", "selectAll": "Seleccionar todo", "clear": "Borrar" diff --git a/i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json b/i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json index 6b1dbc4c66a..1a5374df014 100644 --- a/i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json +++ b/i18n/esn/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json @@ -10,6 +10,5 @@ "never again": "De acuerdo, no volver a mostrar este mensaje", "terminal.integrated.chooseWindowsShell": "Seleccione el shell de terminal que desee, puede cambiarlo más adelante en la configuración", "terminalService.terminalCloseConfirmationSingular": "Hay una sesión de terminal activa, ¿quiere terminarla?", - "terminalService.terminalCloseConfirmationPlural": "Hay {0} sesiones de terminal activas, ¿quiere terminarlas?", - "yes": "Sí" + "terminalService.terminalCloseConfirmationPlural": "Hay {0} sesiones de terminal activas, ¿quiere terminarlas?" } \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/services/configuration/common/configurationExtensionPoint.i18n.json b/i18n/esn/src/vs/workbench/services/configuration/common/configurationExtensionPoint.i18n.json new file mode 100644 index 00000000000..38c0dd3726b --- /dev/null +++ b/i18n/esn/src/vs/workbench/services/configuration/common/configurationExtensionPoint.i18n.json @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "vscode.extension.contributes.configuration.title": "Resumen de la configuración. Esta etiqueta se usará en el archivo de configuración como comentario divisor.", + "vscode.extension.contributes.configuration.properties": "Descripción de las propiedades de configuración.", + "scope.window.description": "Configuración específica para ventanas, que se puede definir en la configuración de usuario o de área de trabajo.", + "scope.resource.description": "Configuración específica para recursos, que se puede definir en la configuración de usuario, de área de trabajo o de carpeta.", + "scope.description": "Ámbito donde es aplicable la configuración. Los ámbitos disponibles son \"window\" y \"resource\".", + "vscode.extension.contributes.configuration": "Aporta opciones de configuración.", + "invalid.title": "configuration.title debe ser una cadena", + "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", + "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.settings.description": "Configuración de área de trabajo", + "workspaceConfig.extensions.description": "Extensiones del área de trabajo", + "unknownWorkspaceProperty": "Propiedad de configuración de espacio de trabajo desconocida" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/services/editor/common/editorService.i18n.json b/i18n/esn/src/vs/workbench/services/editor/common/editorService.i18n.json new file mode 100644 index 00000000000..50e968f8ee3 --- /dev/null +++ b/i18n/esn/src/vs/workbench/services/editor/common/editorService.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. +{ + "compareLabels": "{0} ↔ {1}" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/services/themes/electron-browser/colorThemeStore.i18n.json b/i18n/esn/src/vs/workbench/services/themes/electron-browser/colorThemeStore.i18n.json new file mode 100644 index 00000000000..5be27841f5b --- /dev/null +++ b/i18n/esn/src/vs/workbench/services/themes/electron-browser/colorThemeStore.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "vscode.extension.contributes.themes": "Contributes textmate color themes.", + "vscode.extension.contributes.themes.id": "Identificador del tema de icono como se usa en la configuración de usuario.", + "vscode.extension.contributes.themes.label": "Etiqueta del tema de color tal como se muestra en la interfaz de usuario.", + "vscode.extension.contributes.themes.uiTheme": "Tema base que define los colores que se usan en el editor: 'vs' es el tema de color claro, 'vs-dark' es el tema de color oscuro, 'hc-black' es el tema oscuro de alto contraste.", + "vscode.extension.contributes.themes.path": "Ruta de acceso del archivo tmTheme. La ruta de acceso es relativa a la carpeta de extensión y suele ser './themes/themeFile.tmTheme'.", + "reqarray": "Extension point `{0}` must be an array.", + "reqpath": "Se esperaba una cadena en \"contributes.{0}.path\". Valor proporcionado: {1}", + "invalid.path.1": "Se esperaba que \"contributes.{0}.path\" ({1}) se incluyera en la carpeta de la extensión ({2}). Esto puede hacer que la extensión no sea portátil." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.i18n.json b/i18n/esn/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.i18n.json new file mode 100644 index 00000000000..b0f9faf6b52 --- /dev/null +++ b/i18n/esn/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.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. +{ + "error.cannotparseicontheme": "Problems parsing file icons file: {0}" +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.i18n.json b/i18n/esn/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.i18n.json new file mode 100644 index 00000000000..541e0ac3d8b --- /dev/null +++ b/i18n/esn/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "vscode.extension.contributes.iconThemes": "Contributes file icon themes.", + "vscode.extension.contributes.iconThemes.id": "Identificador del tema de icono como se usa en la configuración de usuario.", + "vscode.extension.contributes.iconThemes.label": "Etiqueta del tema de icono como se muestra en la interfaz de usuario.", + "vscode.extension.contributes.iconThemes.path": "Ruta de acceso del archivo de definición de temas de icono. La ruta de acceso es relativa a la carpeta de extensión y suele ser './icons/awesome-icon-theme.json'.", + "reqarray": "Extension point `{0}` must be an array.", + "reqpath": "Se esperaba una cadena en \"contributes.{0}.path\". Valor proporcionado: {1}", + "reqid": "Se esperaba una cadena en `contributes.{0}.id`. Valor proporcionado: {1}", + "invalid.path.1": "Se esperaba que \"contributes.{0}.path\" ({1}) se incluyera en la carpeta de la extensión ({2}). Esto puede hacer que la extensión no sea portátil." +} \ No newline at end of file diff --git a/i18n/esn/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json b/i18n/esn/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json index 7c308f9717e..cb85af9d3cd 100644 --- a/i18n/esn/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json +++ b/i18n/esn/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json @@ -4,31 +4,15 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "vscode.extension.contributes.themes": "Contributes textmate color themes.", - "vscode.extension.contributes.themes.id": "Identificador del tema de icono como se usa en la configuración de usuario.", - "vscode.extension.contributes.themes.label": "Etiqueta del tema de color tal como se muestra en la interfaz de usuario.", - "vscode.extension.contributes.themes.uiTheme": "Tema base que define los colores que se usan en el editor: 'vs' es el tema de color claro, 'vs-dark' es el tema de color oscuro, 'hc-black' es el tema oscuro de alto contraste.", - "vscode.extension.contributes.themes.path": "Ruta de acceso del archivo tmTheme. La ruta de acceso es relativa a la carpeta de extensión y suele ser './themes/themeFile.tmTheme'.", - "vscode.extension.contributes.iconThemes": "Contributes file icon themes.", - "vscode.extension.contributes.iconThemes.id": "Identificador del tema de icono como se usa en la configuración de usuario.", - "vscode.extension.contributes.iconThemes.label": "Etiqueta del tema de icono como se muestra en la interfaz de usuario.", - "vscode.extension.contributes.iconThemes.path": "Ruta de acceso del archivo de definición de temas de icono. La ruta de acceso es relativa a la carpeta de extensión y suele ser './icons/awesome-icon-theme.json'.", "migration.completed": "Se han agregado nuevos valores de tema a la configuración de usuario. Hay una copia de seguridad disponible en {0}.", "error.cannotloadtheme": "Unable to load {0}: {1}", - "reqarray": "Extension point `{0}` must be an array.", - "reqpath": "Se esperaba una cadena en \"contributes.{0}.path\". Valor proporcionado: {1}", - "invalid.path.1": "Se esperaba que \"contributes.{0}.path\" ({1}) se incluyera en la carpeta de la extensión ({2}). Esto puede hacer que la extensión no sea portátil.", - "reqid": "Se esperaba una cadena en `contributes.{0}.id`. Valor proporcionado: {1}", "error.cannotloadicontheme": "Unable to load {0}", - "error.cannotparseicontheme": "Problems parsing file icons file: {0}", "colorTheme": "Specifies the color theme used in the workbench.", "colorThemeError": "Theme is unknown or not installed.", "iconTheme": "Especifica el tema de icono utilizado en el área de trabajo o \"null\" para no mostrar ningún icono de archivo.", "noIconThemeDesc": "No file icons", "iconThemeError": "File icon theme is unknown or not installed.", "workbenchColors": "Reemplaza los colores del tema de color actual", - "workbenchColors.deprecated": "Esta configuracion no es experimental y se ha cambiado de nombre a 'workbench.colorCustomizations'", - "workbenchColors.deprecatedDescription": "Utilice 'workbench.colorCustomizations' en su lugar", "editorColors": "Reemplaza los colores y el estilo de fuente del editor del tema de color seleccionado.", "editorColors.comments": "Establece los colores y estilos para los comentarios", "editorColors.strings": "Establece los colores y estilos para los literales de cadena.", diff --git a/i18n/esn/src/vs/workbench/services/workspace/node/workspaceEditingService.i18n.json b/i18n/esn/src/vs/workbench/services/workspace/node/workspaceEditingService.i18n.json new file mode 100644 index 00000000000..17798aeb3c8 --- /dev/null +++ b/i18n/esn/src/vs/workbench/services/workspace/node/workspaceEditingService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "openWorkspaceConfigurationFile": "Abrir archivo de configuración del área de trabajo", + "close": "Cerrar" +} \ No newline at end of file diff --git a/i18n/fra/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json b/i18n/fra/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json index fcedce53b3c..c8334701a65 100644 --- a/i18n/fra/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json +++ b/i18n/fra/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json @@ -4,13 +4,13 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "activeEditorShort": "exemple : myFile.txt", - "activeEditorMedium": "exemple : myFolder/myFile.txt", - "activeEditorLong": "exemple : /Users/Development/myProject/myFolder/myFile.txt", - "rootName": "par ex., myFolder1, myFolder2, myFolder3", - "rootPath": "exemple : /Users/Development/myProject", - "folderName": "par ex., myFolder", - "folderPath": "par ex., /Users/Development/myFolder", + "activeEditorShort": "le nom du fichier (ex: monfichier.txt)", + "activeEditorMedium": "le chemin d’accès du fichier relatif au dossier de l’espace de travail (ex: myFolder/myFile.txt)", + "activeEditorLong": "le chemin d’accès complet du fichier (ex: /Users/Development/myProject/myFolder/myFile.txt)", + "rootName": "nom de l’espace de travail (ex: monDossier ou monEspaceDeTravail)", + "rootPath": "chemin d’accès de l’espace de travail (ex: /Users/Development/myWorkspace)", + "folderName": "nom du dossier de l'espace de travail auquel le fichier appartient (ex: monDossier)", + "folderPath": "chemin d’accès du dossier de l'espace de travail auquel le fichier appartient (ex: /Users/Development/myFolder)", "appName": "exemple : VS Code", "dirty": "indicateur d'intégrité si l'intégrité de l'éditeur actif est compromise", "separator": "séparateur conditionnel (' - ') qui s'affiche uniquement quand il est entouré de variables avec des valeurs", diff --git a/i18n/fra/extensions/emmet/package.i18n.json b/i18n/fra/extensions/emmet/package.i18n.json index d9a0b979e40..3136f6135aa 100644 --- a/i18n/fra/extensions/emmet/package.i18n.json +++ b/i18n/fra/extensions/emmet/package.i18n.json @@ -28,13 +28,6 @@ "command.incrementNumberByTen": "Incrémenter de 10", "command.decrementNumberByTen": "Décrémenter de 10", "emmetSyntaxProfiles": "Définissez le profil pour la syntaxe spécifiée ou utilisez votre propre profil avec des règles spécifiques.", - "emmetExclude": "Ensemble de langages où les abréviations emmet ne doivent pas être développées.", - "emmetExtensionsPath": "Chemin d'un dossier contenant les profils et les extraits Emmet.", - "emmetShowExpandedAbbreviation": "Affiche les abréviations Emmet développées sous forme de suggestions.\nL’option \"inMarkupAndStylesheetFilesOnly\" s’applique à html, haml, jade, slim, xml, xsl, css, scss, sass, less et un stylet.\nL’option \"toujours\" s’applique à toutes les parties du fichier indépendamment du balisage/css.", - "emmetShowAbbreviationSuggestions": "Affiche les abréviations Emmet possibles sous forme de suggestions. Non applicable dans les feuilles de style ou quand emmet.showExpandedAbbreviation est défini sur \"jamais\".", - "emmetIncludeLanguages": "Active les abréviations Emmet dans les langages qui ne sont pas pris en charge par défaut. Ajoute un mappage ici entre le langage et le langage Emmet pris en charge .\n Par exemple : {\"vue-html\": \"html\", \"javascript\": \"javascriptreact\"}", - "emmetVariables": "Variables à utiliser dans les extraits Emmet", - "emmetTriggerExpansionOnTab": "Une fois les abréviations Emmet activées, elles se développent quand vous appuyez sur la touche Tab.", "emmetPreferences": "Préférences utilisées pour modifier le comportement de certaines actions et résolveurs d'Emmet.", "emmetPreferencesIntUnit": "Unité par défaut pour les valeurs entières", "emmetPreferencesFloatUnit": "Unité par défaut pour les valeurs float", @@ -44,5 +37,7 @@ "emmetPreferencesCssBetween": "Symbole à placer entre la propriété CSS et la valeur pendant le développement des abréviations CSS", "emmetPreferencesSassBetween": "Symbole à placer entre la propriété CSS et la valeur pendant le développement des abréviations CSS dans les fichiers Sass", "emmetPreferencesStylusBetween": "Symbole à placer entre la propriété CSS et la valeur pendant le développement des abréviations CSS dans les fichiers Stylus", - "emmetShowSuggestionsAsSnippets": "Si la valeur est True, les suggestions Emmet s'affichent sous forme d'extraits, ce qui vous permet de les ordonner conformément au paramètre editor.snippetSuggestions." + "emmetPreferencesFilterCommentBefore": "Une définition de commentaire qui doit être placée avant l’élément correspondant quand le filtre de commentaire est appliqué.", + "emmetPreferencesFilterCommentAfter": "Une définition de commentaire qui doit être placée après l’élément correspondant quand un filtre de commentaire est appliqué.", + "emmetPreferencesFilterCommentTrigger": "Une liste séparée par des virgules de noms d’attributs qui devraient exister en abrégé pour que le filtre de commentaire soit appliqué" } \ No newline at end of file diff --git a/i18n/fra/extensions/git/out/commands.i18n.json b/i18n/fra/extensions/git/out/commands.i18n.json index 72e09a4dac2..e0dab3e5a12 100644 --- a/i18n/fra/extensions/git/out/commands.i18n.json +++ b/i18n/fra/extensions/git/out/commands.i18n.json @@ -12,8 +12,9 @@ "cloning": "Clonage du dépôt git...", "openrepo": "Ouvrir le dépôt", "proposeopen": "Voulez-vous ouvrir le dépôt cloné ?", - "path to init": "Chemin du dossier", - "provide path": "Veuillez fournir un chemin de dossier pour initialiser un dépôt Git", + "init repo": "Initialiser le dépôt", + "create repo": "Initialiser le dépôt", + "are you sure": "Ceci va créer un dépôt Git dans '{0}'. Êtes-vous sûr de vouloir continuer ?", "HEAD not available": "La version HEAD de '{0}' n'est pas disponible.", "confirm stage files with merge conflicts": "Voulez-vous vraiment créer {0} fichiers avec des conflits de fusion ?", "confirm stage file with merge conflicts": "Voulez-vous vraiment créer {0} avec des conflits de fusion ?", diff --git a/i18n/fra/extensions/git/out/repository.i18n.json b/i18n/fra/extensions/git/out/repository.i18n.json index 75b81935d06..9f5e062bdb0 100644 --- a/i18n/fra/extensions/git/out/repository.i18n.json +++ b/i18n/fra/extensions/git/out/repository.i18n.json @@ -21,6 +21,8 @@ "deleted by us": "Supprimé par nos soins", "both added": "Tous deux ajoutés", "both modified": "Tous deux modifiés", + "untracked, short": "U", + "modified, short": "M", "commit": "Commit", "merge changes": "Fusionner les modifications", "staged changes": "Modifications en zone de transit", diff --git a/i18n/fra/extensions/git/package.i18n.json b/i18n/fra/extensions/git/package.i18n.json index 6253e6cf5fe..e5008fa48da 100644 --- a/i18n/fra/extensions/git/package.i18n.json +++ b/i18n/fra/extensions/git/package.i18n.json @@ -15,6 +15,8 @@ "command.stageAll": "Mettre en attente toutes les modifications", "command.stageSelectedRanges": "Mettre en attente les plages sélectionnées", "command.revertSelectedRanges": "Restaurer les portées sélectionnées", + "command.stageChange": "Mettre en attente la modification", + "command.revertChange": "Restaurer la modification", "command.unstage": "Annuler la mise en attente des modifications", "command.unstageAll": "Annuler la mise en attente de toutes les modifications", "command.unstageSelectedRanges": "Annuler la mise en attente des plages sélectionnées", diff --git a/i18n/fra/extensions/typescript/package.i18n.json b/i18n/fra/extensions/typescript/package.i18n.json index 8762fa61f04..848e776f0d6 100644 --- a/i18n/fra/extensions/typescript/package.i18n.json +++ b/i18n/fra/extensions/typescript/package.i18n.json @@ -44,7 +44,8 @@ "typescript.npm": "Spécifie le chemin de l'exécutable NPM utilisé pour l'acquisition de type automatique. Nécessite TypeScript >= 2.3.4.", "typescript.check.npmIsInstalled": "Vérifie si NPM est installé pour l'acquisition de type automatique.", "javascript.nameSuggestions": "Activez/désactivez l'inclusion de noms uniques à partir du fichier dans les listes de suggestions JavaScript.", - "typescript.tsc.autoDetect": "Contrôle si la détection automatique des tâches tsc est activée ou désactivée.", + "typescript.tsc.autoDetect": "Contrôle la détection automatique des tâches tsc. 'off' désactive cette fonctionnalité. 'build' crée uniquement des tâches de compilation à exécution unique. 'watch' crée uniquement des tâches de compilation et de watch. 'on' crée les deux les tâches build et watch. La valeur par défaut est 'on'.", "typescript.problemMatchers.tsc.label": "Problèmes liés à TypeScript", - "typescript.problemMatchers.tscWatch.label": "Problèmes liés à TypeScript (mode espion)" + "typescript.problemMatchers.tscWatch.label": "Problèmes liés à TypeScript (mode espion)", + "typescript.quickSuggestionsForPaths": "Activer/désactiver les suggestions rapides lorsque vous saisissez un chemin d’import." } \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/contrib/find/browser/findWidget.i18n.json b/i18n/fra/src/vs/editor/contrib/find/browser/findWidget.i18n.json index e6ecb5ce261..35e6cb71d88 100644 --- a/i18n/fra/src/vs/editor/contrib/find/browser/findWidget.i18n.json +++ b/i18n/fra/src/vs/editor/contrib/find/browser/findWidget.i18n.json @@ -15,7 +15,6 @@ "label.replaceButton": "Remplacer", "label.replaceAllButton": "Tout remplacer", "label.toggleReplaceButton": "Changer le mode de remplacement", - "title.matchesCountLimit": "Seuls les 999 premiers résultats sont mis en surbrillance. Cependant, toutes les opérations de recherche sont appliquées à l'ensemble du texte.", "label.matchesLocation": "{0} sur {1}", "label.noResults": "Aucun résultat" } \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/contrib/find/common/findController.i18n.json b/i18n/fra/src/vs/editor/contrib/find/common/findController.i18n.json index 2176cea208c..21889b4123e 100644 --- a/i18n/fra/src/vs/editor/contrib/find/common/findController.i18n.json +++ b/i18n/fra/src/vs/editor/contrib/find/common/findController.i18n.json @@ -10,12 +10,6 @@ "nextSelectionMatchFindAction": "Sélection suivante", "previousSelectionMatchFindAction": "Sélection précédente", "startReplace": "Remplacer", - "addSelectionToNextFindMatch": "Ajouter la sélection à la correspondance de recherche suivante", - "addSelectionToPreviousFindMatch": "Ajouter la sélection à la correspondance de recherche précédente", - "moveSelectionToNextFindMatch": "Déplacer la dernière sélection vers la correspondance de recherche suivante", - "moveSelectionToPreviousFindMatch": "Déplacer la dernière sélection à la correspondance de recherche précédente", - "selectAllOccurrencesOfFindMatch": "Sélectionner toutes les occurrences des correspondances de la recherche", - "changeAll.label": "Modifier toutes les occurrences", "showNextFindTermAction": "Afficher le terme de recherche suivant", "showPreviousFindTermAction": "Afficher le terme de recherche précédent" } \ No newline at end of file diff --git a/i18n/fra/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json b/i18n/fra/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json index 081fbcf0bec..04e943155da 100644 --- a/i18n/fra/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json +++ b/i18n/fra/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json @@ -6,5 +6,11 @@ { "mutlicursor.insertAbove": "Ajouter un curseur au-dessus", "mutlicursor.insertBelow": "Ajouter un curseur en dessous", - "mutlicursor.insertAtEndOfEachLineSelected": "Ajouter des curseurs à la fin des lignes" + "mutlicursor.insertAtEndOfEachLineSelected": "Ajouter des curseurs à la fin des lignes", + "addSelectionToNextFindMatch": "Ajouter la sélection à la correspondance de recherche suivante", + "addSelectionToPreviousFindMatch": "Ajouter la sélection à la correspondance de recherche précédente", + "moveSelectionToNextFindMatch": "Déplacer la dernière sélection vers la correspondance de recherche suivante", + "moveSelectionToPreviousFindMatch": "Déplacer la dernière sélection à la correspondance de recherche précédente", + "selectAllOccurrencesOfFindMatch": "Sélectionner toutes les occurrences des correspondances de la recherche", + "changeAll.label": "Modifier toutes les occurrences" } \ No newline at end of file diff --git a/i18n/fra/src/vs/platform/theme/common/colorRegistry.i18n.json b/i18n/fra/src/vs/platform/theme/common/colorRegistry.i18n.json index 1c47e156e38..2cfa0fbeab4 100644 --- a/i18n/fra/src/vs/platform/theme/common/colorRegistry.i18n.json +++ b/i18n/fra/src/vs/platform/theme/common/colorRegistry.i18n.json @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "invalid.color": "Format de couleur non valide. Utilisez #RGB, #RGBA, #RRGGBB ou #RRGGBBAA", "schema.colors": "Couleurs utilisées dans le banc d'essai.", "foreground": "Couleur de premier plan globale. Cette couleur est utilisée si elle n'est pas remplacée par un composant.", "errorForeground": "Couleur principale de premier plan pour les messages d'erreur. Cette couleur est utilisée uniquement si elle n'est pas redéfinie par un composant.", 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 857489eaa8f..778330402d3 100644 --- a/i18n/fra/src/vs/workbench/browser/actions/workspaceActions.i18n.json +++ b/i18n/fra/src/vs/workbench/browser/actions/workspaceActions.i18n.json @@ -9,14 +9,17 @@ "addFolderToWorkspace": "Ajouter un dossier à l'espace de travail...", "add": "&&Ajouter", "addFolderToWorkspaceTitle": "Ajouter un dossier à l'espace de travail", + "globalRemoveFolderFromWorkspace": "Supprimer le dossier d’espace de travail...", "newWorkspace": "Nouvel espace de travail...", "select": "&&Sélectionner", "selectWorkspace": "Sélectionner les dossiers pour l’espace de travail", "removeFolderFromWorkspace": "Supprimer le dossier de l'espace de travail", + "openFolderSettings": "Ouvrir le dossier Paramètres", "saveWorkspaceAsAction": "Enregistrer l’espace de travail sous...", "save": "&&Enregistrer", "saveWorkspace": "Enregistrer l’espace de travail", "openWorkspaceAction": "Ouvrir un espace de travail...", "openWorkspaceConfigFile": "Ouvrir le Fichier de Configuration d’espace de travail", + "openFolderAsWorkspaceInNewWindow": "Ouvrir le dossier en tant qu'espace de travail dans une nouvelle fenêtre", "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/activitybarPart.i18n.json b/i18n/fra/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json index a04b897abfe..affef8547e2 100644 --- a/i18n/fra/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json +++ b/i18n/fra/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json @@ -5,6 +5,5 @@ // Do not edit this file. It is machine generated. { "hideActivitBar": "Masquer la barre d'activités", - "activityBarAriaLabel": "Sélecteur d'affichage actif", "globalActions": "Actions globales" } \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/browser/parts/compositebar/compositeBar.i18n.json b/i18n/fra/src/vs/workbench/browser/parts/compositebar/compositeBar.i18n.json new file mode 100644 index 00000000000..ed052dd9d9c --- /dev/null +++ b/i18n/fra/src/vs/workbench/browser/parts/compositebar/compositeBar.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. +{ + "activityBarAriaLabel": "Sélecteur d'affichage actif" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/browser/parts/compositebar/compositeBarActions.i18n.json b/i18n/fra/src/vs/workbench/browser/parts/compositebar/compositeBarActions.i18n.json new file mode 100644 index 00000000000..0935a1bd196 --- /dev/null +++ b/i18n/fra/src/vs/workbench/browser/parts/compositebar/compositeBarActions.i18n.json @@ -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. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "badgeTitle": "{0} - {1}", + "additionalViews": "Vues supplémentaires", + "numberBadge": "{0} ({1})", + "manageExtension": "Gérer l'extension", + "titleKeybinding": "{0} ({1})", + "toggle": "Afficher/masquer la vue épinglée" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json b/i18n/fra/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json index dc32ce2159e..4326e29e9e4 100644 --- a/i18n/fra/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json +++ b/i18n/fra/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json @@ -12,5 +12,6 @@ "groupTwoPicker": "Afficher les éditeurs du deuxième groupe", "groupThreePicker": "Afficher les éditeurs du troisième groupe", "allEditorsPicker": "Afficher tous les éditeurs ouverts", - "view": "Affichage" + "view": "Affichage", + "file": "Fichier" } \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json b/i18n/fra/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json index 6e9f1b4185b..d4a3eda558a 100644 --- a/i18n/fra/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json +++ b/i18n/fra/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json @@ -4,6 +4,5 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "canNotRun": "La commande '{0}' n'est pas activée et ne peut pas être exécutée.", "manageExtension": "Gérer l'extension" } \ 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 de18c2da7d0..32259e528e6 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,18 +10,15 @@ "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": "Affiche 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 d’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) et apparaissent avec un style de police en italique.", "enablePreviewFromQuickOpen": "Contrôle si les éditeurs de Quick Open 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).", - "editorOpenPositioning": "Contrôle l'emplacement de l'ouverture des éditeurs. Sélectionnez 'left' ou 'right' pour ouvrir les éditeurs à gauche ou à droite de l'éditeur actuellement actif. Sélectionnez 'first' ou 'last' pour ouvrir les éditeurs indépendamment de l'éditeur actuellement actif.", "revealIfOpen": "Contrôle si un éditeur est affiché dans l'un des groupes visibles, s'il est ouvert. Si cette option est désactivée, l'éditeur s'ouvre de préférence dans le groupe d'éditeurs actif. Si cette option est activée, tout éditeur déjà ouvert est affiché au lieu d'être rouvert dans le groupe d'éditeurs actif. Notez que dans certains cas, ce paramètre est ignoré, par exemple quand vous forcez un éditeur à s'ouvrir dans un groupe spécifique ou à côté du groupe actif.", - "commandHistory": "Contrôle le nombre de commandes récemment utilisées à conserver dans l'historique de la palette de commandes. Définissez cette valeur sur 0 pour désactiver l'historique de commandes.", + "commandHistory": "Contrôle le nombre de commandes récemment utilisées à retenir dans l’historique de la palette de commande. Spécifier la valeur 0 pour désactiver l’historique des commandes.", "preserveInput": "Contrôle si la dernière entrée tapée dans la palette de commandes doit être restaurée à la prochaine ouverture.", "closeOnFocusLost": "Contrôle si Quick Open doit se fermer automatiquement, une fois qu'il a perdu le focus.", "openDefaultSettings": "Contrôle si l'ouverture des paramètres entraîne également l'ouverture d'un éditeur qui affiche tous les paramètres par défaut.", @@ -50,7 +47,7 @@ "restoreWindows": "Contrôle comment les fenêtres seront rouvertes après un redémarrage. Sélectionner 'none' pour toujours démarrer avec un espace de travail vide, 'one' pour rouvrir la dernière fenêtre avec laquelle vous avez travaillé, 'folders' pour rouvrir toutes les fenêtres qui avaient des dossiers ouverts ou 'all' pour rouvrir toutes les fenêtres de votre dernière session.", "restoreFullscreen": "Contrôle si une fenêtre doit être restaurée en mode plein écran si elle a été fermée dans ce mode.", "zoomLevel": "Modifiez le niveau de zoom de la fenêtre. La taille d'origine est 0. Chaque incrément supérieur (exemple : 1) ou inférieur (exemple : -1) représente un zoom 20 % plus gros ou plus petit. Vous pouvez également entrer des décimales pour changer le niveau de zoom avec une granularité plus fine.", - "title": "Contrôle le titre de la fenêtre en fonction de l'éditeur actif. Les variables sont remplacées selon le contexte :\n${activeEditorShort} : par ex., myFile.txt\n${activeEditorMedium} : par ex., myFolder/myFile.txt\n${activeEditorLong} : par ex., /Users/Development/myProject/myFolder/myFile.txt\n${folderName} : par ex., myFolder\n${folderPath} : par ex., /Users/Development/myFolder\n${rootName} : par ex., myFolder1, myFolder2, myFolder3\n${rootPath} : par ex., /Users/Development/myWorkspace\n${appName} : par ex., VS Code\n${dirty} : indicateur d'intégrité si l'intégrité de l'éditeur actif est compromise\n${separator} : séparateur conditionnel (\" - \") qui s'affiche uniquement quand il est entouré de variables avec des valeurs", + "title": "Contrôle le titre de la fenêtre en fonction sur l’éditeur actif. Les variables sont remplacés selon le contexte : ${activeEditorShort} : le nom du fichier (ex: monFichier.txt) ${activeEditorMedium} : le chemin d’accès au fichier par rapport au dossier de l’espace de travail (ex: monDossier/monFichier.txt) ${activeEditorLong} : le chemin d’accès complet du fichier (par exemple / Users/Developpement/monDossier/monFichier.txt) ${folderName} : nom du dossier de l’espace de travail auquel le fichier appartient (ex: mondossier) ${folderPath} : chemin d’accès du dossier de l'espace de travail auquel le fichier appartient (ex: /Users/Developpement/monDossier) {$ rootName} : nom de l’espace de travail (:. monDossier ou monEspaceDeTravail) ${rootPath} : chemin d’accès de l’espace de travail (ex: /Users/Developpement/monEspaceDeTravail) ${appName} : ex. VS Code ${dirty} : un indicateur si l’éditeur actif est modifié ${separator} : un séparateur conditionnel (\" - \") qui ne s'affiche que quand ils sont entourés par des variables avec des valeurs", "window.newWindowDimensions.default": "Permet d'ouvrir les nouvelles fenêtres au centre de l'écran.", "window.newWindowDimensions.inherit": "Permet d'ouvrir les nouvelles fenêtres avec la même dimension que la dernière fenêtre active.", "window.newWindowDimensions.maximized": "Permet d'ouvrir les nouvelles fenêtres de manière agrandie.", diff --git a/i18n/fra/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json b/i18n/fra/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json index 30c636b7769..914334d0401 100644 --- a/i18n/fra/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json +++ b/i18n/fra/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json @@ -6,6 +6,8 @@ { "entryAriaLabel": "{0}, débogage", "debugAriaLabel": "Tapez le nom d'une configuration de lancement à exécuter.", + "addConfigTo": "Ajouter une configuration ({0})...", + "addConfiguration": "Ajouter une configuration...", "noConfigurationsMatching": "Aucune configuration de débogage correspondante", "noConfigurationsFound": "Configurations de débogage introuvables. Créez un fichier 'launch.json'." } \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/debug/browser/debugStatus.i18n.json b/i18n/fra/src/vs/workbench/parts/debug/browser/debugStatus.i18n.json new file mode 100644 index 00000000000..449ccd606fe --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/debug/browser/debugStatus.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. +{ + "debug": "Déboguer" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/debug/browser/debugViewlet.i18n.json b/i18n/fra/src/vs/workbench/parts/debug/browser/debugViewlet.i18n.json new file mode 100644 index 00000000000..9782a892c46 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/debug/browser/debugViewlet.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "debugFocusVariablesView": "Focus sur les Variables", + "debugFocusWatchView": "Focus sur Watch", + "debugFocusCallStackView": "Focus sur CallStack", + "debugFocusBreakpointsView": "Focus sur les points d'arrêts" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json b/i18n/fra/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json index 8a1e2b50d5f..5a3987eaa76 100644 --- a/i18n/fra/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json +++ b/i18n/fra/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json @@ -15,7 +15,6 @@ "vscode.extension.contributes.debuggers.initialConfigurations": "Configurations pour la génération du fichier 'launch.json' initial.", "vscode.extension.contributes.debuggers.languages": "Liste de langages pour lesquels l'extension de débogage peut être considérée comme \"débogueur par défaut\".", "vscode.extension.contributes.debuggers.adapterExecutableCommand": "Si l'extension VS Code spécifiée appelle cette commande pour déterminer le chemin de l'exécutable de l'adaptateur de débogage et les arguments à passer.", - "vscode.extension.contributes.debuggers.startSessionCommand": "Si l'extension VS Code spécifiée appelle cette commande pour les actions \"debug\" ou \"run\" ciblées pour cette extension.", "vscode.extension.contributes.debuggers.configurationSnippets": "Extraits pour l'ajout de nouvelles configurations à 'launch.json'.", "vscode.extension.contributes.debuggers.configurationAttributes": "Configurations de schéma JSON pour la validation de 'launch.json'.", "vscode.extension.contributes.debuggers.windows": "Paramètres spécifiques à Windows.", 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 d8dbd301b65..a5a92077a68 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,10 +12,10 @@ "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 configuration de débogage choisie a une valeur d’attribut non prise en charge `{0}` : `{1}`.", - "debugRequesMissing": "L’attribut `{0}` est introuvable dans la configuration de débogage choisie.", + "debugRequestNotSupported": "L’attribut '{0}' a une valeur '{1}' non prise en charge dans la configuration de débogage sélectionnée.", + "debugRequesMissing": "L’attribut '{0}' est introuvable dans la configuration de débogage sélectionnée.", "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.", + "debugTypeMissing": "La propriété 'type' est manquante pour la configuration de lancement sélectionnée.", "preLaunchTaskErrors": "Des erreurs de build ont été détectées durant le preLaunchTask '{0}'.", "preLaunchTaskError": "Une erreur de build a été détectée durant le preLaunchTask '{0}'.", "preLaunchTaskExitCode": "Le preLaunchTask '{0}' s'est terminé avec le code de sortie {1}.", diff --git a/i18n/fra/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json b/i18n/fra/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json index 9fa5e84fe4a..93c9076aca7 100644 --- a/i18n/fra/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json +++ b/i18n/fra/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json @@ -8,5 +8,5 @@ "replVariableAriaLabel": "La variable {0} a la valeur {1}, boucle REPL (Read Eval Print Loop), débogage", "replExpressionAriaLabel": "L'expression {0} a la valeur {1}, boucle REPL (Read Eval Print Loop), débogage", "replValueOutputAriaLabel": "{0}, boucle REPL (Read Eval Print Loop), débogage", - "replKeyValueOutputAriaLabel": "La variable de sortie {0} a la valeur {1}, boucle REPL (Read Eval Print Loop), débogage" + "replRawObjectAriaLabel": "La variable Repl {0} a la valeur {1}, read eval print loop, débogage" } \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json b/i18n/fra/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json index 2a8a3d2e72c..c0057e5215c 100644 --- a/i18n/fra/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json +++ b/i18n/fra/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "filesCategory": "Fichiers", + "filesCategory": "Fichier", "revealInSideBar": "Afficher dans la barre latérale", "acceptLocalChanges": "Utiliser vos modifications et écraser les contenus du disque", "revertLocalChanges": "Ignorer les modifications et revenir au contenu sur le disque" diff --git a/i18n/fra/src/vs/workbench/parts/files/browser/fileActions.i18n.json b/i18n/fra/src/vs/workbench/parts/files/browser/fileActions.i18n.json index 78b61033acc..05c3150dcf4 100644 --- a/i18n/fra/src/vs/workbench/parts/files/browser/fileActions.i18n.json +++ b/i18n/fra/src/vs/workbench/parts/files/browser/fileActions.i18n.json @@ -23,6 +23,7 @@ "confirmMoveTrashMessageFile": "Voulez-vous vraiment supprimer '{0}' ?", "undoBin": "Vous pouvez effectuer une restauration à partir de la Corbeille.", "undoTrash": "Vous pouvez effectuer une restauration à partir de la Poubelle.", + "doNotAskAgain": "Ne plus me demander", "confirmDeleteMessageFolder": "Voulez-vous vraiment supprimer définitivement '{0}' et son contenu ?", "confirmDeleteMessageFile": "Voulez-vous vraiment supprimer définitivement '{0}' ?", "irreversible": "Cette action est irréversible !", @@ -37,8 +38,6 @@ "openToSide": "Ouvrir sur le côté", "compareSource": "Sélectionner pour comparer", "globalCompareFile": "Comparer le fichier actif à...", - "pickHistory": "Sélectionnez un fichier ouvert à comparer", - "unableToFileToCompare": "Impossible de comparer le fichier sélectionné à '{0}'.", "openFileToCompare": "Ouvrez d'abord un fichier pour le comparer à un autre fichier.", "compareWith": "Comparer '{0}' à '{1}'", "compareFiles": "Comparer des fichiers", @@ -47,7 +46,7 @@ "saveAs": "Enregistrer sous...", "saveAll": "Enregistrer tout", "saveAllInGroup": "Enregistrer tout dans le groupe", - "saveFiles": "Enregistrer les fichiers à l'intégrité compromise", + "saveFiles": "Enregistrer tous les fichiers", "revert": "Rétablir le fichier", "focusOpenEditors": "Mettre le focus sur la vue des éditeurs ouverts", "focusFilesExplorer": "Focus sur l'Explorateur de fichiers", 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 066934ce4c8..9693ca31c50 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 @@ -40,10 +40,14 @@ "dynamicHeight": "Contrôle si la hauteur de la section des éditeurs ouverts doit s'adapter dynamiquement ou non au nombre d'éléments.", "autoReveal": "Contrôle si l'Explorateur doit automatiquement afficher et sélectionner les fichiers à l'ouverture.", "enableDragAndDrop": "Contrôle si l'explorateur doit autoriser le déplacement de fichiers et de dossiers par glisser-déplacer.", + "confirmDragAndDrop": "Contrôle si l’Explorateur doit demander confirmation lors du déplacement de fichiers ou de dossiers via glisser-déposer.", + "confirmDelete": "Contrôle si l’explorateur doit demander confirmation lorsque vous supprimez un fichier via la corbeille.", "sortOrder.default": "Les fichiers et dossiers sont triés par nom, dans l’ordre alphabétique. Les dossiers sont affichés avant les fichiers.", "sortOrder.mixed": "Les fichiers et dossiers sont triés par nom, dans l’ordre alphabétique. Les fichiers sont imbriqués dans les dossiers.", "sortOrder.filesFirst": "Les fichiers et dossiers sont triés par nom, dans l’ordre alphabétique. Les fichiers sont affichés avant les dossiers.", "sortOrder.type": "Les fichiers et dossiers sont triés par extension, dans l’ordre alphabétique. Les dossiers sont affichés avant les fichiers.", "sortOrder.modified": "Les fichiers et dossiers sont triés par date de dernière modification, dans l’ordre décroissant. Les dossiers sont affichés avant les fichiers.", - "sortOrder": "Contrôle l'ordre de tri des fichiers et dossiers dans l'explorateur. En plus du tri par défaut, vous pouvez définir l'ordre sur 'mixed' (fichiers et dossiers triés combinés), 'type' (par type de fichier), 'modified' (par date de dernière modification) ou 'fileFirst' (trier les fichiers avant les dossiers)." + "sortOrder": "Contrôle l'ordre de tri des fichiers et dossiers dans l'explorateur. En plus du tri par défaut, vous pouvez définir l'ordre sur 'mixed' (fichiers et dossiers triés combinés), 'type' (par type de fichier), 'modified' (par date de dernière modification) ou 'fileFirst' (trier les fichiers avant les dossiers).", + "explorer.decorations.colors": "Contrôle si les décorations de fichier doivent utiliser des couleurs.", + "explorer.decorations.badges": "Contrôle si les décorations de fichier doivent utiliser des badges." } \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json b/i18n/fra/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json index 8d920cf85ed..a44dd00649c 100644 --- a/i18n/fra/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json +++ b/i18n/fra/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json @@ -6,6 +6,8 @@ { "noWorkspace": "Aucun dossier ouvert", "explorerSection": "Section de l'Explorateur de fichiers", - "noWorkspaceHelp": "Vous n'avez pas encore ouvert de dossier.", + "noWorkspaceHelp": "Vous n’avez pas encore ajouté un dossier à l’espace de travail.", + "addFolder": "Ajouter un dossier", + "noFolderHelp": "Vous n'avez pas encore ouvert de dossier.", "openFolder": "Ouvrir le dossier" } \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json b/i18n/fra/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json index b4031885489..c30df4ecd5b 100644 --- a/i18n/fra/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json +++ b/i18n/fra/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json @@ -4,12 +4,15 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { + "canNotResolve": "Impossible de résoudre le dossier {0}", "fileInputAriaLabel": "Tapez le nom du fichier. Appuyez sur Entrée pour confirmer ou sur Échap pour annuler.", "filesExplorerViewerAriaLabel": "{0}, Explorateur de fichiers", "dropFolders": "Voulez-vous ajouter les dossiers à l’espace de travail ?", "dropFolder": "Voulez-vous ajouter le dossier à l’espace de travail ?", "addFolders": "&&Ajouter les dossiers", "addFolder": "&&Ajouter le dossier", + "confirmMove": "Êtes-vous certain de vouloir déplacer '{0}' ?", + "doNotAskAgain": "Ne plus me demander", "confirmOverwriteMessage": "{0}' existe déjà dans le dossier de destination. Voulez-vous le remplacer ?", "irreversible": "Cette action est irréversible !", "replaceButtonLabel": "&&Remplacer" diff --git a/i18n/fra/src/vs/workbench/parts/markers/browser/markersFileDecorations.i18n.json b/i18n/fra/src/vs/workbench/parts/markers/browser/markersFileDecorations.i18n.json new file mode 100644 index 00000000000..72ea24da7b8 --- /dev/null +++ b/i18n/fra/src/vs/workbench/parts/markers/browser/markersFileDecorations.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "label": "Problèmes", + "tooltip.1": "1 problème dans ce fichier", + "tooltip.N": "{0} problèmes dans ce fichier", + "markers.showOnFile": "Afficher les erreurs & les avertissements sur les fichiers et dossiers." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json b/i18n/fra/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json index 3684b913ba7..2c9bee3d6c2 100644 --- a/i18n/fra/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json +++ b/i18n/fra/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json @@ -5,7 +5,6 @@ // Do not edit this file. It is machine generated. { "emptyUserSettingsHeader": "Placer vos paramètres ici pour remplacer les paramètres par défaut.", - "errorInvalidConfiguration": "Impossible d'écrire les paramètres. Corrigez les erreurs/avertissements présents dans le fichier, puis réessayez.", "emptyWorkspaceSettingsHeader": "Placer vos paramètres ici pour remplacer les paramètres utilisateur.", "emptyFolderSettingsHeader": "Placer les paramètres de votre dossier ici pour remplacer ceux des paramètres de l’espace de travail.", "defaultFolderSettingsTitle": "Paramètres de dossier par défaut", diff --git a/i18n/fra/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json b/i18n/fra/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json index 1c20cdc1e83..9806bbb1795 100644 --- a/i18n/fra/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json +++ b/i18n/fra/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json @@ -13,7 +13,6 @@ "actionNotEnabled": "La commande '{0}' n'est pas activée dans le contexte actuel.", "recentlyUsed": "récemment utilisées", "morecCommands": "autres commandes", - "commandLabel": "{0} : {1}", "cat.title": "{0} : {1}", "noCommandsMatching": "Aucune commande correspondante" } \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json b/i18n/fra/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json index e9c5ba38792..9e292a57bbd 100644 --- a/i18n/fra/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json +++ b/i18n/fra/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json @@ -4,6 +4,10 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { + "changes": "{0} sur {1} modifications", + "change": "{0} sur {1} modification", + "show previous change": "Voir la modification précédente", + "show next change": "Voir la modification suivante", "editorGutterModifiedBackground": "Couleur d'arrière-plan de la reliure de l'éditeur pour les lignes modifiées.", "editorGutterAddedBackground": "Couleur d'arrière-plan de la reliure de l'éditeur pour les lignes ajoutées.", "editorGutterDeletedBackground": "Couleur d'arrière-plan de la reliure de l'éditeur pour les lignes supprimées.", diff --git a/i18n/fra/src/vs/workbench/parts/search/browser/search.contribution.i18n.json b/i18n/fra/src/vs/workbench/parts/search/browser/search.contribution.i18n.json index a2647c9191c..079ee64e791 100644 --- a/i18n/fra/src/vs/workbench/parts/search/browser/search.contribution.i18n.json +++ b/i18n/fra/src/vs/workbench/parts/search/browser/search.contribution.i18n.json @@ -17,7 +17,7 @@ "exclude": "Configurez les modèles Glob pour exclure les fichiers et les dossiers des recherches. Hérite de tous les modèles Glob à partir du paramètre files.exclude.", "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.", "exclude.when": "Vérification supplémentaire des frères d'un fichier correspondant. Utilisez $(basename) comme variable pour le nom de fichier correspondant.", - "useRipgrep": "Contrôle si ripgrep doit être utilisé dans la recherche de texte", + "useRipgrep": "Contrôle si ripgrep doit être utilisé dans la recherche de texte et de fichier", "useIgnoreFilesByDefault": "Indique s'il faut utiliser les fichiers .gitignore et .ignore par défaut en cas de recherche dans un nouvel espace de travail.", "search.quickOpen.includeSymbols": "Configurez l'ajout des résultats d'une recherche de symboles globale dans le fichier de résultats pour Quick Open." } \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/parts/search/browser/searchActions.i18n.json b/i18n/fra/src/vs/workbench/parts/search/browser/searchActions.i18n.json index 2478230b806..e43ec2a86c7 100644 --- a/i18n/fra/src/vs/workbench/parts/search/browser/searchActions.i18n.json +++ b/i18n/fra/src/vs/workbench/parts/search/browser/searchActions.i18n.json @@ -19,7 +19,6 @@ "ClearSearchResultsAction.label": "Effacer les résultats de la recherche", "FocusNextSearchResult.label": "Focus sur le résultat de la recherche suivant", "FocusPreviousSearchResult.label": "Focus sur le résultat de la recherche précédent", - "RemoveAction.label": "Supprimer", "file.replaceAll.label": "Tout remplacer", "match.replace.label": "Remplacer" } \ 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 b340098147f..344eb79e2cd 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,8 +10,8 @@ "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.", + "badFile": "Le fichier d’extrait \"{0}\" n’a pas pu être lu.", "source.snippet": "Extrait de code utilisateur", "detail.snippet": "{0} ({1})", "snippetSuggest.longLabel": "{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 0f3f74aef8d..abe24c7ec4d 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 @@ -14,6 +14,7 @@ "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", + "TaskServer.folderIgnored": "Le dossier {0} est ignoré car il utilise la version 0.1.0 de task", "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.", @@ -30,6 +31,7 @@ "TaskSystem.activeSame.noBackground": "La tâche '{0}' est déjà active. Pour la terminer, il utilise `Terminer la Tâche...` dans le menu Tâches.", "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}", + "TaskService.noConfiguration": "Erreur : La détection de la tâche {0} n’a pas contribué à une tâche pour la configuration suivante : {1}, la tâche sera ignorée.\n", "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": "Les configurations de tâche seront ignorées pour le dossier de l’espace de travail {0}. Le support de la tâche d'espace de travail multi-dossier 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", diff --git a/i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json b/i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json index 8a455dbd3c0..8e54d84198f 100644 --- a/i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json +++ b/i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json @@ -17,6 +17,7 @@ "terminal.integrated.fontFamily": "Contrôle la famille de polices du terminal. La valeur par défaut est la valeur associée à editor.fontFamily.", "terminal.integrated.fontSize": "Contrôle la taille de police en pixels du terminal.", "terminal.integrated.lineHeight": "Contrôle la hauteur de ligne du terminal. La multiplication de ce nombre par la taille de police du terminal permet d'obtenir la hauteur de ligne réelle en pixels.", + "terminal.integrated.enableBold": "Indique s'il faut activer le texte en gras dans le terminal, notez que cela nécessite la prise en charge du terminal Shell.", "terminal.integrated.cursorBlinking": "Contrôle si le curseur du terminal clignote.", "terminal.integrated.cursorStyle": "Contrôle le style du curseur du terminal.", "terminal.integrated.scrollback": "Contrôle la quantité maximale de lignes que le terminal conserve dans sa mémoire tampon.", 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 c06b522fc53..b1d65f3e2d3 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 @@ -16,7 +16,6 @@ "workbench.action.terminal.new.short": "Nouveau terminal", "workbench.action.terminal.focus": "Focus sur le terminal", "workbench.action.terminal.focusNext": "Focus sur le terminal suivant", - "workbench.action.terminal.focusAtIndex": "Focus sur le terminal {0}", "workbench.action.terminal.focusPrevious": "Focus sur le terminal précédent", "workbench.action.terminal.paste": "Coller dans le terminal actif", "workbench.action.terminal.DefaultShell": "Sélectionner l'interpréteur de commandes par défaut", diff --git a/i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json b/i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json index 7aeb29b6337..aa7d60b35db 100644 --- a/i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json +++ b/i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json @@ -5,7 +5,6 @@ // Do not edit this file. It is machine generated. { "copy": "Copier", - "createNewTerminal": "Nouveau terminal", "paste": "Coller", "selectAll": "Tout Sélectionner", "clear": "Effacer" diff --git a/i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json b/i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json index 4692e6dfcd1..d636f6886f8 100644 --- a/i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json +++ b/i18n/fra/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json @@ -10,6 +10,5 @@ "never again": "OK, ne plus afficher", "terminal.integrated.chooseWindowsShell": "Sélectionnez votre interpréteur de commandes de terminal favori. Vous pouvez le changer plus tard dans vos paramètres", "terminalService.terminalCloseConfirmationSingular": "Il existe une session de terminal active. Voulez-vous la tuer ?", - "terminalService.terminalCloseConfirmationPlural": "Il existe {0} sessions de terminal actives. Voulez-vous les tuer ?", - "yes": "Oui" + "terminalService.terminalCloseConfirmationPlural": "Il existe {0} sessions de terminal actives. Voulez-vous les tuer ?" } \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/services/configuration/common/configurationExtensionPoint.i18n.json b/i18n/fra/src/vs/workbench/services/configuration/common/configurationExtensionPoint.i18n.json new file mode 100644 index 00000000000..c93a0aa18c9 --- /dev/null +++ b/i18n/fra/src/vs/workbench/services/configuration/common/configurationExtensionPoint.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "vscode.extension.contributes.configuration.title": "Résumé des paramètres. Cette étiquette va être utilisée dans le fichier de paramètres en tant que commentaire de séparation.", + "vscode.extension.contributes.configuration.properties": "Description des propriétés de configuration.", + "scope.window.description": "Configuration spécifique de la fenêtre, qui peut être configurée dans les paramètres utilisateur ou de l'espace de travail.", + "scope.resource.description": "Configuration spécifique de la ressource, qui peut être configurée dans les paramètres utilisateur, de l'espace de travail ou du dossier.", + "scope.description": "Portée dans laquelle la configuration s’applique. Les portées disponibles sont `window` et `resource`.", + "vscode.extension.contributes.configuration": "Ajoute des paramètres de configuration.", + "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", + "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.", + "unknownWorkspaceProperty": "Propriété de configuration d’espace de travail inconnue" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/services/editor/common/editorService.i18n.json b/i18n/fra/src/vs/workbench/services/editor/common/editorService.i18n.json new file mode 100644 index 00000000000..50e968f8ee3 --- /dev/null +++ b/i18n/fra/src/vs/workbench/services/editor/common/editorService.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. +{ + "compareLabels": "{0} ↔ {1}" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json b/i18n/fra/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json index d9200a0b879..72ed7866243 100644 --- a/i18n/fra/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json +++ b/i18n/fra/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json @@ -6,6 +6,7 @@ { "schema.token.settings": "Couleurs et styles du jeton.", "schema.token.foreground": "Couleur de premier plan du jeton.", + "schema.token.background.warning": "Les couleurs d’arrière-plan des tokens ne sont actuellement pas pris en charge.", "schema.token.fontStyle": "Style de police de la règle : 'italique', 'gras' ou 'souligné', ou une combinaison de ces styles", "schema.fontStyle.error": "Le style de police doit être une combinaison de 'italic', 'bold' et 'underline'", "schema.properties.name": "Description de la règle.", diff --git a/i18n/fra/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json b/i18n/fra/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json index 47c337cef4b..d920cb0bcb8 100644 --- a/i18n/fra/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json +++ b/i18n/fra/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json @@ -33,5 +33,6 @@ "schema.fontSize": "Quand une police est utilisée : taille de police en pourcentage par rapport à la police du texte. En l'absence de définition, la taille de la définition de police est utilisée par défaut.", "schema.fontId": "Quand une police est employée : ID de la police. En l'absence de définition, la première définition de police est utilisée par défaut.", "schema.light": "Associations facultatives des icônes de fichiers dans les thèmes de couleur claire.", - "schema.highContrast": "Associations facultatives des icônes de fichiers dans les thèmes de couleur à contraste élevé." + "schema.highContrast": "Associations facultatives des icônes de fichiers dans les thèmes de couleur à contraste élevé.", + "schema.hidesExplorerArrows": "Détermine si les flèches de l’Explorateur de fichier doivent être masquées lorsque ce thème est actif." } \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/services/themes/electron-browser/colorThemeStore.i18n.json b/i18n/fra/src/vs/workbench/services/themes/electron-browser/colorThemeStore.i18n.json new file mode 100644 index 00000000000..63c65b5f2e0 --- /dev/null +++ b/i18n/fra/src/vs/workbench/services/themes/electron-browser/colorThemeStore.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "vscode.extension.contributes.themes": "Contributes textmate color themes.", + "vscode.extension.contributes.themes.id": "ID du thème d'icône utilisé dans les paramètres utilisateur.", + "vscode.extension.contributes.themes.label": "Étiquette du thème de couleur comme indiqué dans l'interface utilisateur (IU).", + "vscode.extension.contributes.themes.uiTheme": "Thème de base définissant les couleurs autour de l'éditeur : 'vs' est le thème de couleur clair, 'vs-dark' est le thème de couleur sombre. 'hc-black' est le thème sombre à contraste élevé.", + "vscode.extension.contributes.themes.path": "Chemin du fichier tmTheme. Le chemin est relatif au dossier d'extensions et correspond généralement à './themes/themeFile.tmTheme'.", + "reqarray": "Extension point `{0}` must be an array.", + "reqpath": "Chaîne attendue dans 'contributes.{0}.path'. Valeur fournie : {1}", + "invalid.path.1": "'contributes.{0}.path' ({1}) est censé être inclus dans le dossier ({2}) de l'extension. Cela risque de rendre l'extension non portable." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.i18n.json b/i18n/fra/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.i18n.json new file mode 100644 index 00000000000..b0f9faf6b52 --- /dev/null +++ b/i18n/fra/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.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. +{ + "error.cannotparseicontheme": "Problems parsing file icons file: {0}" +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.i18n.json b/i18n/fra/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.i18n.json new file mode 100644 index 00000000000..0c251843630 --- /dev/null +++ b/i18n/fra/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "vscode.extension.contributes.iconThemes": "Contributes file icon themes.", + "vscode.extension.contributes.iconThemes.id": "ID du thème d'icône utilisé dans les paramètres utilisateur.", + "vscode.extension.contributes.iconThemes.label": "Étiquette du thème d'icône indiqué dans l'IU (interface utilisateur).", + "vscode.extension.contributes.iconThemes.path": "Chemin du fichier de définitions de thèmes d'icônes. Le chemin est relatif au dossier d'extensions et correspond généralement à './icons/awesome-icon-theme.json'.", + "reqarray": "Extension point `{0}` must be an array.", + "reqpath": "Chaîne attendue dans 'contributes.{0}.path'. Valeur fournie : {1}", + "reqid": "Chaîne attendue dans 'contributes.{0}.id'. Valeur fournie : {1}", + "invalid.path.1": "'contributes.{0}.path' ({1}) est censé être inclus dans le dossier ({2}) de l'extension. Cela risque de rendre l'extension non portable." +} \ No newline at end of file diff --git a/i18n/fra/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json b/i18n/fra/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json index 8ca3299b2ba..234d5fb5a0e 100644 --- a/i18n/fra/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json +++ b/i18n/fra/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json @@ -4,31 +4,15 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "vscode.extension.contributes.themes": "Contributes textmate color themes.", - "vscode.extension.contributes.themes.id": "ID du thème d'icône utilisé dans les paramètres utilisateur.", - "vscode.extension.contributes.themes.label": "Étiquette du thème de couleur comme indiqué dans l'interface utilisateur (IU).", - "vscode.extension.contributes.themes.uiTheme": "Thème de base définissant les couleurs autour de l'éditeur : 'vs' est le thème de couleur clair, 'vs-dark' est le thème de couleur sombre. 'hc-black' est le thème sombre à contraste élevé.", - "vscode.extension.contributes.themes.path": "Chemin du fichier tmTheme. Le chemin est relatif au dossier d'extensions et correspond généralement à './themes/themeFile.tmTheme'.", - "vscode.extension.contributes.iconThemes": "Contributes file icon themes.", - "vscode.extension.contributes.iconThemes.id": "ID du thème d'icône utilisé dans les paramètres utilisateur.", - "vscode.extension.contributes.iconThemes.label": "Étiquette du thème d'icône indiqué dans l'IU (interface utilisateur).", - "vscode.extension.contributes.iconThemes.path": "Chemin du fichier de définitions de thèmes d'icônes. Le chemin est relatif au dossier d'extensions et correspond généralement à './icons/awesome-icon-theme.json'.", "migration.completed": "De nouveaux paramètres de thème ont été ajoutés aux paramètres utilisateur. Sauvegarde disponible sur {0}.", "error.cannotloadtheme": "Unable to load {0}: {1}", - "reqarray": "Extension point `{0}` must be an array.", - "reqpath": "Chaîne attendue dans 'contributes.{0}.path'. Valeur fournie : {1}", - "invalid.path.1": "'contributes.{0}.path' ({1}) est censé être inclus dans le dossier ({2}) de l'extension. Cela risque de rendre l'extension non portable.", - "reqid": "Chaîne attendue dans 'contributes.{0}.id'. Valeur fournie : {1}", "error.cannotloadicontheme": "Unable to load {0}", - "error.cannotparseicontheme": "Problems parsing file icons file: {0}", "colorTheme": "Specifies the color theme used in the workbench.", "colorThemeError": "Theme is unknown or not installed.", "iconTheme": "Spécifie le thème d'icône utilisé dans le banc d'essai ou 'null' pour n'afficher aucune icône de fichier.", "noIconThemeDesc": "No file icons", "iconThemeError": "File icon theme is unknown or not installed.", "workbenchColors": "Remplace les couleurs du thème de couleur sélectionné.", - "workbenchColors.deprecated": "Le paramètre n'est plus expérimental et a été renommé 'workbench.colorCustomizations'", - "workbenchColors.deprecatedDescription": "Utiliser 'workbench.colorCustomizations' à la place", "editorColors": "Remplace les couleurs et le style de la police de l’éditeur du thème par la couleur actuellement sélectionnée.", "editorColors.comments": "Définit les couleurs et les styles des commentaires", "editorColors.strings": "Définit les couleurs et les styles des littéraux de chaînes.", diff --git a/i18n/fra/src/vs/workbench/services/workspace/node/workspaceEditingService.i18n.json b/i18n/fra/src/vs/workbench/services/workspace/node/workspaceEditingService.i18n.json new file mode 100644 index 00000000000..e6f04379725 --- /dev/null +++ b/i18n/fra/src/vs/workbench/services/workspace/node/workspaceEditingService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "openWorkspaceConfigurationFile": "Ouvrir le Fichier de Configuration d’espace de travail", + "close": "Fermer" +} \ No newline at end of file diff --git a/i18n/hun/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json b/i18n/hun/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json index 73e2b227f47..b0baf1f7552 100644 --- a/i18n/hun/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json +++ b/i18n/hun/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json @@ -4,13 +4,13 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "activeEditorShort": "pl.: myFile.txt", - "activeEditorMedium": "pl.: myFolder/myFile.txt", - "activeEditorLong": "pl.: /Users/Development/myProject/myFolder/myFile.txt", - "rootName": "pl.: myFolder1, myFolder2, myFolder3", - "rootPath": "pl.: /Users/Development/myProject", - "folderName": "pl.: myFolder", - "folderPath": "pl.: /Users/Development/myProject", + "activeEditorShort": "a fájl neve (pl. myFile.txt)", + "activeEditorMedium": "a fájl relatív elérési útja a munkaterület mappájához képest (pl. myFolder/myFile.txt)", + "activeEditorLong": "a fájl teljes elérési útja (pl. /Users/Development/myProject/myFolder/myFile.txt)", + "rootName": "a munkaterület neve (pl. myFolder vagy myWorkspace)", + "rootPath": "a munkaterület elérési útja (pl. /Users/Development/myWorkspace)", + "folderName": "azon munkaterületi mappa a neve, amelyben a fájl található (pl. myFolder)", + "folderPath": "azon munkaterületi mappa elérési útja, amelyben a fájl található (pl. /Users/Development/myFolder)", "appName": "pl.: VS Code", "dirty": "módosításjelző, ami akkor jelenik meg, ha az aktív szerkesztőablak tartalma módosítva lett", "separator": "egy feltételes elválasztó (' - '), ami akkor jelenik meg, ha olyan változókkal van körülvéve, amelyeknek van értéke", diff --git a/i18n/hun/extensions/emmet/package.i18n.json b/i18n/hun/extensions/emmet/package.i18n.json index 576657ed646..e8dd71aabaa 100644 --- a/i18n/hun/extensions/emmet/package.i18n.json +++ b/i18n/hun/extensions/emmet/package.i18n.json @@ -28,13 +28,6 @@ "command.incrementNumberByTen": "Növelés 10-zel", "command.decrementNumberByTen": "Csökkentés 10-zel", "emmetSyntaxProfiles": "Konkrét szintaktika profiljának meghatározása vagy saját profil használata adott szabályokkal.", - "emmetExclude": "Azon nyelvek listája, ahol az Emmet-rövidítések ne legyenek kibontva.", - "emmetExtensionsPath": "Egy Emmet-profilokat és -kódtöredékeket tartalmazó mappa elérési útja.", - "emmetShowExpandedAbbreviation": "Kibontott emmet-rövidítések megjelenítése javaslatként. Az \"inMarkupAndStylesheetFilesOnly\" beállítás csak a html, haml, jade, slim, xml, xsl, css, scss, sass, less és stylus típusú tartalmat jelenti. Az \"always\" beállítás a fájl összes részére vonatkozik a jelölőnyelvtől/css-től függetlenül.", - "emmetShowAbbreviationSuggestions": "A lehetséges emmet-rövidítések megjelenítése javaslatként. Nem használható a stíluslapokon vagy ha az emmet.showExpandedAbbreviation értéke \"never\".", - "emmetIncludeLanguages": "Emmet-rövidítések engedélyezése olyan nyelvek esetében, amelyek alapértelmezés szerint nem támogatottak. Egy megfeleltetést kell felvenni a nyelv és egy emmet által támogatott nyelv között.\nPl.: {\"vue-html\": \"html\", \"javascript\": \"javascriptreact\"}", - "emmetVariables": "Az emmet-kódrészletekben használt változók", - "emmetTriggerExpansionOnTab": "Ha engedélyezve van, akkor az Emmet-rövidítések a Tab billentyű lenyomásával bonthatók ki.", "emmetPreferences": "Beállítások, melyek módosítják az Emmet műveleteinek és feloldó algoritmusainak viselkedését.", "emmetPreferencesIntUnit": "Az egész számok alapértelmezett mértékegysége", "emmetPreferencesFloatUnit": "A lebegőpontos számok alapértelmezett mértékegysége", @@ -44,5 +37,7 @@ "emmetPreferencesCssBetween": "A CSS-tulajdonság és az érték közé helyezett szimbólum CSS-rövidítések kibontásánál", "emmetPreferencesSassBetween": "A CSS-tulajdonság és az érték közé helyezett szimbólum CSS-rövidítések kibontásánál Sass-fájlokban", "emmetPreferencesStylusBetween": "A CSS-tulajdonság és az érték közé helyezett szimbólum CSS-rövidítések kibontásánál Stylus-fájlokban", - "emmetShowSuggestionsAsSnippets": "Ha az értéke igaz, akkor az emmetes javaslatok kódrészletekként jelennek meg, ami lehetővé teszi az editor.snippetSuggestions beállítás alapján történő rendezésüket." + "emmetPreferencesFilterCommentBefore": "Annak a megjegyzésnek a definíciója, ami az illeszkedő elem elé kerül a megjegyzésszűrő alkalmazása esetén.", + "emmetPreferencesFilterCommentAfter": "Annak a megjegyzésnek a definíciója, ami az illeszkedő elem mögé kerül a megjegyzésszűrő alkalmazása esetén.", + "emmetPreferencesFilterCommentTrigger": "Attribútumnevek vesszővel elválasztott listája, amelyeknek léteznie kell a megjegyzésszűrő alkalmazásához." } \ No newline at end of file diff --git a/i18n/hun/extensions/git/out/commands.i18n.json b/i18n/hun/extensions/git/out/commands.i18n.json index e3a35609258..ed82f751b7f 100644 --- a/i18n/hun/extensions/git/out/commands.i18n.json +++ b/i18n/hun/extensions/git/out/commands.i18n.json @@ -12,8 +12,9 @@ "cloning": "Git-forráskódtár klónozása...", "openrepo": "Forráskódtár megnyitása", "proposeopen": "Szeretné megnyitni a klónozott forráskódtárat?", - "path to init": "Mappa elérési útja", - "provide path": "Git-forráskódtár inicializálásához adjon meg egy elérési utat!", + "init repo": "Forráskódtár előkészítése", + "create repo": "Forráskódtár előkészítése", + "are you sure": "A művelet egy Git forráskódtárat hoz létre a következő helyen: '{0}. Biztosan szeretné folytatni?", "HEAD not available": "A(z) '{0}' HEAD-verziója nem elérhető.", "confirm stage files with merge conflicts": "Biztosan elő szeretne jegyezni {0} ütközési konfliktussal rendelkező fájlt?", "confirm stage file with merge conflicts": "Biztosan elő szeretne jegyezni a következő, ütközési konfliktussal rendelkező fájlt: {0}?", diff --git a/i18n/hun/extensions/git/out/repository.i18n.json b/i18n/hun/extensions/git/out/repository.i18n.json index 360a4435ac6..386f0a265df 100644 --- a/i18n/hun/extensions/git/out/repository.i18n.json +++ b/i18n/hun/extensions/git/out/repository.i18n.json @@ -21,6 +21,8 @@ "deleted by us": "Általunk törölt", "both added": "Mindkettő hozzáadta", "both modified": "Mindkettő módosította", + "untracked, short": "N", + "modified, short": "M", "commit": "Commit", "merge changes": "Módosítások összeolvasztása", "staged changes": "Beadásra előjegyzett módosítások", diff --git a/i18n/hun/extensions/git/package.i18n.json b/i18n/hun/extensions/git/package.i18n.json index 3aff31e4cd7..81661105b4d 100644 --- a/i18n/hun/extensions/git/package.i18n.json +++ b/i18n/hun/extensions/git/package.i18n.json @@ -15,6 +15,8 @@ "command.stageAll": "Összes módosítás előjegyzése beadásra", "command.stageSelectedRanges": "Kijelölt területek előjegyzése beadásra", "command.revertSelectedRanges": "Kijelölt területek visszaállítása", + "command.stageChange": "Változás előjegyzése beadásra", + "command.revertChange": "Változtatás visszavonása", "command.unstage": "Módosítások előjegyzésének törlése", "command.unstageAll": "Minden módosítás előjegyzésének törlése", "command.unstageSelectedRanges": "Kijelölt területek előjegyzésének törlése", diff --git a/i18n/hun/extensions/typescript/package.i18n.json b/i18n/hun/extensions/typescript/package.i18n.json index 031efbd3fe4..3b45505e009 100644 --- a/i18n/hun/extensions/typescript/package.i18n.json +++ b/i18n/hun/extensions/typescript/package.i18n.json @@ -44,7 +44,8 @@ "typescript.npm": "Az automatikus típusdefiníció-letöltéshez használt NPM végrehajtható fájl elérési útja. TypeScript 2.3.4-et igényel.", "typescript.check.npmIsInstalled": "Ellenőrizze, hogy az NPM telepítve van-e az automatikus típusdefiníció-letöltéshez.", "javascript.nameSuggestions": "Egyedi nevek listázásának engedélyezése a javascriptes javaslati listákban.", - "typescript.tsc.autoDetect": "Meghatározza, hogy a tsc-feladatok automatikus felderítése be van-e kapcsolva.", + "typescript.tsc.autoDetect": "Meghatározza a tsc-s feladatok automatikus felderítésének működését. 'off' érték esetén a funkció ki van kapcsolva. 'build' esetén egyszer lefutó, fordítást végző feladatok jönnek létre. 'watch' esetén csak fordítást és figyelést végző feladatok jönnek létre. 'on' esetén buildelési és figyelési feladatok is keletkeznek. Az alapértelmezett érték: 'on'.", "typescript.problemMatchers.tsc.label": "TypeScript-problémák", - "typescript.problemMatchers.tscWatch.label": "TypeScript-problémák (figyelő módban)" + "typescript.problemMatchers.tscWatch.label": "TypeScript-problémák (figyelő módban)", + "typescript.quickSuggestionsForPaths": "Kiegészítési javaslatok engedélyezése importált elérési utak beírásakor." } \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/contrib/find/browser/findWidget.i18n.json b/i18n/hun/src/vs/editor/contrib/find/browser/findWidget.i18n.json index 2e445d0ec2e..43d34e8aacc 100644 --- a/i18n/hun/src/vs/editor/contrib/find/browser/findWidget.i18n.json +++ b/i18n/hun/src/vs/editor/contrib/find/browser/findWidget.i18n.json @@ -15,7 +15,6 @@ "label.replaceButton": "Csere", "label.replaceAllButton": "Az összes előfordulás cseréje", "label.toggleReplaceButton": "Váltás csere módra", - "title.matchesCountLimit": "Csak az első 999 találat van kiemelve, de minden keresési művelet a teljes szöveggel dolgozik.", "label.matchesLocation": "{0} (összesen {1})", "label.noResults": "Nincs eredmény" } \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/contrib/find/common/findController.i18n.json b/i18n/hun/src/vs/editor/contrib/find/common/findController.i18n.json index b502771aa98..6e1f33bceda 100644 --- a/i18n/hun/src/vs/editor/contrib/find/common/findController.i18n.json +++ b/i18n/hun/src/vs/editor/contrib/find/common/findController.i18n.json @@ -10,12 +10,6 @@ "nextSelectionMatchFindAction": "Következő kijelölés", "previousSelectionMatchFindAction": "Előző kijelölés", "startReplace": "Csere", - "addSelectionToNextFindMatch": "Kijelölés hozzáadása a következő keresési találathoz", - "addSelectionToPreviousFindMatch": "Kijelölés hozzáadása az előző keresési találathoz", - "moveSelectionToNextFindMatch": "Utolsó kijelölés áthelyezése a következő keresési találatra", - "moveSelectionToPreviousFindMatch": "Utolsó kijelölés áthelyezése az előző keresési találatra", - "selectAllOccurrencesOfFindMatch": "Az összes keresési találat kijelölése", - "changeAll.label": "Minden előfordulás módosítása", "showNextFindTermAction": "Következő keresési kifejezés megjelenítése", "showPreviousFindTermAction": "Előző keresési kifejezés megjelenítése" } \ No newline at end of file diff --git a/i18n/hun/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json b/i18n/hun/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json index 8aa3dbca9ff..f50dc975843 100644 --- a/i18n/hun/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json +++ b/i18n/hun/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json @@ -6,5 +6,11 @@ { "mutlicursor.insertAbove": "Kurzor beszúrása egy sorral feljebb", "mutlicursor.insertBelow": "Kurzor beszúrása egy sorral lejjebb", - "mutlicursor.insertAtEndOfEachLineSelected": "Kurzor beszúrása a sorok végére" + "mutlicursor.insertAtEndOfEachLineSelected": "Kurzor beszúrása a sorok végére", + "addSelectionToNextFindMatch": "Kijelölés hozzáadása a következő keresési találathoz", + "addSelectionToPreviousFindMatch": "Kijelölés hozzáadása az előző keresési találathoz", + "moveSelectionToNextFindMatch": "Utolsó kijelölés áthelyezése a következő keresési találatra", + "moveSelectionToPreviousFindMatch": "Utolsó kijelölés áthelyezése az előző keresési találatra", + "selectAllOccurrencesOfFindMatch": "Az összes keresési találat kijelölése", + "changeAll.label": "Minden előfordulás módosítása" } \ No newline at end of file diff --git a/i18n/hun/src/vs/platform/theme/common/colorRegistry.i18n.json b/i18n/hun/src/vs/platform/theme/common/colorRegistry.i18n.json index 8cfb21c1837..1d88bef987d 100644 --- a/i18n/hun/src/vs/platform/theme/common/colorRegistry.i18n.json +++ b/i18n/hun/src/vs/platform/theme/common/colorRegistry.i18n.json @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "invalid.color": "Érvénytelen színformátum. Az #RGB, #RGBA, #RRGGBB vagy #RRGGBBAA formátum használható.", "schema.colors": "A munkaterületen használt színek.", "foreground": "Általános előtérszín. Csak akkor van használva, ha nem írja felül az adott komponens.", "errorForeground": "A hibaüzenetek általános előtérszíne. Csak akkor van használva, ha nem írja felül az adott komponens.", 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 64c1ec455b3..854d2bb1a23 100644 --- a/i18n/hun/src/vs/workbench/browser/actions/workspaceActions.i18n.json +++ b/i18n/hun/src/vs/workbench/browser/actions/workspaceActions.i18n.json @@ -9,14 +9,17 @@ "addFolderToWorkspace": "Mappa hozzáadása a munkaterülethez...", "add": "&&Hozzáadás", "addFolderToWorkspaceTitle": "Mappa hozzáadása a munkaterülethez", + "globalRemoveFolderFromWorkspace": "Mappa eltávolítása a munkaterületről...", "newWorkspace": "Új munkaterület...", "select": "&&Kiválasztás", "selectWorkspace": "Válassza ki a munkaterület mappáit!", "removeFolderFromWorkspace": "Mappa eltávolítása a munkaterületről", + "openFolderSettings": "Mappa beállításainak megnyitása", "saveWorkspaceAsAction": "Munkaterület mentése másként...", "save": "Menté&&s", "saveWorkspace": "Munkaterület mentése", "openWorkspaceAction": "Munkaterület megnyitása...", "openWorkspaceConfigFile": "Munkaterület konfigurációs fájljának megnyitása", + "openFolderAsWorkspaceInNewWindow": "Mappa megnyitása munkaterületként egy új ablakban", "workspaceFolderPickerPlaceholder": "Válasszon munkaterület-mappát!" } \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json b/i18n/hun/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json index 266ac56433e..3c81d54bd07 100644 --- a/i18n/hun/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json +++ b/i18n/hun/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json @@ -5,6 +5,5 @@ // Do not edit this file. It is machine generated. { "hideActivitBar": "Tevékenységsáv elrejtése", - "activityBarAriaLabel": "Az aktív nézet váltása", "globalActions": "Globális műveletek" } \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/browser/parts/compositebar/compositeBar.i18n.json b/i18n/hun/src/vs/workbench/browser/parts/compositebar/compositeBar.i18n.json new file mode 100644 index 00000000000..4fe5826df65 --- /dev/null +++ b/i18n/hun/src/vs/workbench/browser/parts/compositebar/compositeBar.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. +{ + "activityBarAriaLabel": "Az aktív nézet váltása" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/browser/parts/compositebar/compositeBarActions.i18n.json b/i18n/hun/src/vs/workbench/browser/parts/compositebar/compositeBarActions.i18n.json new file mode 100644 index 00000000000..af96f733ba7 --- /dev/null +++ b/i18n/hun/src/vs/workbench/browser/parts/compositebar/compositeBarActions.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "badgeTitle": "{0} – {1}", + "additionalViews": "További nézetek", + "numberBadge": "{0} ({1})", + "manageExtension": "Kiegészítő kezelése", + "titleKeybinding": "{0} ({1})", + "hide": "Elrejtés", + "toggle": "Nézet rögzítésének be- és kikapcsolása" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json b/i18n/hun/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json index 186c2e66b64..05a3be82161 100644 --- a/i18n/hun/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json +++ b/i18n/hun/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json @@ -12,5 +12,6 @@ "groupTwoPicker": "A második csoportban található szerkesztőablakok megjelenítése", "groupThreePicker": "A harmadik csoportban található szerkesztőablakok megjelenítése", "allEditorsPicker": "Összes megnyitott szerkesztőablak megjelenítése", - "view": "Nézet" + "view": "Nézet", + "file": "Fájl" } \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json b/i18n/hun/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json index 3cc19480b62..dfd63a785b3 100644 --- a/i18n/hun/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json +++ b/i18n/hun/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json @@ -4,6 +4,5 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "canNotRun": "A(z) '{0}' parancs jelenleg nem engedélyezett és nem futtatható.", "manageExtension": "Kiegészítő kezelése" } \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/electron-browser/main.contribution.i18n.json b/i18n/hun/src/vs/workbench/electron-browser/main.contribution.i18n.json index 730c270e5d5..53babca5f63 100644 --- a/i18n/hun/src/vs/workbench/electron-browser/main.contribution.i18n.json +++ b/i18n/hun/src/vs/workbench/electron-browser/main.contribution.i18n.json @@ -10,18 +10,15 @@ "workspaces": "Munkaterületek", "developer": "Fejlesztői", "showEditorTabs": "Meghatározza, hogy a megnyitott szerkesztőablakok telején megjelenjenek-e a fülek", - "workbench.editor.labelFormat.default": "Fájl nevének megjelenítése. Ha a fülek engedélyezve vannak, és két egyező nevű fájl van egy csoportban, az elérési útjuk eltérő része lesz hozzáfűzve a névhez. Ha a fülek le vannak tiltva, a fájl munkaterület gyökérkönyvtárához képest relatív elérési útja jelenik meg, ha a szerkesztőablak aktív.", "workbench.editor.labelFormat.short": "A fájl nevének megjelenítése a könyvtár nevével együtt.", - "workbench.editor.labelFormat.medium": "Fájl nevének megjelenítése a fájl munkaterület gyökérkönyvtárához képest relatív elérési útjával együtt.", "workbench.editor.labelFormat.long": "Fájl nevének megjelenítése a fájl abszolút elérési útjával együtt.", "tabDescription": "Meghatározza a szerkesztőablakok címkéje formáját. A beállítás módosítása könnyebbé teheti a fájl helyének kiderítését:\n- short: 'parent'\n- medium: 'workspace/src/parent'\n- long: '/home/user/workspace/src/parent'\n- default: '.../parent', ha egy másik fülnek ugyanaz a címe, vagy a relatív elérési út, ha a fülek le vannak tiltva", "editorTabCloseButton": "Meghatározza a szerkesztőablakok fülein található bezárógomb pozícióját vagy eltávolítja őket, ha a beállítás értéke 'off'.", "showIcons": "Meghatározza, hogy a megnyitott szerkesztőablakok ikonnal együtt jelenjenek-e meg. A működéshez szükséges egy ikontéma engedélyezése is.", "enablePreview": "Meghatározza, hogy a megnyitott szerkesztőablakok előnézetként jelenjenek-e meg. Az előnézetként használt szerkesztőablakok újra vannak hasznosítva, amíg meg nem tartja őket a felhasználó (pl. dupla kattintás vagy szerkesztés esetén), és dőlt betűvel jelenik meg a címsoruk.", "enablePreviewFromQuickOpen": "Meghatározza, hogy a gyors megnyitás során megnyitott szerkesztőablakok előnézetként jelenjenek-e meg. Az előnézetként használt szerkesztőablakok újra vannak hasznosítva, amíg meg nem tartja őket a felhasználó (pl. dupla kattintás vagy szerkesztés esetén).", - "editorOpenPositioning": "Meghatározza, hogy hol nyíljanak meg a szerkesztőablakok. A 'left' vagy 'right' használata esetén az aktív szerkesztőablaktól jobbra vagy balra nyílnak meg az újak. A 'first' vagy 'last' esetén a szerkesztőablakok a jelenleg aktív ablaktól függetlenül nyílnak meg.", "revealIfOpen": "Meghatározza, hogy egy szerkesztőablak fel legyen-e fedve, ha már meg van nyitva a látható csoportok bármelyiképben. Ha le van tiltva, akkor egy új szerkesztőablak nyílik az aktív szerkesztőablak-csoportban. Ha engedélyezve van, akkor a már megnyitott szerkesztőablak lesz felfedve egy új megnyitása helyett. Megjegyzés: vannak esetek, amikor ez a beállítás figyelmen kívül van hagyva, pl. ha egy adott szerkesztőablak egy konkrét csoportban vagy a jelenleg aktív csoport mellett van menyitva.", - "commandHistory": "Meghatározza, hogy mennyi legutóbb használt parancs jelenjen meg a parancskatalógus előzményeinek listájában. Az előzmények kikapcsolásához állítsa az értéket 0-ra.", + "commandHistory": "Meghatározza, hogy hány legutóbb használt parancs jelenjen meg a parancskatalógus előzményeinek listájában. Az előzmények kikapcsolásához állítsa az értéket nullára.", "preserveInput": "Meghatározza, hogy a legutóbb beírt parancs automatikusan helyre legyen-e állítva a parancskatalógus következő megnyitása során.", "closeOnFocusLost": "Meghatározza, hogy a gyors megnyitás automatikusan bezáródjon-e amint elveszíti a fókuszt.", "openDefaultSettings": "Meghatározza, hogy a beállítások megnyitásakor megnyíljon-e egy szerkesztő az összes alapértelmezett beállítással.", @@ -50,7 +47,7 @@ "restoreWindows": "Meghatározza, hogy újraindítás után hogyan vannak ismét megnyitva az ablakok. A 'none' választása esetén mindig üres munkaterület indul, 'one' esetén a legutóbb használt ablak nyílik meg újra, a 'folders' megnyitja az összes megnyitott mappát, míg az 'all' újranyitja az összes ablakot az előző munkamenetből.", "restoreFullscreen": "Meghatározza, hogy az ablak teljesképernyős módban nyíljon-e meg, ha kilépéskor teljes képernyős módban volt.", "zoomLevel": "Meghatározza az ablak nagyítási szintjét. Az eredei méret 0, és minden egyes plusz (pl. 1) vagy mínusz (pl. -1) 20%-kal nagyobb vagy kisebb nagyítási szintet jelent. Tizedestört megadása esetén a nagyítási szint finomabban állítható.", - "title": "Meghatározza az ablak címét az aktív szerkesztőablak alapján. A változók a környezet alapján vannak behelyettesítve:\n${activeEditorShort}: pl. myFile.txt\n${activeEditorMedium}: pl. myFolder/myFile.txt\n${activeEditorLong}: pl. /Users/Development/myProject/myFolder/myFile.txt\n${folderName}: pl. myFolder\n${folderPath}: pl. /Users/Development/myFolder\n${rootName}: pl. myFolder1, myFolder2, myFolder3\n${rootPath}: pl. /Users/Development/myWorkspace\n${appName}: pl. VS Code\n${dirty}: módosításjelző, ami jelzi, ha az aktív szerkesztőablak tartalma módosítva lett\n${separator}: feltételes elválasztó (\" - \"), ami akkor jelenik meg, ha olyan változókkal van körülvéve, amelyeknek van értéke\n", + "title": "Meghatározza az ablak címét az aktív szerkesztőablak alapján. A változók a környezet alapján vannak behelyettesítve:\n${activeEditorShort}: a fájl neve (pl. myFile.txt)\n${activeEditorMedium}: a fájl relatív elérési útja a munkaterület mappájához képest (pl. myFolder/myFile.txt)\n${activeEditorLong}: a fájl teljes elérési útja (pl. /Users/Development/myProject/myFolder/myFile.txt)\n${folderName}: azon munkaterületi mappa a neve, amelyben a fájl található (pl. myFolder)\n${folderPath}: azon munkaterületi mappa elérési útja, amelyben a fájl található (pl. /Users/Development/myFolder)\n${rootName}: a munkaterület neve (pl. myFolder vagy myWorkspace)\n${rootPath}: a munkaterület elérési útja (pl. /Users/Development/myWorkspace)\n${appName}: pl. VS Code\n${dirty}: módosításjelző, ami jelzi, ha az aktív szerkesztőablak tartalma módosítva lett\n${separator}: feltételes elválasztó (\" - \"), ami akkor jelenik meg, ha olyan változókkal van körülvéve, amelyeknek van értéke", "window.newWindowDimensions.default": "Az új ablakok a képernyő közepén nyílnak meg.", "window.newWindowDimensions.inherit": "Az új ablakok ugyanolyan méretben és ugyanazon a helyen jelennek meg, mint a legutoljára aktív ablak.", "window.newWindowDimensions.maximized": "Az új ablakok teljes méretben nyílnak meg.", diff --git a/i18n/hun/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json b/i18n/hun/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json index 4020242c40c..7c16965748a 100644 --- a/i18n/hun/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json +++ b/i18n/hun/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json @@ -6,6 +6,8 @@ { "entryAriaLabel": "{0}, hibakeresés", "debugAriaLabel": "Írja be a futtatandó konfiguráció nevét.", + "addConfigTo": "Konfiguráció hozzáadása ({0})...", + "addConfiguration": "Konfiguráció hozzáadása...", "noConfigurationsMatching": "Nincs illeszkedő hibakeresési konfiguráció", "noConfigurationsFound": "Nem található hibakeresési konfiguráció. Készítsen egy 'launch.json' fájlt." } \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/debug/browser/debugStatus.i18n.json b/i18n/hun/src/vs/workbench/parts/debug/browser/debugStatus.i18n.json new file mode 100644 index 00000000000..1bd18dd9a99 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/debug/browser/debugStatus.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. +{ + "debug": "Hibakeresés" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/debug/browser/debugViewlet.i18n.json b/i18n/hun/src/vs/workbench/parts/debug/browser/debugViewlet.i18n.json new file mode 100644 index 00000000000..72630bc8424 --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/debug/browser/debugViewlet.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "debugFocusVariablesView": "Váltás a változókra", + "debugFocusWatchView": "Váltás a figyelőlistára", + "debugFocusCallStackView": "Váltás a hívási veremre", + "debugFocusBreakpointsView": "Váltás a töréspontokra" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json b/i18n/hun/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json index fffc1aad27f..a54b3a8b776 100644 --- a/i18n/hun/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json +++ b/i18n/hun/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json @@ -15,7 +15,6 @@ "vscode.extension.contributes.debuggers.initialConfigurations": "Konfigurációk a 'launch.json' első változatának elkészítéséhez.", "vscode.extension.contributes.debuggers.languages": "Azon nyelvek listája, amelyeknél ez a hibakeresési kiegészítő alapértelmezett hibakeresőnek tekinthető.", "vscode.extension.contributes.debuggers.adapterExecutableCommand": "Ha meg van adva, a VS Code ezt a parancsot fogja hívni a hibakeresési illesztő futtatható állománya elérési útjának és az átadandó argumentumok meghatározásához.", - "vscode.extension.contributes.debuggers.startSessionCommand": "Ha meg van határozva, a VS Code ezt a parancsot fogja hívni az ennek a kiegészítőnek küldött \"debug\" vagy \"run\" parancsok esetén.", "vscode.extension.contributes.debuggers.configurationSnippets": "Kódtöredékek új 'launch.json'-konfigurációk hozzáadásához.", "vscode.extension.contributes.debuggers.configurationAttributes": "JSON-sémakonfigurációk a 'launch.json' validálásához.", "vscode.extension.contributes.debuggers.windows": "Windows-specifikus beállítások.", diff --git a/i18n/hun/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json b/i18n/hun/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json index 3945df51449..da4586921b6 100644 --- a/i18n/hun/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json +++ b/i18n/hun/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json @@ -12,10 +12,10 @@ "breakpointRemoved": "Töréspont eltávoíltva, {0}. sor, fájl: {1}", "compoundMustHaveConfigurations": "A kombinációk \"configurations\" tulajdonságát be kell állítani több konfiguráció elindításához.", "configMissing": "A(z) '{0}' konfiguráció hiányzik a 'launch.json'-ból.", - "debugRequestNotSupported": "A kiválasztott hibakeresési konfigurációban a(z) `{0}` attribútum értéke nem támogatott: '{1}'.", - "debugRequesMissing": "A(z) '{0}' attribútum hiányzik a kiválasztott hibakeresési konfigurációból.", + "debugRequestNotSupported": "A(z) `{0}` attribútumnak nem támogatott értéke van ('{1}') a kiválasztott hibakeresési konfigurációban.", + "debugRequesMissing": "A(z) `{0}` attribútum hiányzik a kiválasztott hibakeresési konfigurációból.", "debugTypeNotSupported": "A megadott hibakeresési típus ('{0}') nem támogatott.", - "debugTypeMissing": "A kiválasztott indítási konfigurációnak hiányzik a 'type' tulajdonsága.", + "debugTypeMissing": "A kiválasztott indítási konfigurációnak hiányzik a `type` tulajdonsága.", "preLaunchTaskErrors": "Buildelési hibák léptek fel a(z) '{0}' preLaunchTask futása közben.", "preLaunchTaskError": "Buildelési hiba lépett fel a(z) '{0}' preLaunchTask futása közben.", "preLaunchTaskExitCode": "A(z) '{0}' preLaunchTask a következő hibakóddal fejeződött be: {1}.", diff --git a/i18n/hun/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json b/i18n/hun/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json index 929096a6c43..9c979492ec3 100644 --- a/i18n/hun/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json +++ b/i18n/hun/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json @@ -8,5 +8,5 @@ "replVariableAriaLabel": "A(z) {0} változó értéke: {1}, REPL, hibakeresés", "replExpressionAriaLabel": "A(z) {0} kifejezés értéke: {1}, REPL, hibakeresés", "replValueOutputAriaLabel": "{0}, REPL, hibakeresés", - "replKeyValueOutputAriaLabel": "A(z) {0} kimeneti változó értéke: {1}, REPL, hibakeresés" + "replRawObjectAriaLabel": "{0} repl-változó értéke: {1}, REPL, hibakeresés" } \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json b/i18n/hun/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json index 0992f340c4a..d032a29e684 100644 --- a/i18n/hun/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json +++ b/i18n/hun/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "filesCategory": "Fájlok", + "filesCategory": "Fájl", "revealInSideBar": "Megjelenítés az oldalsávon", "acceptLocalChanges": "A lemezen lévő tartalom felülírása a saját módosításokkal", "revertLocalChanges": "Saját módosítások elvetése és a lemezen lévő tartalom visszaállítása" diff --git a/i18n/hun/src/vs/workbench/parts/files/browser/fileActions.i18n.json b/i18n/hun/src/vs/workbench/parts/files/browser/fileActions.i18n.json index b495b205cd5..2f76c2de05a 100644 --- a/i18n/hun/src/vs/workbench/parts/files/browser/fileActions.i18n.json +++ b/i18n/hun/src/vs/workbench/parts/files/browser/fileActions.i18n.json @@ -23,6 +23,7 @@ "confirmMoveTrashMessageFile": "Törli a(z) '{0}' nevű fájlt?", "undoBin": "Helyreállíthatja a lomtárból.", "undoTrash": "Helyreállíthatja a kukából.", + "doNotAskAgain": "Ne kérdezze meg újra", "confirmDeleteMessageFolder": "Törli a(z) {0} mappát és a teljes tartalmát?", "confirmDeleteMessageFile": "Véglegesen törli a következőt: {0}?", "irreversible": "A művelet nem vonható vissza!", @@ -37,8 +38,6 @@ "openToSide": "Megnyitás oldalt", "compareSource": "Kijelölés összehasonlításhoz", "globalCompareFile": "Aktív fájl összehasonlítása...", - "pickHistory": "Válasszon egy korábban megnyitott fájlt az összehasonlításhoz", - "unableToFileToCompare": "A kiválasztott fájl nem hasonlítható össze a következővel: '{0}'.", "openFileToCompare": "Fájlok összehasonlításához elősször nyisson meg egy fájlt.", "compareWith": "'{0}' összehasonlítása a következővel: '{1}'", "compareFiles": "Fájlok összehasonlítása", @@ -47,7 +46,7 @@ "saveAs": "Mentés másként...", "saveAll": "Összes mentése", "saveAllInGroup": "Összes mentése a csoportban", - "saveFiles": "Módosított fájlok mentése", + "saveFiles": "Összes fájl mentése", "revert": "Fájl visszaállítása", "focusOpenEditors": "Váltás a megnyitott szerkesztőablakok nézetre", "focusFilesExplorer": "Váltás a fájlkezelőre", diff --git a/i18n/hun/src/vs/workbench/parts/files/browser/files.contribution.i18n.json b/i18n/hun/src/vs/workbench/parts/files/browser/files.contribution.i18n.json index 4cea0462d06..a6c7fe4eaef 100644 --- a/i18n/hun/src/vs/workbench/parts/files/browser/files.contribution.i18n.json +++ b/i18n/hun/src/vs/workbench/parts/files/browser/files.contribution.i18n.json @@ -40,10 +40,14 @@ "dynamicHeight": "Meghatározza, hogy a megnyitott szerkesztőablakok szakasz magassága automatikusan illeszkedjen a megnyitott elemek számához vagy sem.", "autoReveal": "Meghatározza, hogy a fájlkezelőben automatikusan fel legyenek fedve és ki legyenek jelölve a fájlok, amikor megnyitják őket.", "enableDragAndDrop": "Meghatározza, hogy a fájlkezelőben áthelyezhetők-e a fájlok és mappák húzással.", + "confirmDragAndDrop": "Meghatározza, hogy a fájlkezelő kérjen-e megerősítést fájlok és mappák húzással történő áthelyezése esetén.", + "confirmDelete": "Meghatározza, hogy a fájlkezelő kérjen-e megerősítést a fájlok lomtárba történő helyezése esetén.", "sortOrder.default": "A fájlok és mappák név szerint vannak rendezve, ABC-sorrendben. A mappák a fájlok előtt vannak listázva.", "sortOrder.mixed": "A fájlok és mappák név szerint vannak rendezve, ABC-sorrendben. A fájlok és a mappák közösen vannak rendezve.", "sortOrder.filesFirst": "A fájlok és mappák név szerint vannak rendezve, ABC-sorrendben. A fájlok a mappák előtt vannak listázva.", "sortOrder.type": "A fájlok és mappák a kiterjesztésük szerint vannak rendezve, ABC-sorrendben. A mappák a fájlok előtt vannak listázva.", "sortOrder.modified": "A fájlok és mappák a legutolsó módosítás dátuma szerint vannak rendezve, csökkenő sorrendben. A mappák a fájlok előtt vannak listázva.", - "sortOrder": "Meghatározza a fájlok és mappák rendezési módját a fájlkezelőben. Az alapértelmezett rendezésen túl beállítható 'mixed' (a fájlok és mappák közösen vannak rendezve), 'type' (rendezés fájltípus szerint), 'modified' (rendezés utolsó módosítási dátum szerint) vagy 'filesFirst' (fájlok a mappák elé vannak rendezve) is." + "sortOrder": "Meghatározza a fájlok és mappák rendezési módját a fájlkezelőben. Az alapértelmezett rendezésen túl beállítható 'mixed' (a fájlok és mappák közösen vannak rendezve), 'type' (rendezés fájltípus szerint), 'modified' (rendezés utolsó módosítási dátum szerint) vagy 'filesFirst' (fájlok a mappák elé vannak rendezve) is.", + "explorer.decorations.colors": "Meghatározza, hogy a fájldekorációk használjanak-e színeket.", + "explorer.decorations.badges": "Meghatározza, hogy a fájldekorációk használjanak-e jelvényeket." } \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json b/i18n/hun/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json index 924c7ac5593..30d6d309142 100644 --- a/i18n/hun/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json +++ b/i18n/hun/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json @@ -6,6 +6,8 @@ { "noWorkspace": "Nincs mappa megnyitva", "explorerSection": "Fájlkezelő szakasz", - "noWorkspaceHelp": "Még nem nyitott meg mappát", + "noWorkspaceHelp": "Még nem adott mappát a munkaterülethez.", + "addFolder": "Mappa hozzáadása", + "noFolderHelp": "Még nem nyitott meg mappát", "openFolder": "Mappa megnyitása" } \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json b/i18n/hun/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json index 21ef2846403..07cd01d7b7a 100644 --- a/i18n/hun/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json +++ b/i18n/hun/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json @@ -4,12 +4,15 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { + "canNotResolve": "Nem lehet feloldani a következő mappát: {0}", "fileInputAriaLabel": "Adja meg a fájl nevét. Nyomjon 'Enter'-t a megerősítéshez vagy 'Escape'-et a megszakításhoz.", "filesExplorerViewerAriaLabel": "{0}, Fájlkezelő", "dropFolders": "Szeretné hozzáadni a mappákat a munkaterülethez?", "dropFolder": "Szeretné hozzáadni a mappát a munkaterülethez?", "addFolders": "Mappák hozzá&&adása", "addFolder": "Mappa hozzá&&adása", + "confirmMove": "Biztosan át szeretné helyezni a következőt: '{0}'?", + "doNotAskAgain": "Ne kérdezze meg újra", "confirmOverwriteMessage": "A célmappában már létezik '{0}' nevű elem. Le szeretné cserélni?", "irreversible": "A művelet nem vonható vissza!", "replaceButtonLabel": "&&Csere" diff --git a/i18n/hun/src/vs/workbench/parts/markers/browser/markersFileDecorations.i18n.json b/i18n/hun/src/vs/workbench/parts/markers/browser/markersFileDecorations.i18n.json new file mode 100644 index 00000000000..c9127f9169b --- /dev/null +++ b/i18n/hun/src/vs/workbench/parts/markers/browser/markersFileDecorations.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "label": "Problémák", + "tooltip.1": "A fájlban 1 probléma található", + "tooltip.N": "A fájlban {0} probléma található", + "markers.showOnFile": "Fájlokban és mappákban található hibák és figyelmeztetések megjelenítése" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json b/i18n/hun/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json index ee0b9750f56..69b1a883b74 100644 --- a/i18n/hun/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json +++ b/i18n/hun/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json @@ -5,7 +5,6 @@ // Do not edit this file. It is machine generated. { "emptyUserSettingsHeader": "Az ebben a fájlban elhelyezett beállítások felülírják az alapértelmezett beállításokat.", - "errorInvalidConfiguration": "Nem sikerült írni a beállításokba. Javítsa a fájlban található hibákat/figyelmeztetéseket, majd próbálja újra!", "emptyWorkspaceSettingsHeader": "Az ebben a fájlban elhelyezett beállítások felülírják a felhasználói beállításokat.", "emptyFolderSettingsHeader": "Az ebben a fájlban elhelyezett beállítások felülírják a munkaterületre vonatkozó beállításokat.", "defaultFolderSettingsTitle": "Alapértelmezett mappabeállítások", diff --git a/i18n/hun/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json b/i18n/hun/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json index 41ec82c33f7..949b0eb4dcf 100644 --- a/i18n/hun/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json +++ b/i18n/hun/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json @@ -13,7 +13,6 @@ "actionNotEnabled": "Ebben a kontextusban nem engedélyezett a(z) {0} parancs futtatása.", "recentlyUsed": "legutóbb használt", "morecCommands": "további parancsok", - "commandLabel": "{0}: {1}", "cat.title": "{0}: {1}", "noCommandsMatching": "Parancs nem található" } \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json b/i18n/hun/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json index 8e8c7a2962e..165c4dd23c2 100644 --- a/i18n/hun/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json +++ b/i18n/hun/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json @@ -4,6 +4,10 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { + "changes": "{0}. módosítás (összesen: {1})", + "change": "{0}. módosítás (összesen: {1})", + "show previous change": "Előző módosítás megjelenítése", + "show next change": "Következő módosítás megjelenítése", "editorGutterModifiedBackground": "A szerkesztőablak margójának háttérszíne a módosított soroknál.", "editorGutterAddedBackground": "A szerkesztőablak margójának háttérszíne a hozzáadott soroknál.", "editorGutterDeletedBackground": "A szerkesztőablak margójának háttérszíne a törölt soroknál.", diff --git a/i18n/hun/src/vs/workbench/parts/search/browser/search.contribution.i18n.json b/i18n/hun/src/vs/workbench/parts/search/browser/search.contribution.i18n.json index ed407924b94..9a084ee3454 100644 --- a/i18n/hun/src/vs/workbench/parts/search/browser/search.contribution.i18n.json +++ b/i18n/hun/src/vs/workbench/parts/search/browser/search.contribution.i18n.json @@ -17,7 +17,7 @@ "exclude": "Globális minták konfigurálása fájlok és mappák keresésből való kizárásához. Örökli az összes globális mintát a fliex.exclude beállításból.", "exclude.boolean": "A globális minta, amire illesztve lesznek a fájlok elérési útjai. A minta engedélyezéséhez vagy letiltásához állítsa igaz vagy hamis értékre.", "exclude.when": "További ellenőrzés elvégzése az illeszkedő fájlok testvérein. Az illeszkedő fájl nevéhez használja a $(basename) változót!", - "useRipgrep": "Meghatározza, hogy a szövegben való kereséshez a ripgrep van-e használva.", + "useRipgrep": "Meghatározza, hogy a szövegben és fájlokban való kereséshez a ripgrep van-e használva.", "useIgnoreFilesByDefault": "Meghatározza, hogy a .gitignore és .ignore fájlok használva vannak-e az új munkaterületeken a kereséshez.", "search.quickOpen.includeSymbols": "Meghatározza, hogy a fájlok gyors megnyitásánál megjelenjenek-e a globális szimbólumkereső találatai." } \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/search/browser/searchActions.i18n.json b/i18n/hun/src/vs/workbench/parts/search/browser/searchActions.i18n.json index 81ca25b06ea..3fe3f8642b4 100644 --- a/i18n/hun/src/vs/workbench/parts/search/browser/searchActions.i18n.json +++ b/i18n/hun/src/vs/workbench/parts/search/browser/searchActions.i18n.json @@ -19,7 +19,6 @@ "ClearSearchResultsAction.label": "Keresési eredmények törlése", "FocusNextSearchResult.label": "Váltás a következő keresési eredményre", "FocusPreviousSearchResult.label": "Váltás az előző keresési eredményre", - "RemoveAction.label": "Eltávolítás", "file.replaceAll.label": "Összes cseréje", "match.replace.label": "Csere" } \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json b/i18n/hun/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json index 94cfdc9e1d9..94208b9eca9 100644 --- a/i18n/hun/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json +++ b/i18n/hun/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json @@ -10,8 +10,8 @@ "vscode.extension.contributes.snippets": "Kódrészleteket szolgáltat.", "vscode.extension.contributes.snippets-language": "Azon nyelv azonosítója, amely számára szolgáltatva van ez a kódrészlet.", "vscode.extension.contributes.snippets-path": "A kódrészlet-fájl elérési útja. Az elérési út relatív a kiegészítő mappájához, és általában a következővel kezdődik: './snippets/',", - "badFile": "A(z) \"{0}\" kódrészletet tartalmazó fájlt nem sikerült beolvasni.", "badVariableUse": "A(z) \"{0}\" kódrészlet nagy valószínűséggel keveri a kódrészletváltozók és a kódrészlet-helyjelölők fogalmát. További információ a következő oldalon található: https://code.visualstudio.com/docs/editor/userdefinedsnippets#_snippet-syntax", + "badFile": "A(z) \"{0}\" kódrészletet tartalmazó fájlt nem sikerült beolvasni.", "source.snippet": "Felhasználói kódrészlet", "detail.snippet": "{0} ({1})", "snippetSuggest.longLabel": "{0}, {1}" 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 26736788a2f..ad07b544ef3 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 @@ -14,6 +14,7 @@ "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.", + "TaskServer.folderIgnored": "A(z) {0} mappa figyelmen kívül van hagyva, mert 0.1.0-s verziójú feladatkonfigurációt használ.", "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!", @@ -30,6 +31,7 @@ "TaskSystem.activeSame.noBackground": "A(z) '{0}' feladat már aktív. A feladat befejezéséhez használja `Feladat megszakítása` parancsot a Feladatok menüből!", "TaskSystem.active": "Már fut egy feladat. Szakítsa meg, mielőtt egy másik feladatot futtatna.", "TaskSystem.restartFailed": "Nem sikerült a(z) {0} feladat befejezése és újraindítása.", + "TaskService.noConfiguration": "Hiba: a(z) {0} feladatok felderítése nem szolgáltatott feladatot a következő konfigurációhoz:\n{1}\nA feladat figyelmen kívül lesz hagyva.\n", "TaskSystem.configurationErrors": "Hiba: a megadott feladatkonfigurációban validációs hibák vannak, és nem használható. Először javítsa ezeket a hibákat!", "taskService.ignoreingFolder": "Feladatkonfiguráció figyelmen kívül hagyva a munkaterület {0} nevű mappája esetében. Több mappás munkaterületen a feladatok támogatásához az összes mappának a 2.0-s verziójú feladatkonfigurációt kell használni.\n", "TaskSystem.invalidTaskJson": "Hiba. A tasks.json fájlban szintaktikai hibák találhatók. Javítsa ezeket a hibákat feladatvégrehajtás előtt.\n", diff --git a/i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json b/i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json index edc638fe092..9abe5fcaf6f 100644 --- a/i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json +++ b/i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json @@ -17,6 +17,7 @@ "terminal.integrated.fontFamily": "Meghatározza a terminál betűtípusát. Alapértelmezett értéke az editor.fontFamily értéke.", "terminal.integrated.fontSize": "Meghatározza a terminálban használt betű méretét, pixelekben.", "terminal.integrated.lineHeight": "Meghatározza a sormagasságot a terminálban. A tényleges méret a megadott szám és a terminál betűméretének szorzatából jön ki.", + "terminal.integrated.enableBold": "Engedélyezve van-e a félkövér szöveg a terminálban. A működéshez szükséges, hogy a terminál shell támogassa a félkövér betűket.", "terminal.integrated.cursorBlinking": "Meghatározza, hogy a terminál kurzora villog-e.", "terminal.integrated.cursorStyle": "Meghatározza a terminál kurzorának stílusát.", "terminal.integrated.scrollback": "Meghatározza, hogy a terminál legfeljebb hány sort tárol a pufferben.", diff --git a/i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json b/i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json index 9883a94a201..c671a91c989 100644 --- a/i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json +++ b/i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json @@ -16,7 +16,6 @@ "workbench.action.terminal.new.short": "Új terminál", "workbench.action.terminal.focus": "Váltás a terminálra", "workbench.action.terminal.focusNext": "Váltás a következő terminálra", - "workbench.action.terminal.focusAtIndex": "Váltás a(z) {0}. terminálra", "workbench.action.terminal.focusPrevious": "Váltás az előző terminálra", "workbench.action.terminal.paste": "Beillesztés az aktív terminálba", "workbench.action.terminal.DefaultShell": "Alapértelmezett shell kiválasztása", diff --git a/i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json b/i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json index 15f0f130a04..1f792eaa3ac 100644 --- a/i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json +++ b/i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json @@ -5,7 +5,6 @@ // Do not edit this file. It is machine generated. { "copy": "Másolás", - "createNewTerminal": "Új terminál", "paste": "Beillesztés", "selectAll": "Összes kijelölése", "clear": "Törlés" diff --git a/i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json b/i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json index babb2962c46..45f5c16180a 100644 --- a/i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json +++ b/i18n/hun/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json @@ -10,6 +10,5 @@ "never again": "Rendben, ne jelenítse meg újra", "terminal.integrated.chooseWindowsShell": "Válassza ki a preferált terminál shellt! Ez később módosítható a beállításokban.", "terminalService.terminalCloseConfirmationSingular": "Van egy aktív terminálmunkamenet. Szeretné megszakítani?", - "terminalService.terminalCloseConfirmationPlural": "{0} aktív terminálmunkamenet van. Szeretné megszakítani?", - "yes": "Igen" + "terminalService.terminalCloseConfirmationPlural": "{0} aktív terminálmunkamenet van. Szeretné megszakítani?" } \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/services/configuration/common/configurationExtensionPoint.i18n.json b/i18n/hun/src/vs/workbench/services/configuration/common/configurationExtensionPoint.i18n.json new file mode 100644 index 00000000000..209303272b7 --- /dev/null +++ b/i18n/hun/src/vs/workbench/services/configuration/common/configurationExtensionPoint.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "vscode.extension.contributes.configuration.title": "A beállítások összefoglaló leírása. Ez a címke jelenik meg a beállítások fájlban egy különálló megjegyzésként.", + "vscode.extension.contributes.configuration.properties": "A konfigurációs tulajdonságok leírása.", + "scope.window.description": "Ablakspecifikus beállítás, ami konfigurálható a felhasználói vagy munkaterületi beállításokban.", + "scope.resource.description": "Erőforrásspecifikus beállítás, ami beállítható a felhasználói, munkaterületi és mappaszintű beállításokban.", + "scope.description": "A hatókör, amire a beállítás vonatkozik. Az elérhető hatókörök: `window` és `resource`.", + "vscode.extension.contributes.configuration": "Konfigurációs beállításokat szolgáltat.", + "invalid.title": "a 'configuration.title' értékét karakterláncként kell megadni", + "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.", + "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", + "unknownWorkspaceProperty": "Ismeretlen munkaterület-konfigurációs tulajdonság" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/services/editor/common/editorService.i18n.json b/i18n/hun/src/vs/workbench/services/editor/common/editorService.i18n.json new file mode 100644 index 00000000000..50e968f8ee3 --- /dev/null +++ b/i18n/hun/src/vs/workbench/services/editor/common/editorService.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. +{ + "compareLabels": "{0} ↔ {1}" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json b/i18n/hun/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json index c20612f1980..c5c7acc39f4 100644 --- a/i18n/hun/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json +++ b/i18n/hun/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json @@ -6,6 +6,7 @@ { "schema.token.settings": "A token színe és stílusa.", "schema.token.foreground": "A token előtérszíne.", + "schema.token.background.warning": "A tokenek háttérszíne jelenleg nem támogatott.", "schema.token.fontStyle": "A szabály betűtípusának stílusa: 'italic', 'bold', 'underline', vagy ezek kombinációja", "schema.fontStyle.error": "A betűtípus stílusa 'italic', 'bold', 'underline', vagy ezek kombinációja lehet.", "schema.properties.name": "A szabály leírása.", diff --git a/i18n/hun/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json b/i18n/hun/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json index b93f26958fa..1a824863bdd 100644 --- a/i18n/hun/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json +++ b/i18n/hun/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json @@ -33,5 +33,6 @@ "schema.fontSize": "Betűkészlet használata esetén a betűkészlet mérete a szöveg betűkészletének méretéhez képest, százalékban. Ha nincs megadva, akkor a betűkészlet-definícióban megadott érték van használva.", "schema.fontId": "Betűkészlet használata esetén a betűkészlet azonosítója. Ha nincs megadva, akkor az első betűkészlet-definíció van használva.", "schema.light": "Fájlikon-társítások világos témák használata esetén. Nem kötelező megadni.", - "schema.highContrast": "Fájlikon-társítások nagy kontrasztú témák használata esetén. Nem kötelező megadni." + "schema.highContrast": "Fájlikon-társítások nagy kontrasztú témák használata esetén. Nem kötelező megadni.", + "schema.hidesExplorerArrows": "Meghatározza, hogy a fájlkezelőben megjelenő nyilak el legyenek-e rejtve, amikor ez a téma aktív." } \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/services/themes/electron-browser/colorThemeStore.i18n.json b/i18n/hun/src/vs/workbench/services/themes/electron-browser/colorThemeStore.i18n.json new file mode 100644 index 00000000000..4dace3f126c --- /dev/null +++ b/i18n/hun/src/vs/workbench/services/themes/electron-browser/colorThemeStore.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "vscode.extension.contributes.themes": "TextMate-színtémákat szolgáltat.", + "vscode.extension.contributes.themes.id": "Az ikontéma felhasználói beállításokban használt azonosítója.", + "vscode.extension.contributes.themes.label": "A színtéma felhasználói felületen megjelenő neve.", + "vscode.extension.contributes.themes.uiTheme": "A szerkesztőablak körül megjelenő elemek alaptémája. A 'vs' a világos, a 'vs-dark' a sötét színtéma, a 'hc-black' pedig a sötét, nagy kontrasztú téma.", + "vscode.extension.contributes.themes.path": "A tmTheme-fájl elérési útja. Az elérési út relatív a kiegészítő mappájához képest, és általában './themes/themeFile.tmTheme'.", + "reqarray": "a(z) `{0}` kiegszítési pontot tömbként kell megadni", + "reqpath": "Hiányzó karakterlánc a `contributes.{0}.path`-ban. A megadott érték: {1}", + "invalid.path.1": "A `contributes.{0}.path` ({1}) nem a kiegészítő mappáján belül található ({2}). Emiatt előfordulhat, hogy a kiegészítő nem lesz hordozható." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.i18n.json b/i18n/hun/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.i18n.json new file mode 100644 index 00000000000..91291d589e7 --- /dev/null +++ b/i18n/hun/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.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. +{ + "error.cannotparseicontheme": "Hiba a fájlikonokat leíró fájl feldolgozása közben: {0}" +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.i18n.json b/i18n/hun/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.i18n.json new file mode 100644 index 00000000000..bdbe180864e --- /dev/null +++ b/i18n/hun/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "vscode.extension.contributes.iconThemes": "Fájlikontémákat szolgáltat.", + "vscode.extension.contributes.iconThemes.id": "Az ikontéma felhasználói beállításokban használt azonosítója.", + "vscode.extension.contributes.iconThemes.label": "Az ikontéma felhasználói felületen megjelenő neve.", + "vscode.extension.contributes.iconThemes.path": "A témadefiníciós fájl elérési útja. Az elérési út relatív a kiegészítő mappájához képest, és általában ./icons/awesome-icon-theme.json'.", + "reqarray": "a(z) `{0}` kiegszítési pontot tömbként kell megadni", + "reqpath": "Hiányzó karakterlánc a `contributes.{0}.path`-ban. A megadott érték: {1}", + "reqid": "Hiányzó karakterlánc a `contributes.{0}.id`-ben. A megadott érték: {1}", + "invalid.path.1": "A `contributes.{0}.path` ({1}) nem a kiegészítő mappáján belül található ({2}). Emiatt előfordulhat, hogy a kiegészítő nem lesz hordozható." +} \ No newline at end of file diff --git a/i18n/hun/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json b/i18n/hun/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json index c1c24477811..f65a2da76bc 100644 --- a/i18n/hun/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json +++ b/i18n/hun/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json @@ -4,31 +4,15 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "vscode.extension.contributes.themes": "TextMate-színtémákat szolgáltat.", - "vscode.extension.contributes.themes.id": "A téma felhasználói beállításokban használt azonosítója.", - "vscode.extension.contributes.themes.label": "A színtéma felhasználói felületen megjelenő neve.", - "vscode.extension.contributes.themes.uiTheme": "A szerkesztőablak körül megjelenő elemek alaptémája. A 'vs' a világos, a 'vs-dark' a sötét színtéma, a 'hc-black' pedig a sötét, nagy kontrasztú téma.", - "vscode.extension.contributes.themes.path": "A tmTheme-fájl elérési útja. Az elérési út relatív a kiegészítő mappájához képest, és általában './themes/themeFile.tmTheme'.", - "vscode.extension.contributes.iconThemes": "Fájlikontémákat szolgáltat.", - "vscode.extension.contributes.iconThemes.id": "Az ikontéma felhasználói beállításokban használt azonosítója.", - "vscode.extension.contributes.iconThemes.label": "Az ikontéma felhasználói felületen megjelenő neve.", - "vscode.extension.contributes.iconThemes.path": "A témadefiníciós fájl elérési útja. Az elérési út relatív a kiegészítő mappájához képest, és általában ./icons/awesome-icon-theme.json'.", "migration.completed": "Új témabeállítások lettek hozzáadva a felhasználói beállításokhoz. Biztonsági mentés a következő helyen érhető el: {0}.", "error.cannotloadtheme": "Nem sikerült betölteni a(z) '{0}' témát: {1}.", - "reqarray": "a(z) `{0}` kiegszítési pontot tömbként kell megadni", - "reqpath": "Hiányzó karakterlánc a `contributes.{0}.path`-ban. A megadott érték: {1}", - "invalid.path.1": "A `contributes.{0}.path` ({1}) nem a kiegészítő mappáján belül található ({2}). Emiatt előfordulhat, hogy a kiegészítő nem lesz hordozható.", - "reqid": "Hiányzó karakterlánc a `contributes.{0}.id`-ben. A megadott érték: {1}", "error.cannotloadicontheme": "Nem sikerült megnyitni a(z) '{0}' témát", - "error.cannotparseicontheme": "Hiba a fájlikonokat leíró fájl feldolgozása közben: {0}", "colorTheme": "Meghatározza a munkaterületen használt színtémát.", "colorThemeError": "A téma ismeretlen vagy nincs telepítve.", "iconTheme": "Meghatározza a munkaterületen használt ikontémát. 'null' érték esetén nem jelenik meg egyetlen fájlikon sem.", "noIconThemeDesc": "Nincsenek fájlikonok", "iconThemeError": "A fájlikontéma ismeretlen vagy nincs telepítve.", "workbenchColors": "Felülírja az aktuális színtémában definiált színeket.", - "workbenchColors.deprecated": "A beállítás már nem kísérleti, és át lett nevezve 'workbench.colorCustomizations'-re.", - "workbenchColors.deprecatedDescription": "Használja a 'workbench.colorCustomizations' tulajdonságot helyette.", "editorColors": "Felülírja az aktuális színtémában definiált, szerkesztőablakhoz kapcsolódó színeket és betűstílusokat.", "editorColors.comments": "Meghatározza a megjegyzések színét és stílusát.", "editorColors.strings": "Meghatározza a sztringliterálok színét és stílusát.", diff --git a/i18n/hun/src/vs/workbench/services/workspace/node/workspaceEditingService.i18n.json b/i18n/hun/src/vs/workbench/services/workspace/node/workspaceEditingService.i18n.json new file mode 100644 index 00000000000..dbbe55dc9e3 --- /dev/null +++ b/i18n/hun/src/vs/workbench/services/workspace/node/workspaceEditingService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "openWorkspaceConfigurationFile": "Munkaterület konfigurációs fájljának megnyitása", + "close": "Bezárás" +} \ No newline at end of file diff --git a/i18n/ita/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json b/i18n/ita/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json index da8de5ed00e..f99b5799775 100644 --- a/i18n/ita/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json +++ b/i18n/ita/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json @@ -4,13 +4,13 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "activeEditorShort": "ad esempio myFile.txt", - "activeEditorMedium": "ad esempio myFolder/myFile.txt", - "activeEditorLong": "ad esempio /Users/Development/myProject/myFolder/myFile.txt", - "rootName": "ad es. myFolder1, myFolder2, myFolder3", - "rootPath": "ad esempio /Users/Development/myProject", - "folderName": "ad es. myFolder", - "folderPath": "ad es. /Users/Development/myFolder", + "activeEditorShort": "il nome del file (ad esempio MyFile.txt)", + "activeEditorMedium": "il percorso del file relativo alla cartella dell'area di lavoro (ad es. myFolder/myFile.txt)", + "activeEditorLong": "il percorso completo del file (ad es. /Users/Development/myProject/myFolder/myFile.txt)", + "rootName": "nome dell'area di lavoro (ad es. myFolder o myWorkspace)", + "rootPath": "percorso dell'area di lavoro (ad es. /Users/Development/myWorkspace)", + "folderName": "nome della cartella dell'area di lavoro in cui è contenuto il file (ad es. myFolder)", + "folderPath": "percorso della cartella dell'area di lavoro in cui è contenuto il file (ad es. /Users/Development/myFolder)", "appName": "ad esempio VS Code", "dirty": "un indicatore dirty se l'editor attivo è dirty", "separator": "un separatore condizionale (' - ') visualizzato solo se circondato da variabili con valori", diff --git a/i18n/ita/extensions/emmet/package.i18n.json b/i18n/ita/extensions/emmet/package.i18n.json index 6d5829bd5ff..f232e705ff6 100644 --- a/i18n/ita/extensions/emmet/package.i18n.json +++ b/i18n/ita/extensions/emmet/package.i18n.json @@ -28,13 +28,6 @@ "command.incrementNumberByTen": "Aumenta di 10", "command.decrementNumberByTen": "Riduci di 10", "emmetSyntaxProfiles": "Consente di definire il profilo per la sintassi specificata oppure di usare un profilo personalizzato con regole specifiche.", - "emmetExclude": "Matrice di linguaggi in cui le abbreviazioni Emmet non devono essere espanse.", - "emmetExtensionsPath": "Percorso di una cartella contenente snippet e profili Emmet.'", - "emmetShowExpandedAbbreviation": "Mostra le abbreviazioni emmet espanse come suggerimenti.\nL'opzione \"inMarkupAndStylesheetFilesOnly\" si applica a html, haml, jade, slim, xml, xsl, css, scss, sass, less e stylus.\nL'opzione \"sempre\" (always) si applica a tutte le parti del file indipendentemente dal markup/css.", - "emmetShowAbbreviationSuggestions": "Mostra possibili abbreviazioni emmet come suggerimenti. Non si applica a fogli di stile o quando emmet.showExpandedAbbreviation è impostata a \"mai\" (never).", - "emmetIncludeLanguages": "Abilita le abbreviazioni emmet in linguaggi che non sono normalmente supportati. Aggiungere un mapping tra il linguaggio ed il linguaggio supportato da emmet.\n Ad esempio: {\"vue-html\": \"html\", \"javascript\": \"javascriptreact\"}", - "emmetVariables": "Variabili da utilizzare nei frammenti di emmet", - "emmetTriggerExpansionOnTab": "Se abilitate, le abbreviazioni Emmet vengono espanse quando si preme TAB.", "emmetPreferences": "Preferenze usate per modificare il comportamento di alcune azioni e i resolver di Emmet.", "emmetPreferencesIntUnit": "Unità di misura predefinita per i valori integer", "emmetPreferencesFloatUnit": "Unità di misura predefinita per i valori float", @@ -44,5 +37,7 @@ "emmetPreferencesCssBetween": "Simbolo da inserire tra la proprietà CSS e il valore quando si espandono le abbreviazioni CSS", "emmetPreferencesSassBetween": "Simbolo da inserire tra la proprietà CSS e il valore quando si espandono le abbreviazioni CSS nei file Sass", "emmetPreferencesStylusBetween": "Simbolo da inserire tra la proprietà CSS e il valore quando si espandono le abbreviazioni CSS nei file Stylus", - "emmetShowSuggestionsAsSnippets": "Se è true, i suggerimenti Emmet verranno visualizzati come frammenti consentendo di ordinarli in base all'impostazione editor.snippetSuggestions." + "emmetPreferencesFilterCommentBefore": "Una definizione di commento che deve essere inserita prima dell'elemento corrispondente quando viene applicato il filtro commenti.", + "emmetPreferencesFilterCommentAfter": "Una definizione di commento che deve essere posizionato dopo l'elemento corrispondente quando viene applicato il filtro commenti.", + "emmetPreferencesFilterCommentTrigger": "Un elenco delimitato da virgole di nomi di attributi che dovrebbero esistere come abbreviazione per il filtro commenti da applicare" } \ No newline at end of file diff --git a/i18n/ita/extensions/git/out/commands.i18n.json b/i18n/ita/extensions/git/out/commands.i18n.json index 16a12505d76..09e5850f8cd 100644 --- a/i18n/ita/extensions/git/out/commands.i18n.json +++ b/i18n/ita/extensions/git/out/commands.i18n.json @@ -12,8 +12,9 @@ "cloning": "Clonazione del repository GIT...", "openrepo": "Apri repository", "proposeopen": "Aprire il repository clonato?", - "path to init": "Percorso della cartella", - "provide path": "Specificare un percorso di cartella per inizializzare un repository GIT", + "init repo": "Inizializza repository", + "create repo": "Inizializza repository", + "are you sure": "Questo creerà un repository Git in '{0}'. Sei sicuro di voler continuare?", "HEAD not available": "La versione HEAD di '{0}' non è disponibile.", "confirm stage files with merge conflicts": "Preparare per il commit {0} file con conflitti di merge?", "confirm stage file with merge conflicts": "Preparare per il commit {0} con conflitti di merge?", diff --git a/i18n/ita/extensions/git/out/repository.i18n.json b/i18n/ita/extensions/git/out/repository.i18n.json index 0b2210016a0..9088cc9c340 100644 --- a/i18n/ita/extensions/git/out/repository.i18n.json +++ b/i18n/ita/extensions/git/out/repository.i18n.json @@ -21,6 +21,8 @@ "deleted by us": "Eliminato da noi", "both added": "Entrambi aggiunti", "both modified": "Entrambi modificati", + "untracked, short": "U", + "modified, short": "M", "commit": "Esegui commit", "merge changes": "Esegui merge delle modifiche", "staged changes": "Modifiche preparate per il commit", diff --git a/i18n/ita/extensions/git/package.i18n.json b/i18n/ita/extensions/git/package.i18n.json index fd78ff2a50b..8844282381c 100644 --- a/i18n/ita/extensions/git/package.i18n.json +++ b/i18n/ita/extensions/git/package.i18n.json @@ -15,6 +15,8 @@ "command.stageAll": "Prepara tutte le modifiche per commit", "command.stageSelectedRanges": "Prepara per il commit intervalli selezionati", "command.revertSelectedRanges": "Ripristina intervalli selezionati", + "command.stageChange": "Prepara modifica per commit", + "command.revertChange": "Annulla modifica", "command.unstage": "Annulla preparazione modifiche per commit", "command.unstageAll": "Annulla preparazione di tutte le modifiche per commit", "command.unstageSelectedRanges": "Annulla preparazione per il commit di intervalli selezionati", diff --git a/i18n/ita/extensions/typescript/package.i18n.json b/i18n/ita/extensions/typescript/package.i18n.json index 21866260c71..e881e2dc9e0 100644 --- a/i18n/ita/extensions/typescript/package.i18n.json +++ b/i18n/ita/extensions/typescript/package.i18n.json @@ -44,7 +44,8 @@ "typescript.npm": "Specifica il percorso dell'eseguibile NPM utilizzato per l'acquisizione automatica delle definizioni di tipi. Richiede TypeScript >= 2.3.4.", "typescript.check.npmIsInstalled": "Controlla se NPM è installato per l'acquisizione automatica delle definizioni di tipi", "javascript.nameSuggestions": "Abilita/disabilita l'inclusione di nomi univoci dal file negli elenchi di suggerimento di JavaScript.", - "typescript.tsc.autoDetect": "Controlla se la rilevazione automatica di attività tsc è on/off.", + "typescript.tsc.autoDetect": "Controlla l'auto-rilevazione di attività di tsc. 'off' disabilita questa funzionalità. 'build' crea solo attività di singola compilazione esecuzione. 'watch' crea solo attività di compilazione e controllo. 'on' crea attività sia di tipo 'build' che 'watch'. Il valore predefinito è 'on'.", "typescript.problemMatchers.tsc.label": "Problemi TypeScript", - "typescript.problemMatchers.tscWatch.label": "Problemi TypeScript (modalità espressione di controllo)" + "typescript.problemMatchers.tscWatch.label": "Problemi TypeScript (modalità espressione di controllo)", + "typescript.quickSuggestionsForPaths": "Attiva/Disattiva suggerimenti rapidi quando si digita un percorso di importazione." } \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/contrib/find/browser/findWidget.i18n.json b/i18n/ita/src/vs/editor/contrib/find/browser/findWidget.i18n.json index 76cf5a81fb0..97a23bba223 100644 --- a/i18n/ita/src/vs/editor/contrib/find/browser/findWidget.i18n.json +++ b/i18n/ita/src/vs/editor/contrib/find/browser/findWidget.i18n.json @@ -15,7 +15,6 @@ "label.replaceButton": "Sostituisci", "label.replaceAllButton": "Sostituisci tutto", "label.toggleReplaceButton": "Attiva/Disattiva modalità sostituzione", - "title.matchesCountLimit": "Vengono evidenziati solo i primi 999 risultati, ma tutte le operazioni di ricerca funzionano sull'intero testo.", "label.matchesLocation": "{0} di {1}", "label.noResults": "Nessun risultato" } \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/contrib/find/common/findController.i18n.json b/i18n/ita/src/vs/editor/contrib/find/common/findController.i18n.json index 0b6e1248aab..753c08a072a 100644 --- a/i18n/ita/src/vs/editor/contrib/find/common/findController.i18n.json +++ b/i18n/ita/src/vs/editor/contrib/find/common/findController.i18n.json @@ -10,12 +10,6 @@ "nextSelectionMatchFindAction": "Trova selezione successiva", "previousSelectionMatchFindAction": "Trova selezione precedente", "startReplace": "Sostituisci", - "addSelectionToNextFindMatch": "Aggiungi selezione a risultato ricerca successivo", - "addSelectionToPreviousFindMatch": "Aggiungi selezione a risultato ricerca precedente", - "moveSelectionToNextFindMatch": "Sposta ultima selezione a risultato ricerca successivo", - "moveSelectionToPreviousFindMatch": "Sposta ultima selezione a risultato ricerca precedente", - "selectAllOccurrencesOfFindMatch": "Seleziona tutte le occorrenze del risultato ricerca", - "changeAll.label": "Cambia tutte le occorrenze", "showNextFindTermAction": "Mostra il termine di ricerca successivo", "showPreviousFindTermAction": "Mostra il termine di ricerca precedente" } \ No newline at end of file diff --git a/i18n/ita/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json b/i18n/ita/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json index 732a2e19079..af2b081d453 100644 --- a/i18n/ita/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json +++ b/i18n/ita/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json @@ -6,5 +6,11 @@ { "mutlicursor.insertAbove": "Aggiungi cursore sopra", "mutlicursor.insertBelow": "Aggiungi cursore sotto", - "mutlicursor.insertAtEndOfEachLineSelected": "Aggiungi cursore alla fine delle righe" + "mutlicursor.insertAtEndOfEachLineSelected": "Aggiungi cursore alla fine delle righe", + "addSelectionToNextFindMatch": "Aggiungi selezione a risultato ricerca successivo", + "addSelectionToPreviousFindMatch": "Aggiungi selezione a risultato ricerca precedente", + "moveSelectionToNextFindMatch": "Sposta ultima selezione a risultato ricerca successivo", + "moveSelectionToPreviousFindMatch": "Sposta ultima selezione a risultato ricerca precedente", + "selectAllOccurrencesOfFindMatch": "Seleziona tutte le occorrenze del risultato ricerca", + "changeAll.label": "Cambia tutte le occorrenze" } \ No newline at end of file diff --git a/i18n/ita/src/vs/platform/theme/common/colorRegistry.i18n.json b/i18n/ita/src/vs/platform/theme/common/colorRegistry.i18n.json index 8e09cda877c..1d0c0c88c61 100644 --- a/i18n/ita/src/vs/platform/theme/common/colorRegistry.i18n.json +++ b/i18n/ita/src/vs/platform/theme/common/colorRegistry.i18n.json @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "invalid.color": "Formato colore non valido. Usare #RGB, #RGBA, #RRGGBB o #RRGGBBAA", "schema.colors": "Colori usati nell'area di lavoro.", "foreground": "Colore primo piano. Questo colore è utilizzato solo se non viene sovrascritto da un componente.", "errorForeground": "Colore primo piano globale per i messaggi di errore. Questo colore è utilizzato solamente se non viene sottoposto a override da un componente.", 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 4d31bf1f0c4..91e26eba907 100644 --- a/i18n/ita/src/vs/workbench/browser/actions/workspaceActions.i18n.json +++ b/i18n/ita/src/vs/workbench/browser/actions/workspaceActions.i18n.json @@ -13,6 +13,7 @@ "select": "&&Seleziona", "selectWorkspace": "Seleziona cartelle per l'area di lavoro", "removeFolderFromWorkspace": "Rimuovi cartella dall'area di lavoro", + "openFolderSettings": "Apri impostazioni cartella", "saveWorkspaceAsAction": "Salva area di lavoro come...", "save": "&&Salva", "saveWorkspace": "Salva area di lavoro", diff --git a/i18n/ita/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json b/i18n/ita/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json index d8cbee32bd5..bee5fb6341a 100644 --- a/i18n/ita/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json +++ b/i18n/ita/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json @@ -5,6 +5,5 @@ // Do not edit this file. It is machine generated. { "hideActivitBar": "Nascondi barra attività", - "activityBarAriaLabel": "Cambio visualizzazione attiva", "globalActions": "Azioni globali" } \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/browser/parts/compositebar/compositeBar.i18n.json b/i18n/ita/src/vs/workbench/browser/parts/compositebar/compositeBar.i18n.json new file mode 100644 index 00000000000..33e0b7a1979 --- /dev/null +++ b/i18n/ita/src/vs/workbench/browser/parts/compositebar/compositeBar.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. +{ + "activityBarAriaLabel": "Cambio visualizzazione attiva" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/browser/parts/compositebar/compositeBarActions.i18n.json b/i18n/ita/src/vs/workbench/browser/parts/compositebar/compositeBarActions.i18n.json new file mode 100644 index 00000000000..427b8914624 --- /dev/null +++ b/i18n/ita/src/vs/workbench/browser/parts/compositebar/compositeBarActions.i18n.json @@ -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. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "badgeTitle": "{0} - {1}", + "additionalViews": "Visualizzazioni aggiuntive", + "numberBadge": "{0} ({1})", + "manageExtension": "Gestisci estensione", + "titleKeybinding": "{0} ({1})", + "toggle": "Attiva/Disattiva visualizzazione bloccata" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json b/i18n/ita/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json index 766b02a726a..aa914d12c1d 100644 --- a/i18n/ita/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json +++ b/i18n/ita/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json @@ -12,5 +12,6 @@ "groupTwoPicker": "Mostra editor nel secondo gruppo", "groupThreePicker": "Mostra editor nel terzo gruppo", "allEditorsPicker": "Mostra tutti gli editor aperti", - "view": "Visualizza" + "view": "Visualizza", + "file": "File" } \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json b/i18n/ita/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json index 9b0548995db..358f121a809 100644 --- a/i18n/ita/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json +++ b/i18n/ita/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json @@ -4,6 +4,5 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "canNotRun": "Il comando '{0}' non è attualmente abilitato e non può essere eseguito.", "manageExtension": "Gestisci estensione" } \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/electron-browser/main.contribution.i18n.json b/i18n/ita/src/vs/workbench/electron-browser/main.contribution.i18n.json index 52b4f223124..f17ae8d5fa8 100644 --- a/i18n/ita/src/vs/workbench/electron-browser/main.contribution.i18n.json +++ b/i18n/ita/src/vs/workbench/electron-browser/main.contribution.i18n.json @@ -10,18 +10,14 @@ "workspaces": "Aree di lavoro", "developer": "Sviluppatore", "showEditorTabs": "Controlla se visualizzare o meno gli editor aperti in schede.", - "workbench.editor.labelFormat.default": "Visualizza il nome del file. Quando le schede sono abilitate e due file presentano lo stesso nome in un unico gruppo, vengono aggiunte le sezioni distintive del percorso di ciascun file. Quando le schede sono disabilitate, viene visualizzato il percorso relativo alla radice dell'area di lavoro se l'editor è attivo.", "workbench.editor.labelFormat.short": "Visualizza il nome del file seguito dal relativo nome di directory.", - "workbench.editor.labelFormat.medium": "Visualizza il nome del file seguito dal percorso corrispondente relativo alla radice dell'area di lavoro.", "workbench.editor.labelFormat.long": "Visualizza il nome del file seguito dal relativo percorso assoluto.", "tabDescription": "Controlla il formato dell'etichetta per un editor. Se si modifica questa impostazione, ad esempio, risulterà più agevole individuare il percorso di un file:\n- short: 'parent'\n- medium: 'workspace/src/parent'\n- long: '/home/user/workspace/src/parent'\n- default: '.../parent', quando un'altra scheda condivide lo stesso titolo, oppure il percorso relativo dell'area di lavoro se le schede sono disabilitate", "editorTabCloseButton": "Controlla la posizione dei pulsanti di chiusura delle schede dell'editor oppure li disabilita quando è impostata su 'off'.", "showIcons": "Controlla se visualizzare o meno un'icona per gli editor aperti. Richiede l'abilitazione anche di un tema dell'icona.", "enablePreview": "Controlla se gli editor aperti vengono visualizzati come anteprima. Le anteprime editor vengono riutilizzate finché vengono mantenute (ad esempio tramite doppio clic o modifica) e vengono visualizzate in corsivo.", "enablePreviewFromQuickOpen": "Controlla se gli editor aperti da Quick Open vengono visualizzati come anteprima. Le anteprime editor vengono riutilizzate finché vengono mantenute, ad esempio tramite doppio clic o modifica.", - "editorOpenPositioning": "Controlla la posizione in cui vengono aperti gli editor. Selezionare 'left' o 'right' per aprire gli editor a sinistra o a destra di quello attualmente attivo. Selezionare 'first' o 'last' per aprire gli editor indipendentemente da quello attualmente attivo.", "revealIfOpen": "Controlla se un editor viene visualizzato in uno qualsiasi dei gruppi visibili se viene aperto. Se l'opzione è disabilitata, un editor verrà aperto preferibilmente nel gruppo di editor attualmente attivo. Se è abilitata, un editor già aperto verrà visualizzato e non aperto di nuovo nel gruppo di editor attualmente attivo. Nota: in alcuni casi questa impostazione viene ignorata, ad esempio quando si forza l'apertura di un editor in un gruppo specifico oppure a lato del gruppo attualmente attivo.", - "commandHistory": "Controlla il numero di comandi usati di recente da mantenere nella cronologia per il riquadro comandi. Impostare su 0 per disabilitare la cronologia dei comandi.", "preserveInput": "Controlla se l'ultimo input digitato nel riquadro comandi deve essere ripristinato alla successiva riapertura del riquadro.", "closeOnFocusLost": "Controlla se Quick Open deve essere chiuso automaticamente quando perde lo stato attivo.", "openDefaultSettings": "Controlla se all'apertura delle impostazioni viene aperto anche un editor che mostra tutte le impostazioni predefinite.", @@ -50,7 +46,6 @@ "restoreWindows": "Controlla la modalità di riapertura delle finestre dopo un riavvio. Selezionare 'none' per iniziare sempre con un'area di lavoro vuota, 'one' per riaprire l'ultima finestra usata, 'folders' per riaprire tutte le finestre con cartelle aperte oppure 'all' per riaprire tutte le finestre dell'ultima sessione.", "restoreFullscreen": "Controlla se una finestra deve essere ripristinata a schermo intero se è stata chiusa in questa modalità.", "zoomLevel": "Consente di modificare il livello di zoom della finestra. Il valore originale è 0 e ogni incremento superiore (ad esempio 1) o inferiore (ad esempio -1) rappresenta un aumento o una diminuzione del 20% della percentuale di zoom. È anche possibile immettere valori decimali per modificare il livello di zoom con maggiore granularità.", - "title": "Controlla il titolo della finestra in base all'editor attivo. Le variabili vengono sostituite in base al contesto:\n${activeEditorShort}: ad esempio myFile.txt\n${activeEditorMedium}: ad esempio myFolder/myFile.txt\n${activeEditorLong}: ad esempio /Users/Development/myProject/myFolder/myFile.txt\n${folderName}: ad esempio myFolder\n${folderPath}: ad esempio /Users/Development/myFolder\n${rootName}: ad esempio myFolder1, myFolder2, myFolder3\n${rootPath}: ad esempio /Users/Development/myWorkspace\n${appName}: ad esempio VS Code\n${dirty}: indicatore dirty se l'editor attivo è dirty\n${separator}: separatore condizionale (\" - \") visualizzato solo quando è racchiuso tra variabili con valori", "window.newWindowDimensions.default": "Apre nuove finestre al centro della schermata.", "window.newWindowDimensions.inherit": "Apre nuove finestre le cui dimensioni sono uguali a quelle dell'ultima finestra attiva.", "window.newWindowDimensions.maximized": "Apre nuove finestre ingrandite a schermo intero.", diff --git a/i18n/ita/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json b/i18n/ita/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json index b3b797b17a0..cd95d333b64 100644 --- a/i18n/ita/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json +++ b/i18n/ita/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json @@ -6,6 +6,8 @@ { "entryAriaLabel": "{0}, debug", "debugAriaLabel": "Digitare il nome di una configurazione di avvio da eseguire.", + "addConfigTo": "Aggiungi configurazione ({0})...", + "addConfiguration": "Aggiungi configurazione...", "noConfigurationsMatching": "Non esistono configurazioni di debug corrispondenti", "noConfigurationsFound": "Non è stata trovata alcuna configurazione di debug. Creare un file 'launch.json'." } \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/debug/browser/debugStatus.i18n.json b/i18n/ita/src/vs/workbench/parts/debug/browser/debugStatus.i18n.json new file mode 100644 index 00000000000..8eeb71caf1a --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/debug/browser/debugStatus.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. +{ + "debug": "Debug" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/debug/browser/debugViewlet.i18n.json b/i18n/ita/src/vs/workbench/parts/debug/browser/debugViewlet.i18n.json new file mode 100644 index 00000000000..8b6ad71cd4e --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/debug/browser/debugViewlet.i18n.json @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json b/i18n/ita/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json index 2b0ce6dfdf4..7729690837e 100644 --- a/i18n/ita/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json +++ b/i18n/ita/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json @@ -15,7 +15,6 @@ "vscode.extension.contributes.debuggers.initialConfigurations": "Configurazioni per generare la versione iniziale di 'launch.json'.", "vscode.extension.contributes.debuggers.languages": "Elenco dei linguaggi. per cui l'estensione di debug può essere considerata il \"debugger predefinito\".", "vscode.extension.contributes.debuggers.adapterExecutableCommand": "Se è specificato, Visual Studio Code chiamerà questo comando per determinare il percorso eseguibile della scheda di debug e gli argomenti da passare.", - "vscode.extension.contributes.debuggers.startSessionCommand": "Se è specificato, Visual Studio Code chiamerà questo comando per le azioni \"debug\" o \"run\" previste come destinazione di questa estensione.", "vscode.extension.contributes.debuggers.configurationSnippets": "Frammenti per l'aggiunta di nuove configurazioni in 'launch.json'.", "vscode.extension.contributes.debuggers.configurationAttributes": "Configurazioni dello schema JSON per la convalida di 'launch.json'.", "vscode.extension.contributes.debuggers.windows": "Impostazioni specifiche di Windows.", diff --git a/i18n/ita/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json b/i18n/ita/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json index 70ec9c0d711..c70e4e525c6 100644 --- a/i18n/ita/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json +++ b/i18n/ita/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json @@ -12,10 +12,7 @@ "breakpointRemoved": "Rimosso un punto di interruzione a riga {0} del file {1}", "compoundMustHaveConfigurations": "Per avviare più configurazioni, deve essere impostato l'attributo \"configurations\" dell'elemento compounds.", "configMissing": "In 'launch.json' manca la configurazione '{0}'.", - "debugRequestNotSupported": "La configurazione di debug scelta contiene un valore di attributo `{0}` che non è supportato: '{1}'.", - "debugRequesMissing": "Nella configurazione di debug scelta manca l'attributo '{0}'.", "debugTypeNotSupported": "Il tipo di debug configurato '{0}' non è supportato.", - "debugTypeMissing": "Manca la proprietà 'type' per la configurazione di avvio scelta.", "preLaunchTaskErrors": "Sono stati rilevati errori di compilazione durante preLaunchTask '{0}'.", "preLaunchTaskError": "È stato rilevato un errore di compilazione durante preLaunchTask '{0}'.", "preLaunchTaskExitCode": "L'attività di preavvio '{0}' è stata terminata ed è stato restituito il codice di uscita {1}.", diff --git a/i18n/ita/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json b/i18n/ita/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json index fff7cbcba9e..2740934029a 100644 --- a/i18n/ita/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json +++ b/i18n/ita/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json @@ -7,6 +7,5 @@ "stateCapture": "Lo stato dell'oggetto viene acquisito dalla prima valutazione", "replVariableAriaLabel": "Il valore della variabile {0} è {1}, ciclo Read Eval Print, debug", "replExpressionAriaLabel": "Il valore dell'espressione {0} è {1}, ciclo Read Eval Print, debug", - "replValueOutputAriaLabel": "{0}, ciclo Read Eval Print, debug", - "replKeyValueOutputAriaLabel": "Il valore della variabile di output {0} è {1}, ciclo Read Eval Print, debug" + "replValueOutputAriaLabel": "{0}, ciclo Read Eval Print, debug" } \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/files/browser/fileActions.i18n.json b/i18n/ita/src/vs/workbench/parts/files/browser/fileActions.i18n.json index f1f608e1731..d68d405085a 100644 --- a/i18n/ita/src/vs/workbench/parts/files/browser/fileActions.i18n.json +++ b/i18n/ita/src/vs/workbench/parts/files/browser/fileActions.i18n.json @@ -37,8 +37,6 @@ "openToSide": "Apri lateralmente", "compareSource": "Seleziona per il confronto", "globalCompareFile": "Confronta file attivo con...", - "pickHistory": "Selezionare un file aperto in precedenza per il confronto", - "unableToFileToCompare": "Non è possibile confrontare il file selezionato con '{0}'.", "openFileToCompare": "Aprire prima un file per confrontarlo con un altro file.", "compareWith": "Confronta '{0}' con '{1}'", "compareFiles": "Confronta file", @@ -47,7 +45,6 @@ "saveAs": "Salva con nome...", "saveAll": "Salva tutto", "saveAllInGroup": "Salva tutto nel gruppo", - "saveFiles": "Salva file modificati ma non salvati", "revert": "Ripristina file", "focusOpenEditors": "Stato attivo su visualizzazione editor aperti", "focusFilesExplorer": "Stato attivo su Esplora file", diff --git a/i18n/ita/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json b/i18n/ita/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json index 9ae2216ff99..586895b4e44 100644 --- a/i18n/ita/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json +++ b/i18n/ita/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json @@ -6,6 +6,6 @@ { "noWorkspace": "Nessuna cartella aperta", "explorerSection": "Sezione Esplora file", - "noWorkspaceHelp": "Non ci sono ancora cartelle aperte.", + "noFolderHelp": "Non ci sono ancora cartelle aperte.", "openFolder": "Apri cartella" } \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/markers/browser/markersFileDecorations.i18n.json b/i18n/ita/src/vs/workbench/parts/markers/browser/markersFileDecorations.i18n.json new file mode 100644 index 00000000000..bcb8c50044f --- /dev/null +++ b/i18n/ita/src/vs/workbench/parts/markers/browser/markersFileDecorations.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. +{ + "label": "Problemi" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json b/i18n/ita/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json index 237ebccfb39..36b2ac76a3a 100644 --- a/i18n/ita/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json +++ b/i18n/ita/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json @@ -5,7 +5,6 @@ // Do not edit this file. It is machine generated. { "emptyUserSettingsHeader": "Inserire le impostazioni qui per sovrascrivere quelle predefinite.", - "errorInvalidConfiguration": "Impossibile scrivere nelle impostazioni. Correggere eventuali errori o avvisi nel file e riprovare.", "emptyWorkspaceSettingsHeader": "Inserire le impostazioni qui per sovrascrivere le impostazioni utente.", "emptyFolderSettingsHeader": "Inserire le impostazioni cartella qui per sovrascrivere quelle dell'area di lavoro.", "defaultFolderSettingsTitle": "Impostazioni cartella predefinite", diff --git a/i18n/ita/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json b/i18n/ita/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json index 28964d0cf5c..543c602df83 100644 --- a/i18n/ita/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json +++ b/i18n/ita/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json @@ -13,7 +13,6 @@ "actionNotEnabled": "Il comando '{0}' non è abilitato nel contesto corrente.", "recentlyUsed": "usate di recente", "morecCommands": "altri comandi", - "commandLabel": "{0}: {1}", "cat.title": "{0}: {1}", "noCommandsMatching": "Non ci sono comandi corrispondenti" } \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/search/browser/search.contribution.i18n.json b/i18n/ita/src/vs/workbench/parts/search/browser/search.contribution.i18n.json index 7b4843f3a16..65fd721bb17 100644 --- a/i18n/ita/src/vs/workbench/parts/search/browser/search.contribution.i18n.json +++ b/i18n/ita/src/vs/workbench/parts/search/browser/search.contribution.i18n.json @@ -17,7 +17,6 @@ "exclude": "Consente di configurare i criteri GLOB per escludere file e cartelle nelle ricerche. Eredita tutti i criteri GLOB dall'impostazione files.exclude.", "exclude.boolean": "Criterio GLOB da usare per trovare percorsi file. Impostare su True o False per abilitare o disabilitare il criterio.", "exclude.when": "Controllo aggiuntivo sugli elementi di pari livello di un file corrispondente. Usare $(basename) come variabile del nome file corrispondente.", - "useRipgrep": "Controlla se usare ripgrep durante la ricerca di testo", "useIgnoreFilesByDefault": "Controlla se utilizzare i file .gitignore e .ignore come impostazione predefinita durante la ricerca in una nuova area di lavoro", "search.quickOpen.includeSymbols": "Configurare questa opzione per includere i risultati di una ricerca di simboli globale nei risultati dei file per Quick Open." } \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/search/browser/searchActions.i18n.json b/i18n/ita/src/vs/workbench/parts/search/browser/searchActions.i18n.json index 942721004df..7faf8ff218a 100644 --- a/i18n/ita/src/vs/workbench/parts/search/browser/searchActions.i18n.json +++ b/i18n/ita/src/vs/workbench/parts/search/browser/searchActions.i18n.json @@ -19,7 +19,6 @@ "ClearSearchResultsAction.label": "Cancella risultati della ricerca", "FocusNextSearchResult.label": "Sposta lo stato attivo sul risultato della ricerca successivo", "FocusPreviousSearchResult.label": "Sposta lo stato attivo sul risultato della ricerca precedente", - "RemoveAction.label": "Rimuovi", "file.replaceAll.label": "Sostituisci tutto", "match.replace.label": "Sostituisci" } \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json b/i18n/ita/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json index 09e4993b61b..8b2ae57561b 100644 --- a/i18n/ita/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json +++ b/i18n/ita/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json @@ -10,8 +10,8 @@ "vscode.extension.contributes.snippets": "Frammenti per contributes.", "vscode.extension.contributes.snippets-language": "Identificatore di linguaggio per cui si aggiunge come contributo questo frammento.", "vscode.extension.contributes.snippets-path": "Percorso del file snippets. È relativo alla cartella delle estensioni e in genere inizia con './snippets/'.", - "badFile": "Non è stato possibile leggere il file di frammento \"{0}\".", "badVariableUse": "Il frammento \"{0}\" molto probabilmente confonde variabili-frammento con segnaposti-frammento. Vedere https://code.visualstudio.com/docs/editor/userdefinedsnippets#_snippet-syntax per ulteriori dettagli.", + "badFile": "Non è stato possibile leggere il file di frammento \"{0}\".", "source.snippet": "Frammento utente", "detail.snippet": "{0} ({1})", "snippetSuggest.longLabel": "{0}, {1}" diff --git a/i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json b/i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json index ad495da28bf..9941d145008 100644 --- a/i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json +++ b/i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json @@ -16,7 +16,6 @@ "workbench.action.terminal.new.short": "Nuovo terminale", "workbench.action.terminal.focus": "Sposta stato attivo su terminale", "workbench.action.terminal.focusNext": "Sposta stato attivo su terminale successivo", - "workbench.action.terminal.focusAtIndex": "Sposta stato attivo su terminale {0}", "workbench.action.terminal.focusPrevious": "Sposta stato attivo su terminale precedente", "workbench.action.terminal.paste": "Incolla nel terminale attivo", "workbench.action.terminal.DefaultShell": "Selezionare la Shell di Default", diff --git a/i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json b/i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json index e32834163e9..adc0b4440da 100644 --- a/i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json +++ b/i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json @@ -5,7 +5,6 @@ // Do not edit this file. It is machine generated. { "copy": "Copia", - "createNewTerminal": "Nuovo terminale", "paste": "Incolla", "selectAll": "Seleziona tutto", "clear": "Cancella" diff --git a/i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json b/i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json index e21d2d1aaf0..6055f7da096 100644 --- a/i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json +++ b/i18n/ita/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json @@ -10,6 +10,5 @@ "never again": "OK, non visualizzare più", "terminal.integrated.chooseWindowsShell": "Seleziona la shell di terminale preferita - è possibile modificare questa impostazione dopo", "terminalService.terminalCloseConfirmationSingular": "C'è una sessione di terminale attiva. Terminarla?", - "terminalService.terminalCloseConfirmationPlural": "Ci sono {0} sessioni di terminale attive. Terminarle?", - "yes": "Sì" + "terminalService.terminalCloseConfirmationPlural": "Ci sono {0} sessioni di terminale attive. Terminarle?" } \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/services/configuration/common/configurationExtensionPoint.i18n.json b/i18n/ita/src/vs/workbench/services/configuration/common/configurationExtensionPoint.i18n.json new file mode 100644 index 00000000000..afd8ffa11fc --- /dev/null +++ b/i18n/ita/src/vs/workbench/services/configuration/common/configurationExtensionPoint.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "vscode.extension.contributes.configuration.title": "Riepilogo delle impostazioni. Questa etichetta verrà usata nel file di impostazioni come commento di separazione.", + "vscode.extension.contributes.configuration.properties": "Descrizione delle proprietà di configurazione.", + "scope.window.description": "Configurazione specifica della finestra, che può essere configurata nelle impostazioni dell'utente o dell'area di lavoro.", + "scope.resource.description": "Configurazione specifica di risorse, che possono essere configurate nelle impostazioni utente, in quelle dell'area di lavoro o di una cartella.", + "vscode.extension.contributes.configuration": "Impostazioni di configurazione di contributes.", + "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", + "invalid.allOf": "'configuration.allOf' è deprecato e non deve più essere usato. Passare invece una matrice di sezioni di configurazione al punto di aggiunta contributo 'configuration'.", + "workspaceConfig.folders.description": "Elenco di cartelle da caricare nell'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.name.description": "Nome facoltativo per la cartella. ", + "unknownWorkspaceProperty": "La proprietà di configurazione dell'area di lavoro è sconosciuta" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/services/editor/common/editorService.i18n.json b/i18n/ita/src/vs/workbench/services/editor/common/editorService.i18n.json new file mode 100644 index 00000000000..50e968f8ee3 --- /dev/null +++ b/i18n/ita/src/vs/workbench/services/editor/common/editorService.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. +{ + "compareLabels": "{0} ↔ {1}" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/services/themes/electron-browser/colorThemeStore.i18n.json b/i18n/ita/src/vs/workbench/services/themes/electron-browser/colorThemeStore.i18n.json new file mode 100644 index 00000000000..34e0bc8f5e8 --- /dev/null +++ b/i18n/ita/src/vs/workbench/services/themes/electron-browser/colorThemeStore.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "vscode.extension.contributes.themes": "Contributes textmate color themes.", + "vscode.extension.contributes.themes.id": "ID del tema dell'icona usato nelle impostazioni utente.", + "vscode.extension.contributes.themes.label": "Etichetta del tema colori visualizzata nell'interfaccia utente.", + "vscode.extension.contributes.themes.uiTheme": "Tema di base che definisce i colori nell'editor: 'vs' è il tema colori chiaro, mentre 'vs-dark' è il tema colori scuro e 'hc-black' è il tema a contrasto elevato scuro.", + "vscode.extension.contributes.themes.path": "Percorso del file tmTheme. È relativo alla cartella delle estensioni e corrisponde in genere a './themes/themeFile.tmTheme'.", + "reqarray": "Extension point `{0}` must be an array.", + "reqpath": "È previsto un valore stringa in `contributes.{0}.path`. Valore specificato: {1}", + "invalid.path.1": "Valore previsto di `contributes.{0}.path` ({1}) da includere nella cartella dell'estensione ({2}). L'estensione potrebbe non essere più portatile." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.i18n.json b/i18n/ita/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.i18n.json new file mode 100644 index 00000000000..b0f9faf6b52 --- /dev/null +++ b/i18n/ita/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.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. +{ + "error.cannotparseicontheme": "Problems parsing file icons file: {0}" +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.i18n.json b/i18n/ita/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.i18n.json new file mode 100644 index 00000000000..53cc79371c9 --- /dev/null +++ b/i18n/ita/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "vscode.extension.contributes.iconThemes": "Contributes file icon themes.", + "vscode.extension.contributes.iconThemes.id": "ID del tema dell'icona usato nelle impostazioni utente.", + "vscode.extension.contributes.iconThemes.label": "Etichetta del tema dell'icona visualizzata nell'interfaccia utente.", + "vscode.extension.contributes.iconThemes.path": "Percorso del file di definizione del tema dell'icona. È relativo alla cartella delle estensioni e corrisponde in genere a './icons/awesome-icon-theme.json'.", + "reqarray": "Extension point `{0}` must be an array.", + "reqpath": "È previsto un valore stringa in `contributes.{0}.path`. Valore specificato: {1}", + "reqid": "È previsto un valore stringa in `contributes.{0}.id`. Valore specificato: {1}", + "invalid.path.1": "Valore previsto di `contributes.{0}.path` ({1}) da includere nella cartella dell'estensione ({2}). L'estensione potrebbe non essere più portatile." +} \ No newline at end of file diff --git a/i18n/ita/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json b/i18n/ita/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json index d6ed75ea2e1..97d1cdc1754 100644 --- a/i18n/ita/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json +++ b/i18n/ita/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json @@ -4,31 +4,15 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "vscode.extension.contributes.themes": "Contributes textmate color themes.", - "vscode.extension.contributes.themes.id": "ID del tema dell'icona usato nelle impostazioni utente.", - "vscode.extension.contributes.themes.label": "Etichetta del tema colori visualizzata nell'interfaccia utente.", - "vscode.extension.contributes.themes.uiTheme": "Tema di base che definisce i colori nell'editor: 'vs' è il tema colori chiaro, mentre 'vs-dark' è il tema colori scuro e 'hc-black' è il tema a contrasto elevato scuro.", - "vscode.extension.contributes.themes.path": "Percorso del file tmTheme. È relativo alla cartella delle estensioni e corrisponde in genere a './themes/themeFile.tmTheme'.", - "vscode.extension.contributes.iconThemes": "Contributes file icon themes.", - "vscode.extension.contributes.iconThemes.id": "ID del tema dell'icona usato nelle impostazioni utente.", - "vscode.extension.contributes.iconThemes.label": "Etichetta del tema dell'icona visualizzata nell'interfaccia utente.", - "vscode.extension.contributes.iconThemes.path": "Percorso del file di definizione del tema dell'icona. È relativo alla cartella delle estensioni e corrisponde in genere a './icons/awesome-icon-theme.json'.", "migration.completed": "Sono state aggiunte nuove impostazioni tema alle impostazioni utente. Backup disponibile in {0}.", "error.cannotloadtheme": "Unable to load {0}: {1}", - "reqarray": "Extension point `{0}` must be an array.", - "reqpath": "È previsto un valore stringa in `contributes.{0}.path`. Valore specificato: {1}", - "invalid.path.1": "Valore previsto di `contributes.{0}.path` ({1}) da includere nella cartella dell'estensione ({2}). L'estensione potrebbe non essere più portatile.", - "reqid": "È previsto un valore stringa in `contributes.{0}.id`. Valore specificato: {1}", "error.cannotloadicontheme": "Unable to load {0}", - "error.cannotparseicontheme": "Problems parsing file icons file: {0}", "colorTheme": "Specifies the color theme used in the workbench.", "colorThemeError": "Theme is unknown or not installed.", "iconTheme": "Specifica il tema dell'icona usato nell'area di lavoro oppure 'null' se non viene visualizzato alcun icona di file.", "noIconThemeDesc": "No file icons", "iconThemeError": "File icon theme is unknown or not installed.", "workbenchColors": "Sostituisce i colori del tema colori attualmente selezionato.", - "workbenchColors.deprecated": "L'impostazione non è più sperimentale ed è stata rinominata in 'workbench.colorCustomizations'", - "workbenchColors.deprecatedDescription": "In alternativa, usare 'workbench.colorCustomizations'", "editorColors": "Sostituisce i colori dell'editor e lo stile dei font nel tema colori attualmente selezionato.", "editorColors.comments": "Imposta i colori e gli stili per i commenti", "editorColors.strings": "Imposta i colori e gli stili per i valori letterali stringa.", diff --git a/i18n/ita/src/vs/workbench/services/workspace/node/workspaceEditingService.i18n.json b/i18n/ita/src/vs/workbench/services/workspace/node/workspaceEditingService.i18n.json new file mode 100644 index 00000000000..5cfa730d8ff --- /dev/null +++ b/i18n/ita/src/vs/workbench/services/workspace/node/workspaceEditingService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "openWorkspaceConfigurationFile": "Apri file di configurazione dell'area di lavoro", + "close": "Chiudi" +} \ No newline at end of file diff --git a/i18n/jpn/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json b/i18n/jpn/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json index 7c9fffc491f..f8b65e69685 100644 --- a/i18n/jpn/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json +++ b/i18n/jpn/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json @@ -4,13 +4,13 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "activeEditorShort": "例: myFile.txt", - "activeEditorMedium": "例: myFolder/myFile.txt", - "activeEditorLong": "例: /Users/Development/myProject/myFolder/myFile.txt", - "rootName": "例: myFolder1, myFolder2, myFolder3", - "rootPath": "例: /Users/Development/myProject", - "folderName": "例: myFolder", - "folderPath": "例: /Users/Development/myFolder", + "activeEditorShort": "ファイル名 (例: myFile.txt)", + "activeEditorMedium": "ワークスペース フォルダーに相対的なファイルのパス (例: myFolder/myFile.txt)", + "activeEditorLong": "ファイルの完全なパス (例: /Users/Development/myProject/myFolder/myFile.txt)", + "rootName": "ワークスペースの名前 (例: myFolder または myWorkspace)", + "rootPath": "ワークスペースのファイル パス (例: /Users/Development/myWorkspace)", + "folderName": "ファイルが含まれているワークスペース フォルダーの名前 (例: myFolder)", + "folderPath": "ファイルが含まれているワークスペース フォルダーのファイル パス (例: /Users/Development/myFolder)", "appName": "例: VS Code", "dirty": "アクティブなエディターがダーティである場合のダーティ インジケーター", "separator": "値のある変数で囲まれた場合にのみ表示される条件付き区切り記号 (' - ')", diff --git a/i18n/jpn/extensions/emmet/package.i18n.json b/i18n/jpn/extensions/emmet/package.i18n.json index d8400d06d02..01629a8bcec 100644 --- a/i18n/jpn/extensions/emmet/package.i18n.json +++ b/i18n/jpn/extensions/emmet/package.i18n.json @@ -28,13 +28,6 @@ "command.incrementNumberByTen": "10 ずつ増加", "command.decrementNumberByTen": "10 ずつ減少", "emmetSyntaxProfiles": "指定した構文に対してプロファイルを定義するか、特定の規則がある独自のプロファイルをご使用ください。", - "emmetExclude": "Emmet 省略記法を展開すべきでない言語の配列。", - "emmetExtensionsPath": "Emmet のプロファイルとスニペットを含むフォルダーへのパス。", - "emmetShowExpandedAbbreviation": "候補として、展開された Emmet 省略記法を表示します。\n\"inMarkupAndStylesheetFilesOnly\" オプションは、html、haml、jade、slim、xml、xsl、css、scss、sass、less、および stylus に適用されます。\n\"always\" オプションは、マークアップ / css に関係なく、ファイルのすべての部分に適用されます。", - "emmetShowAbbreviationSuggestions": "候補として考えられる Emmet 省略記法を表示します。 スタイルシートや emmet.showExpandedAbbreviation が \"never\" に設定されている場合は適用されません。", - "emmetIncludeLanguages": "既定でサポートされていない言語で Emmet 省略記法を有効にします。 言語と Emmet がサポートする言語の間にマッピングを追加します。\n例: {\"vue-html\": \"html\", \"javascript\": \"javascriptreact\"}", - "emmetVariables": "Emmet のスニペットで使用される変数 ", - "emmetTriggerExpansionOnTab": "これをオンにすると、TAB キーを押したときに emmet 省略記法が展開されます.", "emmetPreferences": "Emmet の一部のアクションやリゾルバーの動作の変更に使用される基本設定。", "emmetPreferencesIntUnit": "整数値に使用する既定の単位", "emmetPreferencesFloatUnit": "float 値に使用する既定の単位", @@ -44,5 +37,7 @@ "emmetPreferencesCssBetween": "CSS の略語を展開するときに CSS プロパティと値の間に配置されるシンボル ", "emmetPreferencesSassBetween": "Sass ファイルで CSS の略語を展開するときに CSS プロパティと値の間に配置されるシンボル ", "emmetPreferencesStylusBetween": "Stylus ファイルで CSS の略語を展開するときに CSS プロパティと値の間に配置されるシンボル ", - "emmetShowSuggestionsAsSnippets": "true の場合、emmet 候補では、editor.snippetSuggestions 設定に従ってそれらを並べてスニペットとして表示されます。" + "emmetPreferencesFilterCommentBefore": "コメント フィルター使用時、一致した要素の前に配置するコメントの定義。 ", + "emmetPreferencesFilterCommentAfter": "コメント フィルター使用時、一致した要素の後に配置するコメントの定義。", + "emmetPreferencesFilterCommentTrigger": "コメント フィルターに適用される略語に存在する属性名のカンマ区切りのリスト" } \ No newline at end of file diff --git a/i18n/jpn/extensions/git/out/commands.i18n.json b/i18n/jpn/extensions/git/out/commands.i18n.json index af7b32febd1..e825de0716d 100644 --- a/i18n/jpn/extensions/git/out/commands.i18n.json +++ b/i18n/jpn/extensions/git/out/commands.i18n.json @@ -12,8 +12,9 @@ "cloning": "Git リポジトリを複製しています...", "openrepo": "リポジトリを開く", "proposeopen": "複製したリポジトリを開きますか?", - "path to init": "フォルダーのパス", - "provide path": "初期化する Git リポジトリへのフォルダー パスを入力してください", + "init repo": "リポジトリの初期化", + "create repo": "リポジトリの初期化", + "are you sure": "'{0}' に Git リポジトリを作成します。続行してもよろしいですか?", "HEAD not available": "'{0}' の HEAD バージョンは利用できません。", "confirm stage files with merge conflicts": "マージの競合がある {0} 個のファイルをステージしてもよろしいですか?", "confirm stage file with merge conflicts": "マージの競合がある {0} をステージしてもよろしいですか? ", diff --git a/i18n/jpn/extensions/git/out/repository.i18n.json b/i18n/jpn/extensions/git/out/repository.i18n.json index 553e8a8277f..11c4d9a6d7f 100644 --- a/i18n/jpn/extensions/git/out/repository.i18n.json +++ b/i18n/jpn/extensions/git/out/repository.i18n.json @@ -21,6 +21,8 @@ "deleted by us": "こちら側による削除", "both added": "双方とも追加", "both modified": "双方とも変更", + "untracked, short": "U", + "modified, short": "M", "commit": "コミット", "merge changes": "変更のマージ", "staged changes": "ステージング済みの変更", diff --git a/i18n/jpn/extensions/git/package.i18n.json b/i18n/jpn/extensions/git/package.i18n.json index ffdb7e9c5a1..e176e6c6ec0 100644 --- a/i18n/jpn/extensions/git/package.i18n.json +++ b/i18n/jpn/extensions/git/package.i18n.json @@ -12,12 +12,14 @@ "command.openFile": "ファイルを開く", "command.openHEADFile": "HEAD のファイルを開く", "command.stage": "変更のステージング", - "command.stageAll": "すべての変更のステージング", - "command.stageSelectedRanges": "選択した範囲をステージする", + "command.stageAll": "すべての変更をステージ", + "command.stageSelectedRanges": "選択した範囲をステージ", "command.revertSelectedRanges": "選択範囲を元に戻す", + "command.stageChange": "変更のステージング", + "command.revertChange": "変更を元に戻す", "command.unstage": "変更のステージング解除", "command.unstageAll": "すべての変更のステージング解除", - "command.unstageSelectedRanges": "選択した範囲をアンステージする", + "command.unstageSelectedRanges": "選択した範囲のステージを解除", "command.clean": "変更を破棄", "command.cleanAll": "すべての変更を破棄", "command.commit": "Commit", diff --git a/i18n/jpn/extensions/typescript/package.i18n.json b/i18n/jpn/extensions/typescript/package.i18n.json index 7d6417ebf7d..48300f99d35 100644 --- a/i18n/jpn/extensions/typescript/package.i18n.json +++ b/i18n/jpn/extensions/typescript/package.i18n.json @@ -44,7 +44,8 @@ "typescript.npm": "型定義の自動取得に使用される NPM 実行可能ファイルへのパスを指定します。TypeScript 2.3.4 以上が必要です。", "typescript.check.npmIsInstalled": "型定義の自動取得に NPM がインストールされているかどうかを確認します。", "javascript.nameSuggestions": "JavaScript の候補リスト内でファイルから一意の名前を含むかどうかを有効/無効にします。", - "typescript.tsc.autoDetect": "tsc タスクの自動検出をオンにするかオフにするかを制御します。", + "typescript.tsc.autoDetect": "tsc タスクの自動検出を制御します。'off' はこの機能を無効にします。'build' は 1 つのコンパイル実行タスクのみを表示します。'watch' はコンパイルとウォッチ タスクのみを表示します。'on' はビルド タスクとウォッチ タスクの両方を表示します。既定値は 'on' です。", "typescript.problemMatchers.tsc.label": "TypeScript の問題", - "typescript.problemMatchers.tscWatch.label": "TypeScript の問題 (ウォッチ モード)" + "typescript.problemMatchers.tscWatch.label": "TypeScript の問題 (ウォッチ モード)", + "typescript.quickSuggestionsForPaths": "Import パスを入力するときのクイック候補を有効/無効にします。" } \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/contrib/find/browser/findWidget.i18n.json b/i18n/jpn/src/vs/editor/contrib/find/browser/findWidget.i18n.json index 17c146860ec..b91635cb4cd 100644 --- a/i18n/jpn/src/vs/editor/contrib/find/browser/findWidget.i18n.json +++ b/i18n/jpn/src/vs/editor/contrib/find/browser/findWidget.i18n.json @@ -15,7 +15,6 @@ "label.replaceButton": "置換", "label.replaceAllButton": "すべて置換", "label.toggleReplaceButton": "置換モードの切り替え", - "title.matchesCountLimit": "最初の 999 の結果だけを強調表示しますが、テキスト全体を検索します。", - "label.matchesLocation": "{1} の {0}", + "label.matchesLocation": "{0} / {1} 件", "label.noResults": "結果なし" } \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/contrib/find/common/findController.i18n.json b/i18n/jpn/src/vs/editor/contrib/find/common/findController.i18n.json index c8149938eee..93947cdf9ce 100644 --- a/i18n/jpn/src/vs/editor/contrib/find/common/findController.i18n.json +++ b/i18n/jpn/src/vs/editor/contrib/find/common/findController.i18n.json @@ -10,12 +10,6 @@ "nextSelectionMatchFindAction": "次の選択項目を検索", "previousSelectionMatchFindAction": "前の選択項目を検索", "startReplace": "置換", - "addSelectionToNextFindMatch": "選択した項目を次の一致項目に追加", - "addSelectionToPreviousFindMatch": "選んだ項目を前の一致項目に追加する", - "moveSelectionToNextFindMatch": "最後に選択した項目を次の一致項目に移動", - "moveSelectionToPreviousFindMatch": "最後に選んだ項目を前の一致項目に移動する", - "selectAllOccurrencesOfFindMatch": "一致するすべての出現箇所を選択します", - "changeAll.label": "すべての出現箇所を変更", "showNextFindTermAction": "次の検索語句を表示", "showPreviousFindTermAction": "前の検索語句を表示" } \ No newline at end of file diff --git a/i18n/jpn/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json b/i18n/jpn/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json index 47f31c8a681..0df6dca88c6 100644 --- a/i18n/jpn/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json +++ b/i18n/jpn/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json @@ -6,5 +6,11 @@ { "mutlicursor.insertAbove": "カーソルを上に挿入", "mutlicursor.insertBelow": "カーソルを下に挿入", - "mutlicursor.insertAtEndOfEachLineSelected": "カーソルを行末に挿入" + "mutlicursor.insertAtEndOfEachLineSelected": "カーソルを行末に挿入", + "addSelectionToNextFindMatch": "選択した項目を次の一致項目に追加", + "addSelectionToPreviousFindMatch": "選んだ項目を前の一致項目に追加する", + "moveSelectionToNextFindMatch": "最後に選択した項目を次の一致項目に移動", + "moveSelectionToPreviousFindMatch": "最後に選んだ項目を前の一致項目に移動する", + "selectAllOccurrencesOfFindMatch": "一致するすべての出現箇所を選択します", + "changeAll.label": "すべての出現箇所を変更" } \ No newline at end of file diff --git a/i18n/jpn/src/vs/platform/theme/common/colorRegistry.i18n.json b/i18n/jpn/src/vs/platform/theme/common/colorRegistry.i18n.json index 69cc08c144e..55a15149aaf 100644 --- a/i18n/jpn/src/vs/platform/theme/common/colorRegistry.i18n.json +++ b/i18n/jpn/src/vs/platform/theme/common/colorRegistry.i18n.json @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "invalid.color": "無効な色形式です。 #RGB、#RGBA、#RRGGBB、#RRGGBBAA のいずれかを使用してください", "schema.colors": "ワークベンチで使用する色。", "foreground": "全体の前景色。この色は、コンポーネントによってオーバーライドされていない場合にのみ使用されます。", "errorForeground": "エラー メッセージ全体の前景色。この色は、コンポーネントによって上書きされていない場合にのみ使用されます。", 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 a72aa05e5aa..cf2a58db97a 100644 --- a/i18n/jpn/src/vs/workbench/browser/actions/workspaceActions.i18n.json +++ b/i18n/jpn/src/vs/workbench/browser/actions/workspaceActions.i18n.json @@ -9,14 +9,17 @@ "addFolderToWorkspace": "ワークスペースにフォルダーを追加...", "add": "追加(&&A)", "addFolderToWorkspaceTitle": "ワークスペースにフォルダーを追加", + "globalRemoveFolderFromWorkspace": "ワークスペースからフォルダーを削除...", "newWorkspace": "新しいワークスペース...", "select": "選択(&&S)", "selectWorkspace": "ワークスペースのフォルダーを選択", "removeFolderFromWorkspace": "ワークスペースからフォルダーを削除", + "openFolderSettings": "フォルダーの設定を開く", "saveWorkspaceAsAction": "名前を付けてワークスペースを保存...", "save": "保存(&&S)", "saveWorkspace": "ワークスペースを保存", "openWorkspaceAction": "ワークスペースを開く...", "openWorkspaceConfigFile": "ワークスペースの構成ファイルを開く", + "openFolderAsWorkspaceInNewWindow": "新しいウィンドウでワークスペースとしてフォルダーを開く", "workspaceFolderPickerPlaceholder": "ワークスペース フォルダーを選択" } \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json b/i18n/jpn/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json index b9c158c0786..85e7c1f4a9c 100644 --- a/i18n/jpn/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json +++ b/i18n/jpn/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json @@ -5,6 +5,5 @@ // Do not edit this file. It is machine generated. { "hideActivitBar": "アクティビティ バーを非表示にする", - "activityBarAriaLabel": "アクティブなビュー スイッチャー", "globalActions": "グローバル操作" } \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/browser/parts/compositebar/compositeBar.i18n.json b/i18n/jpn/src/vs/workbench/browser/parts/compositebar/compositeBar.i18n.json new file mode 100644 index 00000000000..23e9cfae128 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/browser/parts/compositebar/compositeBar.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. +{ + "activityBarAriaLabel": "アクティブなビュー スイッチャー" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/browser/parts/compositebar/compositeBarActions.i18n.json b/i18n/jpn/src/vs/workbench/browser/parts/compositebar/compositeBarActions.i18n.json new file mode 100644 index 00000000000..9edd21a8c9a --- /dev/null +++ b/i18n/jpn/src/vs/workbench/browser/parts/compositebar/compositeBarActions.i18n.json @@ -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. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "badgeTitle": "{0} - {1}", + "additionalViews": "その他のビュー", + "numberBadge": "{0} ({1})", + "manageExtension": "拡張機能を管理", + "titleKeybinding": "{0} ({1})", + "toggle": "ビューのピン留めの切り替え" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json b/i18n/jpn/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json index 198f105b057..86cc5cdfa14 100644 --- a/i18n/jpn/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json +++ b/i18n/jpn/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json @@ -12,5 +12,6 @@ "groupTwoPicker": "2 番目のグループでエディターを表示する", "groupThreePicker": "3 番目のグループのエディターを表示する", "allEditorsPicker": "開いているエディターをすべて表示する", - "view": "表示" + "view": "表示", + "file": "ファイル" } \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json b/i18n/jpn/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json index 8056b34fd1f..e750906b858 100644 --- a/i18n/jpn/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json +++ b/i18n/jpn/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json @@ -4,6 +4,5 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "canNotRun": "コマンド '{0}' は現在有効ではなく、実行できません。", "manageExtension": "拡張機能を管理" } \ 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 880049ace58..a4a1c53595c 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,16 +10,13 @@ "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": "開かれるエディターをプレビューとして表示するかどうかを制御します。プレビュー エディターは (例: ダブル クリックまたは編集などによって) 変更される時まで再利用し、斜体で表示します。", "enablePreviewFromQuickOpen": "Quick Open で開いたエディターをプレビューとして表示するかどうかを制御します。プレビュー エディターは、保持されている間、再利用されます (ダブルクリックまたは編集などによって)。", - "editorOpenPositioning": "エディターを開く場所を制御します。[左] または [右] を選択して、現在アクティブになっているエディターの左または右にエディターを開きます。[最初] または [最後] を選択して、現在アクティブになっているエディターとは別個にエディターを開きます。", "revealIfOpen": "任意の表示グループが開かれた場合に、そこにエディターを表示するかどうかを制御します。無効にした場合、エディターは現在のアクティブなエディター グループに優先して開かれます。有効にした場合は、現在のアクティブなエディター グループにもう一度開くのではなく、既に開いているエディターが表示されます。特定のグループ内や現在アクティブなグループの横に強制的にエディターを開いた場合などに、この設定が無視される場合もあることにご注意ください。", "commandHistory": "コマンド パレットで最近使用したコマンド履歴を保持する数を制御します。0 に設定するとコマンド履歴を無効にします。", "preserveInput": "次回開いたとき、コマンド パレットの最後の入力を復元するかどうかを制御します。", @@ -50,7 +47,7 @@ "restoreWindows": "再起動後にワークスペースを再度開く方法を制御します。'none' を選択すると常に空のワークスペースで開始します。'one' を選択すると最後に使用したウィンドウを再度開きます。'folders' を選択すると開かれていたフォルダーとすべてのウィンドウを再度開きます。'all' を選択すると前回のセッションのすべてのウィンドウを再度開きます。", "restoreFullscreen": "全画面表示モードで終了した場合に、ウィンドウを全画面表示モードに復元するかどうかを制御します。", "zoomLevel": "ウィンドウのズーム レベルを調整します。元のサイズは 0 で、1 つ上げるごとに (1 など) 20% ずつ拡大することを表し、1 つ下げるごとに (-1 など) 20% ずつ縮小することを表します。小数点以下の桁数を入力して、さらに細かくズーム レベルを調整することもできます。", - "title": "アクティブなエディターに基づいてウィンドウのタイトルを制御します。変数は、コンテキストに基づいて置換されます:\n${activeEditorShort}: 例: myFile.txt\n${activeEditorMedium}: 例: myFolder/myFile.txt\n${activeEditorLong}: 例: /Users/Development/myProject/myFolder/myFile.txt\n${folderName}: 例: myFolder\n${folderPath}: 例: /Users/Development/myFolder\n${rootName}: 例: myFolder1, myFolder2, myFolder3\n${rootPath}: 例: /Users/Development/myWorkspace\n${appName}: 例: VS Code\n${dirty}: アクティブなエディターがダーティである場合のダーティ インジゲーター\n${separator}: 値のある変数で囲まれた場合にのみ表示される条件付き区切り記号 (\" - \")", + "title": "アクティブなエディターに基づいてウィンドウのタイトルを制御します。変数は、コンテキストに基づいて置換されます:\n${activeEditorShort}: ファイル名 (例: myFile.txt)\n${activeEditorMedium}: ワークスペース フォルダーへの相対パス (例: myFolder/myFile.txt)\n${activeEditorLong}: ファイルの完全なパス (例: /Users/Development/myProject/myFolder/myFile.txt)\n${folderName}: ファイルが含まれているワークスペース フォルダー名 (例: myFolder)\n${folderPath}: ァイルが含まれているワークスペース フォルダーのファイルパス (例: /Users/Development/myFolder)\n${rootName}: ワークスペースの名前 (例: myFolder や myWorkspace)\n${rootPath}: ワークスペースのファイル パス (例: /Users/Development/myWorkspace)\n${appName}: 例: VS Code\n${dirty}: アクティブなエディターがダーティである場合のダーティ インジゲーター\n${separator}: 値のある変数で囲まれた場合にのみ表示される条件付き区切り記号 (\" - \")", "window.newWindowDimensions.default": "新しいウィンドウを画面の中央に開きます。", "window.newWindowDimensions.inherit": "新しいウィンドウを、最後にアクティブだったウィンドウと同じサイズで開きます。", "window.newWindowDimensions.maximized": "新しいウィンドウを最大化した状態で開きます。", diff --git a/i18n/jpn/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json b/i18n/jpn/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json index 24b30de36ed..f9530ecfb90 100644 --- a/i18n/jpn/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json +++ b/i18n/jpn/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json @@ -6,6 +6,8 @@ { "entryAriaLabel": "{0}、デバッグ", "debugAriaLabel": "実行する起動構成の名前を入力してください。", + "addConfigTo": "設定 ({0}) の追加 ...", + "addConfiguration": "構成の追加...", "noConfigurationsMatching": "一致するデバッグ構成はありません", "noConfigurationsFound": "デバッグ構成が見つかりません。'launch.json' ファイルを作成してください。" } \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/debug/browser/debugStatus.i18n.json b/i18n/jpn/src/vs/workbench/parts/debug/browser/debugStatus.i18n.json new file mode 100644 index 00000000000..9465d938566 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/debug/browser/debugStatus.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. +{ + "debug": "デバッグ" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/debug/browser/debugViewlet.i18n.json b/i18n/jpn/src/vs/workbench/parts/debug/browser/debugViewlet.i18n.json new file mode 100644 index 00000000000..b317922a971 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/debug/browser/debugViewlet.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "debugFocusVariablesView": "変数にフォーカス", + "debugFocusWatchView": "ウォッチにフォーカス", + "debugFocusCallStackView": "コールスタックにフォーカス", + "debugFocusBreakpointsView": "ブレークポイントにフォーカス" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json b/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json index 03f52be8c47..84777c482b2 100644 --- a/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json +++ b/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json @@ -15,7 +15,6 @@ "vscode.extension.contributes.debuggers.initialConfigurations": "初期 'launch.json' を生成するための構成。", "vscode.extension.contributes.debuggers.languages": "デバッグ拡張機能が \"既定のデバッガー\" とされる言語の一覧。", "vscode.extension.contributes.debuggers.adapterExecutableCommand": "指定されている場合、VS Code はこのコマンドを呼び出し、デバッグ アダプターの実行可能パスと、渡す引数を決定します。", - "vscode.extension.contributes.debuggers.startSessionCommand": "VS Code が指定されている場合、この拡張機能を対象とする \"デバッグ\" または \"実行\" アクションにこのコマンドが呼び出されます。", "vscode.extension.contributes.debuggers.configurationSnippets": "'launch.json' に新しい構成を追加するためのスニペット。", "vscode.extension.contributes.debuggers.configurationAttributes": "'launch.json' を検証するための JSON スキーマ構成。", "vscode.extension.contributes.debuggers.windows": "Windows 固有の設定。", 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 86136d977a5..37262b21f31 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,10 +12,10 @@ "breakpointRemoved": "ブレークポイントを削除しました。行 {0}、ファイル {1}", "compoundMustHaveConfigurations": "複合構成を開始するには、複合に \"configurations\" 属性が設定されている必要があります。", "configMissing": "構成 '{0}' が 'launch.json' 内にありません。", - "debugRequestNotSupported": "選択したデバッグ構成ではサポートされていない属性値 `{0}` : '{1}'。", - "debugRequesMissing": "選択しているデバッグ構成に '{0}' 属性が含まれていません ", + "debugRequestNotSupported": "選択しているデバッグ構成で `{0}` 属性はサポートされない値 '{1}' を指定しています。", + "debugRequesMissing": "選択しているデバッグ構成に `{0}` 属性が含まれていません。", "debugTypeNotSupported": "構成されているデバッグの種類 '{0}' はサポートされていません。", - "debugTypeMissing": "選択された起動構成のプロパティ 'type' がありません。", + "debugTypeMissing": "選択している起動構成の `type` プロパティがありません。", "preLaunchTaskErrors": "preLaunchTask '{0}' の実行中にビルド エラーが検出されました。", "preLaunchTaskError": "preLaunchTask '{0}' の実行中にビルド エラーが検出されました。", "preLaunchTaskExitCode": "preLaunchTask '{0}' が終了コード {1} で終了しました。", diff --git a/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json b/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json index 38c84790727..57032e9d668 100644 --- a/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json +++ b/i18n/jpn/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json @@ -8,5 +8,5 @@ "replVariableAriaLabel": "変数 {0} に値 {1} があります、Read Eval Print Loop、デバッグ", "replExpressionAriaLabel": "式 {0} に値 {1} があります、Read Eval Print Loop、デバッグ", "replValueOutputAriaLabel": "{0}、Read Eval Print Loop、デバッグ", - "replKeyValueOutputAriaLabel": "出力変数 {0} に値 {1} があります、Read Eval Print Loop、デバッグ" + "replRawObjectAriaLabel": "Repl 変数 {0} に値 {1} があります、Read Eval Print Loop、デバッグ" } \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/files/browser/fileActions.i18n.json b/i18n/jpn/src/vs/workbench/parts/files/browser/fileActions.i18n.json index b11053bec02..c73b9e75a7e 100644 --- a/i18n/jpn/src/vs/workbench/parts/files/browser/fileActions.i18n.json +++ b/i18n/jpn/src/vs/workbench/parts/files/browser/fileActions.i18n.json @@ -23,6 +23,7 @@ "confirmMoveTrashMessageFile": "'{0}' を削除しますか?", "undoBin": "ごみ箱から復元できます。", "undoTrash": "ゴミ箱から復元できます。", + "doNotAskAgain": "再度表示しない", "confirmDeleteMessageFolder": "'{0}' とその内容を完全に削除してもよろしいですか?", "confirmDeleteMessageFile": "'{0}' を完全に削除してもよろしいですか?", "irreversible": "このアクションは元に戻すことができません。", @@ -37,8 +38,6 @@ "openToSide": "横に並べて開く", "compareSource": "比較対象の選択", "globalCompareFile": "アクティブ ファイルを比較しています...", - "pickHistory": "比較対象として、以前に開いたファイルを選択する", - "unableToFileToCompare": "選択されたファイルを '{0}' と比較できません。", "openFileToCompare": "まずファイルを開いてから別のファイルと比較してください", "compareWith": "'{0}' と '{1}' を比較", "compareFiles": "ファイルの比較", @@ -47,7 +46,7 @@ "saveAs": "名前を付けて保存...", "saveAll": "すべて保存", "saveAllInGroup": "グループ内のすべてを保存する", - "saveFiles": "ダーティ ファイルを保存", + "saveFiles": "すべてのファイルを保存", "revert": "ファイルを元に戻す", "focusOpenEditors": "開いているエディターのビューにフォーカスする", "focusFilesExplorer": "ファイル エクスプローラーにフォーカスを置く", @@ -58,7 +57,7 @@ "openFile": "ファイルを開く...", "openFileInNewWindow": "新しいウィンドウでアクティブ ファイルを開く", "openFileToShowInNewWindow": "まずファイルを開いてから新しいウィンドウで開きます", - "revealInWindows": "エクスプローラーで表示します", + "revealInWindows": "エクスプローラーで表示", "revealInMac": "Finder で表示します", "openContainer": "このアイテムのフォルダーを開く", "revealActiveFileInWindows": "Windows エクスプローラーでアクティブ ファイルを表示する", 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 c1dd7969f73..a34064cb71f 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 @@ -40,10 +40,14 @@ "dynamicHeight": "開いているエディターのセクションの高さを要素の数に合わせて動的に調整するかどうかを制御します。", "autoReveal": "エクスプローラーでファイルを開くとき、自動的にファイルの内容を表示して選択するかどうかを制御します。", "enableDragAndDrop": "ドラッグ アンド ドロップを使用したファイルとフォルダーの移動をエクスプローラーが許可するかどうかを制御します。", + "confirmDragAndDrop": "ドラッグ アンド ドロップによるファイルやフォルダーの移動時にエクスプローラーが確認を求めるかどうかを制御します。", + "confirmDelete": "ごみ箱を経由したファイル削除時にエクスプローラーが確認を求めるかどうかを制御します。", "sortOrder.default": "ファイルとフォルダーをアルファベット順に名前で並び替えます。フォルダーはファイルの前に表示されます。", "sortOrder.mixed": "ファイルとフォルダーをアルファベット順に名前で並び替えます。ファイルはフォルダーと混交して表示されます。", "sortOrder.filesFirst": "ファイルとフォルダーをアルファベット順に名前で並び替えます。ファイルはフォルダーの前に表示されます。", "sortOrder.type": "ファイルとフォルダーをアルファベット順に拡張子で並び替えます。フォルダーはファイルの前に表示されます。", "sortOrder.modified": "ファイルとフォルダーを降順に最終更新日で並び替えます。フォルダーはファイルの前に表示されます。", - "sortOrder": "エクスプローラー内のファイルとフォルダーの並び順を制御します。既定の並び順に加えて、'mixed' (ファイルとフォルダーを混交した並び順)、' type' (ファイルの種類順)、' modified' (最終更新日時順)、または 'filesFirst' (フォルダーの前にファイルを並べる) のいずれかの並び順に設定できます。 " + "sortOrder": "エクスプローラー内のファイルとフォルダーの並び順を制御します。既定の並び順に加えて、'mixed' (ファイルとフォルダーを混交した並び順)、' type' (ファイルの種類順)、' modified' (最終更新日時順)、または 'filesFirst' (フォルダーの前にファイルを並べる) のいずれかの並び順に設定できます。 ", + "explorer.decorations.colors": "ファイルの装飾に配色を使用するかどうかを制御します。", + "explorer.decorations.badges": "ファイルの装飾にバッジを使用するかどうかを制御します。" } \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json b/i18n/jpn/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json index 93f172ab424..ac152686d08 100644 --- a/i18n/jpn/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json +++ b/i18n/jpn/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json @@ -6,6 +6,8 @@ { "noWorkspace": "開いているフォルダーがありません", "explorerSection": "ファイル エクスプローラー セクション", - "noWorkspaceHelp": "まだフォルダーを開いていません。", + "noWorkspaceHelp": "まだフォルダーをワークスペースに追加していません。", + "addFolder": "フォルダーの追加", + "noFolderHelp": "まだフォルダーを開いていません。", "openFolder": "フォルダーを開く" } \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json b/i18n/jpn/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json index 2199fe2e71e..2a38faa864f 100644 --- a/i18n/jpn/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json +++ b/i18n/jpn/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json @@ -4,12 +4,15 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { + "canNotResolve": "{0} フォルダーを解決できません", "fileInputAriaLabel": "ファイル名を入力します。Enter キーを押して確認するか、Esc キーを押して取り消します。", "filesExplorerViewerAriaLabel": "{0}、ファイル エクスプローラー", "dropFolders": "ワークスペースにフォルダーを追加しますか?", "dropFolder": "ワークスペースにフォルダーを追加しますか?", "addFolders": "フォルダーの追加(&&A)", "addFolder": "フォルダーの追加(&&A)", + "confirmMove": "'{0}' を移動しますか?", + "doNotAskAgain": "再度表示しない", "confirmOverwriteMessage": "'{0}' は保存先フォルダーに既に存在します。置き換えてもよろしいですか。", "irreversible": "このアクションは元に戻すことができません。", "replaceButtonLabel": "置換(&&R)" diff --git a/i18n/jpn/src/vs/workbench/parts/markers/browser/markersFileDecorations.i18n.json b/i18n/jpn/src/vs/workbench/parts/markers/browser/markersFileDecorations.i18n.json new file mode 100644 index 00000000000..1f12247ced5 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/parts/markers/browser/markersFileDecorations.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "label": "問題", + "tooltip.1": "このファイルに 1 つの問題", + "tooltip.N": "このファイルに {0} 個の問題", + "markers.showOnFile": "ファイルとフォルダーにエラーと警告を表示します。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json b/i18n/jpn/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json index 72cb0a9b0ae..c087aadd823 100644 --- a/i18n/jpn/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json +++ b/i18n/jpn/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json @@ -5,7 +5,6 @@ // Do not edit this file. It is machine generated. { "emptyUserSettingsHeader": "既定の設定を上書きするには、このファイル内に設定を挿入します。", - "errorInvalidConfiguration": "設定を書き込めません。ファイル内のエラー/警告を修正してからもう一度お試しください。", "emptyWorkspaceSettingsHeader": "ユーザー設定を上書きするには、このファイル内に設定を挿入します。", "emptyFolderSettingsHeader": "ワークスペースの設定を上書きするには、このファイル内にフォルダーの設定を挿入します。", "defaultFolderSettingsTitle": "既定のフォルダー設定", diff --git a/i18n/jpn/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json b/i18n/jpn/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json index b0aca24403e..306d7198a6b 100644 --- a/i18n/jpn/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json +++ b/i18n/jpn/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json @@ -13,7 +13,6 @@ "actionNotEnabled": "コマンド '{0}' は現在のコンテキストでは無効です。", "recentlyUsed": "最近使用したもの", "morecCommands": "その他のコマンド", - "commandLabel": "{0}: {1}", "cat.title": "{0}: {1}", "noCommandsMatching": "一致するコマンドはありません" } \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json b/i18n/jpn/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json index 3eaaf1477f2..043a0f4014f 100644 --- a/i18n/jpn/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json +++ b/i18n/jpn/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json @@ -4,6 +4,10 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { + "changes": "{1} 個のうち {0} 個の変更", + "change": "{1} 個のうち {0} 個の変更 ", + "show previous change": "前の変更箇所を表示", + "show next change": "次の変更箇所を表示", "editorGutterModifiedBackground": "編集された行を示すエディター余白の背景色。", "editorGutterAddedBackground": "追加された行を示すエディター余白の背景色。", "editorGutterDeletedBackground": "削除された行を示すエディター余白の背景色。", diff --git a/i18n/jpn/src/vs/workbench/parts/search/browser/search.contribution.i18n.json b/i18n/jpn/src/vs/workbench/parts/search/browser/search.contribution.i18n.json index d39ad81f948..68c9699706e 100644 --- a/i18n/jpn/src/vs/workbench/parts/search/browser/search.contribution.i18n.json +++ b/i18n/jpn/src/vs/workbench/parts/search/browser/search.contribution.i18n.json @@ -17,7 +17,7 @@ "exclude": "検索でファイルとフォルダーを除外するために glob パターンを構成します。files.exclude 設定からすべての glob パターンを継承します。", "exclude.boolean": "ファイル パスの照合基準となる glob パターン。これを true または false に設定すると、パターンがそれぞれ有効/無効になります。", "exclude.when": "一致するファイルの兄弟をさらにチェックします。一致するファイル名の変数として $(basename) を使用します。", - "useRipgrep": "テキスト検索で ripgrep を使用するかどうかを制御します", + "useRipgrep": "テキストとファイル検索で ripgrep を使用するかどうかを制御します", "useIgnoreFilesByDefault": "新しいワークスペースで検索するときに、既定で .gitignore ファイルを使用するか .ignore ファイルを使用するかを制御します。", "search.quickOpen.includeSymbols": "グローバル シンボル検索の結果を、Quick Open の結果ファイルに含めるように構成します。" } \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/parts/search/browser/searchActions.i18n.json b/i18n/jpn/src/vs/workbench/parts/search/browser/searchActions.i18n.json index ab57a31ce38..1aa0dfacb0e 100644 --- a/i18n/jpn/src/vs/workbench/parts/search/browser/searchActions.i18n.json +++ b/i18n/jpn/src/vs/workbench/parts/search/browser/searchActions.i18n.json @@ -19,7 +19,6 @@ "ClearSearchResultsAction.label": "検索結果のクリア", "FocusNextSearchResult.label": "次の検索結果にフォーカス", "FocusPreviousSearchResult.label": "前の検索結果にフォーカス", - "RemoveAction.label": "削除", "file.replaceAll.label": "すべて置換", "match.replace.label": "置換" } \ 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 adb4b5b9a1d..057c918a74d 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,8 +10,8 @@ "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 をご覧ください。", + "badFile": "スニペット ファイル \"{0}\" を読み込むことができませんでした。", "source.snippet": "ユーザー スニペット", "detail.snippet": "{0} ({1})", "snippetSuggest.longLabel": "{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 b93c884904a..ec353f855bf 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 @@ -14,6 +14,7 @@ "runningTasks": "実行中のタスクを表示", "tasks": "タスク", "TaskSystem.noHotSwap": "アクティブなタスクを実行しているタスク実行エンジンを変更するには、ウィンドウの再読み込みが必要です", + "TaskServer.folderIgnored": "{0} フォルダーはタスク バージョン 0.1.0 を使用しているために無視されます", "TaskService.noBuildTask1": "ビルド タスクが定義されていません。tasks.json ファイルでタスクに 'isBuildCommand' というマークを付けてください。", "TaskService.noBuildTask2": "ビルド タスクが定義されていません。tasks.json ファイルでタスクに 'build' グループとしてマークを付けてください。", "TaskService.noTestTask1": "テスト タスクが定義されていません。tasks.json ファイルでタスクに 'isTestCommand' というマークを付けてください。", @@ -30,6 +31,7 @@ "TaskSystem.activeSame.noBackground": "'{0}' タスクは既にアクティブです。タスクを終了するにはタスク メニューから`タスクの終了...` を使用してください。 ", "TaskSystem.active": "既に実行中のタスクがあります。まずこのタスクを終了してから、別のタスクを実行してください。", "TaskSystem.restartFailed": "タスク {0} を終了して再開できませんでした", + "TaskService.noConfiguration": "エラー: {0} タスク検出は次の構成に対してタスクを提供していません:\n{1}\nこのタスクは無視されます。\n", "TaskSystem.configurationErrors": "エラー: 指定したタスク構成に検証エラーがあり、使用できません。最初にエラーを修正してください。", "taskService.ignoreingFolder": "ワークスペース フォルダー {0} のタスク構成を無視します。マルチ フォルダー ワークスペース タスクのサポートには、すべてのフォルダーがタスク バージョン 2.0.0 を使用する必要があります。\n", "TaskSystem.invalidTaskJson": "エラー: tasks.json ファイルの内容に構文エラーがあります。訂正してからタスクを実行してください。\n", diff --git a/i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json b/i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json index 4d0ecdd18b0..76dbf9d84f0 100644 --- a/i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json +++ b/i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json @@ -17,6 +17,7 @@ "terminal.integrated.fontFamily": "端末のフォント ファミリを制御します。既定値は editor.fontFamily になります。", "terminal.integrated.fontSize": "ターミナルのフォント サイズをピクセル単位で制御します。", "terminal.integrated.lineHeight": "ターミナルの行の高さを制御します。この数値にターミナルのフォント サイズを乗算すると、実際の行の高さ (ピクセル単位) になります。", + "terminal.integrated.enableBold": "ターミナル内でテキストを太字にするかどうか。ターミナル シェルのサポートが必要なことに注意してください。", "terminal.integrated.cursorBlinking": "ターミナルのカーソルを点滅させるかどうかを制御します。", "terminal.integrated.cursorStyle": "端末のカーソルのスタイルを制御します。", "terminal.integrated.scrollback": "端末がそのバッファーに保持できる最大行数を制御します。", 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 3c51dcd1d5d..6015fb400ae 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 @@ -16,7 +16,6 @@ "workbench.action.terminal.new.short": "新しいターミナル", "workbench.action.terminal.focus": "端末にフォーカス", "workbench.action.terminal.focusNext": "次の端末にフォーカス", - "workbench.action.terminal.focusAtIndex": "ターミナル {0} にフォーカス", "workbench.action.terminal.focusPrevious": "前のターミナルにフォーカス", "workbench.action.terminal.paste": "アクティブなターミナルに貼り付け", "workbench.action.terminal.DefaultShell": "既定のシェルの選択", diff --git a/i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json b/i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json index 0c5802f9502..acbc00bf882 100644 --- a/i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json +++ b/i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json @@ -5,7 +5,6 @@ // Do not edit this file. It is machine generated. { "copy": "コピー", - "createNewTerminal": "新しいターミナル", "paste": "貼り付け", "selectAll": "すべて選択", "clear": "クリア" diff --git a/i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json b/i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json index 0303f610bb0..42ec3aff619 100644 --- a/i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json +++ b/i18n/jpn/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json @@ -10,6 +10,5 @@ "never again": "OK、今後は表示しない", "terminal.integrated.chooseWindowsShell": "優先するターミナル シェルを選択します。これは後で設定から変更できます", "terminalService.terminalCloseConfirmationSingular": "アクティブなターミナル セッションが 1 つあります。中止しますか?", - "terminalService.terminalCloseConfirmationPlural": "アクティブなターミナル セッションが {0} 個あります。中止しますか?", - "yes": "はい" + "terminalService.terminalCloseConfirmationPlural": "アクティブなターミナル セッションが {0} 個あります。中止しますか?" } \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/services/configuration/common/configurationExtensionPoint.i18n.json b/i18n/jpn/src/vs/workbench/services/configuration/common/configurationExtensionPoint.i18n.json new file mode 100644 index 00000000000..d93af154acf --- /dev/null +++ b/i18n/jpn/src/vs/workbench/services/configuration/common/configurationExtensionPoint.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "vscode.extension.contributes.configuration.title": "設定の概要です。このラベルは、設定ファイルでコメントの区切り文字として使用します。", + "vscode.extension.contributes.configuration.properties": "構成のプロパティの説明です。", + "scope.window.description": "ウィンドウ固有の構成。ユーザーまたはワークスペースの設定で構成できます。", + "scope.resource.description": "リソース固有の構成。ユーザー、ワークスペース、またはフォルダーの設定で構成できます。", + "scope.description": "構成が適用される範囲。 使用可能なスコープは `window` と ` resource` です。", + "vscode.extension.contributes.configuration": "構成の設定を提供します。", + "invalid.title": "'configuration.title' は、文字列である必要があります", + "vscode.extension.contributes.defaultConfiguration": "言語ごとに既定のエディター構成の設定を提供します。", + "invalid.properties": "'configuration.properties' は、オブジェクトである必要があります", + "invalid.allOf": "'configuration.allOf' は非推奨で使用できなくなります。代わりに 'configuration' コントリビューション ポイントに複数の構成セクションを配列として渡します。", + "workspaceConfig.folders.description": "ワークスペースで読み込まれるフォルダーのリスト。", + "workspaceConfig.path.description": "ファイルパス。例: `/root/folderA` または `./folderA` のようなワークスペース ファイルの場所に対して解決される相対パス。", + "workspaceConfig.name.description": "フォルダーにつけるオプションの名前。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/services/editor/common/editorService.i18n.json b/i18n/jpn/src/vs/workbench/services/editor/common/editorService.i18n.json new file mode 100644 index 00000000000..50e968f8ee3 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/services/editor/common/editorService.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. +{ + "compareLabels": "{0} ↔ {1}" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json b/i18n/jpn/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json index 2773e1cc1be..31dd84eaa5c 100644 --- a/i18n/jpn/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json +++ b/i18n/jpn/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json @@ -6,6 +6,7 @@ { "schema.token.settings": "トークンの色とスタイル。", "schema.token.foreground": "トークンの前景色。", + "schema.token.background.warning": "トークンの背景色は、現在サポートされていません。", "schema.token.fontStyle": "ルールのフォント スタイル: '斜体'、'太字'、'下線' のいずれかまたはこれらの組み合わせ", "schema.fontStyle.error": "フォント スタイルは '斜体'、'太字'、'下線'を組み合わせる必要があります。", "schema.properties.name": "ルールの説明。", diff --git a/i18n/jpn/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json b/i18n/jpn/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json index a38d55a5642..335b53c2495 100644 --- a/i18n/jpn/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json +++ b/i18n/jpn/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json @@ -33,5 +33,6 @@ "schema.fontSize": "フォントを使用する場合: テキスト フォントに対するフォントサイズの割合。設定されていない場合、既定値はフォント定義のサイズになります。", "schema.fontId": "フォントを使用する場合: フォントの ID。設定されていない場合、既定値は最初のフォント定義になります。", "schema.light": "明るい配色テーマでのファイル アイコンの任意の関連付け。", - "schema.highContrast": "ハイ コントラスト配色テーマでのファイル アイコンの任意の関連付け。" + "schema.highContrast": "ハイ コントラスト配色テーマでのファイル アイコンの任意の関連付け。", + "schema.hidesExplorerArrows": "このテーマがアクティブな時に、エクスプローラーの矢印を非表示にするかどうかを構成します。" } \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/services/themes/electron-browser/colorThemeStore.i18n.json b/i18n/jpn/src/vs/workbench/services/themes/electron-browser/colorThemeStore.i18n.json new file mode 100644 index 00000000000..c985a8de47f --- /dev/null +++ b/i18n/jpn/src/vs/workbench/services/themes/electron-browser/colorThemeStore.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "vscode.extension.contributes.themes": "TextMate の配色テーマを提供します。", + "vscode.extension.contributes.themes.id": "ユーザー設定で使用されるアイコン テーマの ID。", + "vscode.extension.contributes.themes.label": "UI で表示される配色テーマのラベル。", + "vscode.extension.contributes.themes.uiTheme": "エディターの周囲の色を定義する基本テーマ: 'vs' は明るい色のテーマで、'vs-dark' は濃い色のテーマです。'hc-black' は濃い色のハイ コントラストのテーマです。", + "vscode.extension.contributes.themes.path": "tmTheme ファイルのパス。拡張機能フォルダーに対する相対パスで、通常 './themes/themeFile.tmTheme' です。", + "reqarray": "Extension point `{0}` must be an array.", + "reqpath": "`contributes.{0}.path` に文字列が必要です。提供された値: {1}", + "invalid.path.1": "拡張機能のフォルダー ({2}) の中に `contributes.{0}.path` ({1}) が含まれている必要があります。これにより拡張を移植できなくなる可能性があります。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.i18n.json b/i18n/jpn/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.i18n.json new file mode 100644 index 00000000000..26366aa3de6 --- /dev/null +++ b/i18n/jpn/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.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. +{ + "error.cannotparseicontheme": "アイコン ファイルの解析中に問題が発生しました: {0}" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.i18n.json b/i18n/jpn/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.i18n.json new file mode 100644 index 00000000000..b9a14b3199a --- /dev/null +++ b/i18n/jpn/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "vscode.extension.contributes.iconThemes": "Contributes file icon themes.", + "vscode.extension.contributes.iconThemes.id": "ユーザー設定で使用されるアイコン テーマの ID。", + "vscode.extension.contributes.iconThemes.label": "UI に表示されるアイコン テーマのラベル。", + "vscode.extension.contributes.iconThemes.path": "アイコン テーマの定義ファイルのパス。このパスは拡張フォルダーの相対パスであり、通常は './icons/awesome-icon-theme.json' です。", + "reqarray": "Extension point `{0}` must be an array.", + "reqpath": "`contributes.{0}.path` に文字列が必要です。提供された値: {1}", + "reqid": "`contributes.{0}.id` に文字列が必要です。提供された値: {1}", + "invalid.path.1": "拡張機能のフォルダー ({2}) の中に `contributes.{0}.path` ({1}) が含まれている必要があります。これにより拡張を移植できなくなる可能性があります。" +} \ No newline at end of file diff --git a/i18n/jpn/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json b/i18n/jpn/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json index 18938fb3a92..db40a8ef3c1 100644 --- a/i18n/jpn/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json +++ b/i18n/jpn/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json @@ -4,31 +4,15 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "vscode.extension.contributes.themes": "TextMate の配色テーマを提供します。", - "vscode.extension.contributes.themes.id": "ユーザー設定で使用されるアイコン テーマの ID。", - "vscode.extension.contributes.themes.label": "UI で表示される配色テーマのラベル。", - "vscode.extension.contributes.themes.uiTheme": "エディターの周囲の色を定義する基本テーマ: 'vs' は明るい色のテーマで、'vs-dark' は濃い色のテーマです。'hc-black' は濃い色のハイ コントラストのテーマです。", - "vscode.extension.contributes.themes.path": "tmTheme ファイルのパス。拡張機能フォルダーに対する相対パスで、通常 './themes/themeFile.tmTheme' です。", - "vscode.extension.contributes.iconThemes": "Contributes file icon themes.", - "vscode.extension.contributes.iconThemes.id": "ユーザー設定で使用されるアイコン テーマの ID。", - "vscode.extension.contributes.iconThemes.label": "UI に表示されるアイコン テーマのラベル。", - "vscode.extension.contributes.iconThemes.path": "アイコン テーマの定義ファイルのパス。このパスは拡張フォルダーの相対パスであり、通常は './icons/awesome-icon-theme.json' です。", "migration.completed": "ユーザー設定に新しいテーマの設定が追加されました。{0} に利用可能なバックアップがあります。", "error.cannotloadtheme": "Unable to load {0}: {1}", - "reqarray": "Extension point `{0}` must be an array.", - "reqpath": "`contributes.{0}.path` に文字列が必要です。提供された値: {1}", - "invalid.path.1": "拡張機能のフォルダー ({2}) の中に `contributes.{0}.path` ({1}) が含まれている必要があります。これにより拡張を移植できなくなる可能性があります。", - "reqid": "`contributes.{0}.id` に文字列が必要です。提供された値: {1}", "error.cannotloadicontheme": "Unable to load {0}", - "error.cannotparseicontheme": "アイコン ファイルの解析中に問題が発生しました: {0}", "colorTheme": "ワークベンチで使用する配色テーマを指定します。", "colorThemeError": "テーマが不明、またはインストールされていません。", "iconTheme": "ワークベンチで使用するアイコンのテーマを指定します。'null' を指定するとファイル アイコンが表示されなくなります。", "noIconThemeDesc": "ファイル アイコンがありません", "iconThemeError": "ファイル アイコンのテーマが不明、またはインストールされていません。", "workbenchColors": "現在選択している配色テーマで配色を上書きします。", - "workbenchColors.deprecated": "この設定はもう試験的なものではなく、名前が 'workbench.colorCustomizations' に変更されています", - "workbenchColors.deprecatedDescription": "代わりに 'workbench.colorCustomizations' を使用してください", "editorColors": "現在選択している配色テーマで配色とフォント スタイルを上書きします。", "editorColors.comments": "コメントの色とスタイルを設定します", "editorColors.strings": "文字列リテラルの色とスタイルを設定します。", diff --git a/i18n/jpn/src/vs/workbench/services/workspace/node/workspaceEditingService.i18n.json b/i18n/jpn/src/vs/workbench/services/workspace/node/workspaceEditingService.i18n.json new file mode 100644 index 00000000000..963427aa46c --- /dev/null +++ b/i18n/jpn/src/vs/workbench/services/workspace/node/workspaceEditingService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "openWorkspaceConfigurationFile": "ワークスペースの構成ファイルを開く", + "close": "閉じる" +} \ No newline at end of file diff --git a/i18n/kor/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json b/i18n/kor/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json index c2b4c21ed25..3fd8cdf57b3 100644 --- a/i18n/kor/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json +++ b/i18n/kor/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json @@ -4,13 +4,6 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "activeEditorShort": "예: myFile.txt", - "activeEditorMedium": "예: myFolder/myFile.txt", - "activeEditorLong": "예: /Users/Development/myProject/myFolder/myFile.txt", - "rootName": "예: myFolder1, myFolder2, myFolder3", - "rootPath": "예: /Users/Development/myProject", - "folderName": "예: myFolder", - "folderPath": "예: /Users/Development/myFolder", "appName": "예: VS Code", "dirty": "활성 편집기가 더티인 경우 더티 표시기", "separator": "값이 있는 변수로 둘러싸인 경우에만 표시되는 조건부 구분 기호 (' - ')", diff --git a/i18n/kor/extensions/emmet/package.i18n.json b/i18n/kor/extensions/emmet/package.i18n.json index 0576d669667..7b41ed8ca2a 100644 --- a/i18n/kor/extensions/emmet/package.i18n.json +++ b/i18n/kor/extensions/emmet/package.i18n.json @@ -28,13 +28,6 @@ "command.incrementNumberByTen": "10씩 증가", "command.decrementNumberByTen": "10씩 감소", "emmetSyntaxProfiles": "지정된 구문에 대한 프로필을 정의하거나 특정 규칙이 포함된 고유한 프로필을 사용하세요.", - "emmetExclude": "Emmet 약어를 확장하면 안 되는 언어의 배열입니다.", - "emmetExtensionsPath": "Emmet 프로필 및 코드 조각이 포함된 폴더의 경로입니다.'", - "emmetShowExpandedAbbreviation": "확장된 emmet 약어를 제안으로 표시합니다.\n\"inMarkupAndStylesheetFilesOnly\" 옵션이 html, haml, jade, slim, xml, xsl, css, scss, sass, less 및 stylus에 적용됩니다.\n\"always\" 옵션이 마크업/css에 관계없이 파일의 모든 부분에 적용됩니다.", - "emmetShowAbbreviationSuggestions": "가능한 emmet 약어를 제안으로 표시합니다. 스타일시트에는 적용되지 않고 emmet.showExpandedAbbreviation이 \"never\"로 설정되어 있을 때도 적용되지 않습니다.", - "emmetIncludeLanguages": "기본 지원되지 않는 언어에서 emmet 약어를 사용합니다. 언어와 emmet 지원 언어 사이에 매핑을 추가합니다.\n예: {\"vue-html\": \"html\", \"javascript\": \"javascriptreact\"}", - "emmetVariables": "emmet 조각에 사용되는 변수", - "emmetTriggerExpansionOnTab": "사용하도록 설정하면 emmet 약어는 키를 눌렀을 때 확장됩니다.", "emmetPreferences": "Emmet의 일부 작업 및 해결 프로그램의 동작을 수정하는 데 사용되는 기본 설정입니다.", "emmetPreferencesIntUnit": "정수 값의 기본 단위", "emmetPreferencesFloatUnit": "부동 소수점 값의 기본 단위", @@ -43,6 +36,5 @@ "emmetPreferencesStylusAfter": "Stylus 파일에서 CSS 약어를 확장할 때 CSS 속성의 끝에 배치할 기호", "emmetPreferencesCssBetween": "CSS 약어를 확장할 때 CSS 속성 및 값 사이에 배치할 기호", "emmetPreferencesSassBetween": "Sass 파일에서 CSS 약어를 확장할 때 CSS 속성 및 값 사이에 배치할 기호", - "emmetPreferencesStylusBetween": "Stylus 파일에서 CSS 약어를 확장할 때 CSS 속성 및 값 사이에 배치할 기호", - "emmetShowSuggestionsAsSnippets": "True이면 emmet 제안이 코드 조각으로 표시되며 editor.snippetSuggestions 설정에 따라 코드 조각을 정렬할 수 있습니다." + "emmetPreferencesStylusBetween": "Stylus 파일에서 CSS 약어를 확장할 때 CSS 속성 및 값 사이에 배치할 기호" } \ No newline at end of file diff --git a/i18n/kor/extensions/git/out/commands.i18n.json b/i18n/kor/extensions/git/out/commands.i18n.json index 74283128949..1e7920f8a21 100644 --- a/i18n/kor/extensions/git/out/commands.i18n.json +++ b/i18n/kor/extensions/git/out/commands.i18n.json @@ -12,8 +12,8 @@ "cloning": "Git 리포지토리를 복제하는 중...", "openrepo": "리포지토리 열기", "proposeopen": "복제된 리포지토리를 여시겠습니까?", - "path to init": "폴더 경로", - "provide path": "Git 리포지토리를 초기화할 폴더 경로를 입력하세요.", + "init repo": "리포지토리 초기화", + "create repo": "리포지토리 초기화", "HEAD not available": "'{0}'의 HEAD 버전이 없습니다.", "confirm stage files with merge conflicts": "병합 충돌이 있는 {0} 파일을 스테이징하시겠습니까?", "confirm stage file with merge conflicts": "병합 충돌이 있는 {0}을(를) 스테이징하시겠습니까?", diff --git a/i18n/kor/extensions/git/out/repository.i18n.json b/i18n/kor/extensions/git/out/repository.i18n.json index c5c45af0062..6334a9e47ef 100644 --- a/i18n/kor/extensions/git/out/repository.i18n.json +++ b/i18n/kor/extensions/git/out/repository.i18n.json @@ -21,6 +21,8 @@ "deleted by us": "본인이 삭제함", "both added": "둘 다 추가됨", "both modified": "둘 다 수정됨", + "untracked, short": "U", + "modified, short": "M", "commit": "커밋", "merge changes": "변경 내용 병합", "staged changes": "스테이징된 변경 내용", diff --git a/i18n/kor/extensions/typescript/package.i18n.json b/i18n/kor/extensions/typescript/package.i18n.json index 9024eb3187d..7077f24ff61 100644 --- a/i18n/kor/extensions/typescript/package.i18n.json +++ b/i18n/kor/extensions/typescript/package.i18n.json @@ -44,7 +44,6 @@ "typescript.npm": "자동 입력 인식에 사용된 NPM 실행 파일 경로를 지정합니다. TypeScript >= 2.3.4가 필요합니다.", "typescript.check.npmIsInstalled": "자동 입력 인식에 대해 NPM이 설치되어 있는지 확인합니다.", "javascript.nameSuggestions": "JavaScript 제안 목록의 파일에서 고유한 이름 포함을 사용/사용 안 함으로 설정합니다.", - "typescript.tsc.autoDetect": "tsc 작업의 자동 검색을 켜거나 끕니다.", "typescript.problemMatchers.tsc.label": "TypeScript 문제", "typescript.problemMatchers.tscWatch.label": "TypeScript 문제(감시 모드)" } \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/contrib/find/browser/findWidget.i18n.json b/i18n/kor/src/vs/editor/contrib/find/browser/findWidget.i18n.json index c60d6d54352..e90c793f848 100644 --- a/i18n/kor/src/vs/editor/contrib/find/browser/findWidget.i18n.json +++ b/i18n/kor/src/vs/editor/contrib/find/browser/findWidget.i18n.json @@ -15,7 +15,6 @@ "label.replaceButton": "바꾸기", "label.replaceAllButton": "모두 바꾸기", "label.toggleReplaceButton": "바꾸기 모드 설정/해제", - "title.matchesCountLimit": "처음 999개의 결과가 강조 표시되지만 모든 찾기 작업은 전체 텍스트에 대해 수행됩니다.", "label.matchesLocation": "{0}/{1}", "label.noResults": "결과 없음" } \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/contrib/find/common/findController.i18n.json b/i18n/kor/src/vs/editor/contrib/find/common/findController.i18n.json index d0e58fa206e..7201d1ab08f 100644 --- a/i18n/kor/src/vs/editor/contrib/find/common/findController.i18n.json +++ b/i18n/kor/src/vs/editor/contrib/find/common/findController.i18n.json @@ -10,12 +10,6 @@ "nextSelectionMatchFindAction": "다음 선택 찾기", "previousSelectionMatchFindAction": "이전 선택 찾기", "startReplace": "바꾸기", - "addSelectionToNextFindMatch": "다음 일치 항목 찾기에 선택 항목 추가", - "addSelectionToPreviousFindMatch": "이전 일치 항목 찾기에 선택 항목 추가", - "moveSelectionToNextFindMatch": "다음 일치 항목 찾기로 마지막 선택 항목 이동", - "moveSelectionToPreviousFindMatch": "마지막 선택 항목을 이전 일치 항목 찾기로 이동", - "selectAllOccurrencesOfFindMatch": "일치 항목 찾기의 모든 항목 선택", - "changeAll.label": "모든 항목 변경", "showNextFindTermAction": "다음 검색어 표시", "showPreviousFindTermAction": "이전 검색어 표시" } \ No newline at end of file diff --git a/i18n/kor/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json b/i18n/kor/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json index 2cd42573465..af6f5d6235c 100644 --- a/i18n/kor/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json +++ b/i18n/kor/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json @@ -6,5 +6,11 @@ { "mutlicursor.insertAbove": "위에 커서 추가", "mutlicursor.insertBelow": "아래에 커서 추가", - "mutlicursor.insertAtEndOfEachLineSelected": "줄 끝에 커서 추가" + "mutlicursor.insertAtEndOfEachLineSelected": "줄 끝에 커서 추가", + "addSelectionToNextFindMatch": "다음 일치 항목 찾기에 선택 항목 추가", + "addSelectionToPreviousFindMatch": "이전 일치 항목 찾기에 선택 항목 추가", + "moveSelectionToNextFindMatch": "다음 일치 항목 찾기로 마지막 선택 항목 이동", + "moveSelectionToPreviousFindMatch": "마지막 선택 항목을 이전 일치 항목 찾기로 이동", + "selectAllOccurrencesOfFindMatch": "일치 항목 찾기의 모든 항목 선택", + "changeAll.label": "모든 항목 변경" } \ No newline at end of file diff --git a/i18n/kor/src/vs/platform/theme/common/colorRegistry.i18n.json b/i18n/kor/src/vs/platform/theme/common/colorRegistry.i18n.json index 98c0d356e83..8e8997c7ddf 100644 --- a/i18n/kor/src/vs/platform/theme/common/colorRegistry.i18n.json +++ b/i18n/kor/src/vs/platform/theme/common/colorRegistry.i18n.json @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "invalid.color": "잘못된 색 형식입니다. #RGB, #RGBA, #RRGGBB 또는 #RRGGBBAA를 사용하세요.", "schema.colors": "워크벤치에서 사용되는 색입니다.", "foreground": "전체 전경색입니다. 이 색은 구성 요소에서 재정의하지 않은 경우에만 사용됩니다.", "errorForeground": "오류 메시지에 대한 전체 전경색입니다. 이 색은 구성 요소에서 재정의하지 않은 경우에만 사용됩니다.", 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 b414854fcac..b3a4fc27abc 100644 --- a/i18n/kor/src/vs/workbench/browser/actions/workspaceActions.i18n.json +++ b/i18n/kor/src/vs/workbench/browser/actions/workspaceActions.i18n.json @@ -13,6 +13,7 @@ "select": "선택(&&S)", "selectWorkspace": "작업 영역 폴더 선택", "removeFolderFromWorkspace": "작업 영역에서 폴더 삭제", + "openFolderSettings": "폴더 설정 열기", "saveWorkspaceAsAction": "작업 영역을 다른 이름으로 저장", "save": "저장(&&S)", "saveWorkspace": "작업 영역 저장", diff --git a/i18n/kor/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json b/i18n/kor/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json index 775ac027185..6ab66076b97 100644 --- a/i18n/kor/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json +++ b/i18n/kor/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json @@ -5,6 +5,5 @@ // Do not edit this file. It is machine generated. { "hideActivitBar": "작업 막대 숨기기", - "activityBarAriaLabel": "활성 뷰 전환기", "globalActions": "전역 작업" } \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/browser/parts/compositebar/compositeBar.i18n.json b/i18n/kor/src/vs/workbench/browser/parts/compositebar/compositeBar.i18n.json new file mode 100644 index 00000000000..f515e983418 --- /dev/null +++ b/i18n/kor/src/vs/workbench/browser/parts/compositebar/compositeBar.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. +{ + "activityBarAriaLabel": "활성 뷰 전환기" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/browser/parts/compositebar/compositeBarActions.i18n.json b/i18n/kor/src/vs/workbench/browser/parts/compositebar/compositeBarActions.i18n.json new file mode 100644 index 00000000000..882b528591a --- /dev/null +++ b/i18n/kor/src/vs/workbench/browser/parts/compositebar/compositeBarActions.i18n.json @@ -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. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "badgeTitle": "{0} - {1}", + "additionalViews": "추가 뷰", + "numberBadge": "{0}({1})", + "manageExtension": "확장 관리", + "titleKeybinding": "{0}({1})", + "toggle": "뷰 고정 전환" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json b/i18n/kor/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json index 29467820136..32f439e8de1 100644 --- a/i18n/kor/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json +++ b/i18n/kor/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json @@ -12,5 +12,6 @@ "groupTwoPicker": "두 번째 그룹에 편집기 표시", "groupThreePicker": "세 번째 그룹에 편집기 표시", "allEditorsPicker": "열려 있는 모든 편집기 표시", - "view": "보기" + "view": "보기", + "file": "파일" } \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json b/i18n/kor/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json index 1b2e94b2c53..1a622afa7d4 100644 --- a/i18n/kor/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json +++ b/i18n/kor/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json @@ -4,6 +4,5 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "canNotRun": "'{0}' 명령은 현재 사용하도록 설정되지 않아 실행할 수 없습니다.", "manageExtension": "확장 관리" } \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/electron-browser/main.contribution.i18n.json b/i18n/kor/src/vs/workbench/electron-browser/main.contribution.i18n.json index 7c9f66647d9..22baa2ccddf 100644 --- a/i18n/kor/src/vs/workbench/electron-browser/main.contribution.i18n.json +++ b/i18n/kor/src/vs/workbench/electron-browser/main.contribution.i18n.json @@ -10,18 +10,14 @@ "workspaces": "작업 영역", "developer": "개발자", "showEditorTabs": "열려 있는 편집기를 탭에서 표시할지 여부를 제어합니다.", - "workbench.editor.labelFormat.default": "파일 이름을 표시합니다. 탭이 사용하도록 설정되어 있고 하나의 그룹에서 파일 2개의 이름이 동일하면, 각 파일 경로의 특정 섹션이 추가됩니다. 탭이 사용하도록 설정되어 있지 않으면, 작업 영역 루트에 대한 경로는 편집기가 활성 상태일 때 표시됩니다.", "workbench.editor.labelFormat.short": "디렉터리 이름 앞에 오는 파일의 이름을 표시합니다.", - "workbench.editor.labelFormat.medium": "작업 영역 루트를 기준으로 하는 상대 경로 앞에 오는 파일의 이름을 표시합니다.", "workbench.editor.labelFormat.long": "절대 경로 앞에 오는 파일의 이름을 표시합니다.", "tabDescription": "편집기의 레이블 형식을 제어합니다. 예를 들어 이 설정을 변경하면 파일의 위치를 더 쉽게 파악할 수 있습니다.:\n- 짧게: 'parent'\n- 중간: 'workspace/src/parent'\n- 길게: '/home/user/workspace/src/parent'\n- 기본값: '.../parent', 다른 탭이 동일한 제목을 공유하거나, 탭을 사용하지 않도록 설정한 경우 작업 영역 상대 경로", "editorTabCloseButton": "편집기의 탭 닫기 단추의 위치를 제어하거나 'off'로 설정된 경우 이 단추를 사용하지 않도록 설정합니다.", "showIcons": "열린 편집기를 아이콘과 함께 표시할지 여부를 제어합니다. 이를 위해서는 아이콘 테마도 사용하도록 설정해야 합니다.", "enablePreview": "열려 있는 편집기를 미리 보기로 표시할지 여부를 제어합니다. 미리 보기 편집기는 유지된 상태까지(예: 두 번 클릭 또는 편집을 통해) 다시 사용되며 기울임꼴 글꼴 스타일로 표시됩니다.", "enablePreviewFromQuickOpen": "Quick Open에서 연 편집기를 미리 보기로 표시할지 여부를 제어합니다. 미리 보기 편집기는 유지된 상태까지(예: 두 번 클릭 또는 편집을 통해) 다시 사용됩니다.", - "editorOpenPositioning": "편집기가 열리는 위치를 제어합니다. 현재 활성 편집기의 왼쪽 또는 오른쪽에서 편집기를 열려면 '왼쪽' 또는 '오른쪽'을 선택합니다. 현재 활성 편집기와 독립적으로 편집기를 열려면 '처음' 또는 '마지막'을 선택합니다.", "revealIfOpen": "편집기를 여는 경우 보이는 그룹 중 하나에 표시할지 여부를 제어합니다. 사용하지 않도록 설정할 경우 편집기가 기본적으로 현재 활성 편집기 그룹에 열립니다. 사용하도록 설정할 경우 현재 활성 편집기 그룹에서 편집기가 다시 열리지 않고 이미 열린 편집기가 표시됩니다. 강제로 편집기가 특정 그룹에서 열리거나 현재 활성 그룹 옆에 열리도록 하는 등의 일부 경우에는 이 설정이 무시됩니다.", - "commandHistory": "명령 팔레트 기록에 최근 사용 명령을 몇 개 유지할지 결정합니다. 0으로 설정하면 명령 기록을 사용하지 않습니다.", "preserveInput": "다음에 열 때 마지막으로 명령 팔레트에 입력한 내용을 복원할지 결정합니다.", "closeOnFocusLost": "Quick Open가 포커스를 잃으면 자동으로 닫을지 여부를 제어합니다.", "openDefaultSettings": "설정을 열면 모든 기본 설정을 표시하는 편집기도 열리는지 여부를 제어합니다.", @@ -50,7 +46,6 @@ "restoreWindows": "다시 시작한 이후에 창을 다시 여는 방법을 설정합니다. 'none'을 선택하면 항상 빈 작업 영역으로 시작하고 'one'을 선택하면 마지막으로 작업한 창이 다시 열리고 'folders'를 선택하면 열었던 폴더가 포함된 모든 창이 다시 열리며 'all'을 선택하면 지난 세션의 모든 창이 다시 열립니다.", "restoreFullscreen": "창이 전체 화면 모드에서 종료된 경우 창을 전체 화면 모드로 복원할지 여부를 제어합니다.", "zoomLevel": "창의 확대/축소 수준을 조정합니다. 원래 크기는 0이고 각 상한 증분(예: 1) 또는 하한 증분(예: -1)은 20% 더 크거나 더 작게 확대/축소하는 것을 나타냅니다. 10진수를 입력하여 확대/축소 수준을 세부적으로 조정할 수도 있습니다.", - "title": "활성 편집기를 기반으로 창 제목을 제어합니다. 변수는 컨텍스트를 기반으로 대체됩니다. \n${activeEditorShort}: 예: myFile.txt\n${activeEditorMedium}: 예: myFolder/myFile.txt\n${activeEditorLong}: 예: /Users/Development/myProject/myFolder/myFile.txt\n${folderName}: 예: myFolder\n${folderPath}: 예: /Users/Development/myFolder\n${rootName}: 예: myFolder1, myFolder2, myFolder3\n${rootPath}: 예: /Users/Development/myWorkspace\n${appName}: 예: VS Code\n${dirty}: 활성 편집기가 더티인 경우 더티 표시기\n${separator}: 값이 있는 변수로 둘러싸인 경우에만 표시되는 조건부 구분 기호(\" - \")", "window.newWindowDimensions.default": "화면 가운데에서 새 창을 엽니다.", "window.newWindowDimensions.inherit": "마지막 활성 창과 동일한 크기로 새 창을 엽니다.", "window.newWindowDimensions.maximized": "최대화된 새 창을 엽니다.", diff --git a/i18n/kor/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json b/i18n/kor/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json index 674839cc069..753a861d411 100644 --- a/i18n/kor/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json +++ b/i18n/kor/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json @@ -6,6 +6,8 @@ { "entryAriaLabel": "{0}, 디버그", "debugAriaLabel": "실행할 시작 구성의 이름을 입력하세요.", + "addConfigTo": "구성 추가 ({0})...", + "addConfiguration": "구성 추가...", "noConfigurationsMatching": "일치하는 디버그 구성 없음", "noConfigurationsFound": "디버그 구성을 찾을 수 없습니다. 'launch.json' 파일을 만드세요." } \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/debug/browser/debugStatus.i18n.json b/i18n/kor/src/vs/workbench/parts/debug/browser/debugStatus.i18n.json new file mode 100644 index 00000000000..416944e28fd --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/debug/browser/debugStatus.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. +{ + "debug": "디버그" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/debug/browser/debugViewlet.i18n.json b/i18n/kor/src/vs/workbench/parts/debug/browser/debugViewlet.i18n.json new file mode 100644 index 00000000000..8b6ad71cd4e --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/debug/browser/debugViewlet.i18n.json @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json b/i18n/kor/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json index 2b0632b1635..0db91f83b2f 100644 --- a/i18n/kor/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json +++ b/i18n/kor/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json @@ -15,7 +15,6 @@ "vscode.extension.contributes.debuggers.initialConfigurations": "초기 'launch.json'을 생성하기 위한 구성입니다.", "vscode.extension.contributes.debuggers.languages": "디버그 확장이 \"기본 디버거\"로 간주될 수 있는 언어 목록입니다.", "vscode.extension.contributes.debuggers.adapterExecutableCommand": "지정하는 경우 VS Code에서 이 명령을 호출하여 디버그 어댑터의 실행 파일 경로 및 전달할 인수를 확인합니다.", - "vscode.extension.contributes.debuggers.startSessionCommand": "지정하는 경우 VS Code에서 이 확장을 대상으로 하는 \"디버그\" 또는 \"실행\" 작업에 대해 이 명령을 호출합니다.", "vscode.extension.contributes.debuggers.configurationSnippets": "'launch.json'에 새 구성을 추가하는 코드 조각입니다.", "vscode.extension.contributes.debuggers.configurationAttributes": "'launch.json'의 유효성 검사를 위한 JSON 스키마 구성입니다.", "vscode.extension.contributes.debuggers.windows": "Windows 특정 설정", diff --git a/i18n/kor/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json b/i18n/kor/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json index 7b4fa4d91c9..13125eab426 100644 --- a/i18n/kor/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json +++ b/i18n/kor/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json @@ -12,10 +12,7 @@ "breakpointRemoved": "파일 {1}, 줄 {0}에서 중단점이 제거되었습니다.", "compoundMustHaveConfigurations": "여러 구성을 시작하려면 복합에 \"configurations\" 특성 집합이 있어야 합니다.", "configMissing": "'{0}' 구성이 'launch.json'에 없습니다.", - "debugRequestNotSupported": "선택한 디버그 구성에 지원되지 않는 특성 값 `{0}`: '{1}'이(가) 있습니다.", - "debugRequesMissing": "선택한 디버그 구성에 특성 '{0}'이(가) 없습니다.", "debugTypeNotSupported": "구성된 디버그 형식 '{0}'은(는) 지원되지 않습니다.", - "debugTypeMissing": "선택한 시작 구성에 대한 'type' 속성이 없습니다.", "preLaunchTaskErrors": "preLaunchTask '{0}' 진행 중에 빌드 오류가 감지되었습니다.", "preLaunchTaskError": "preLaunchTask '{0}' 진행 중에 빌드 오류가 감지되었습니다.", "preLaunchTaskExitCode": "preLaunchTask '{0}'이(가) {1} 종료 코드와 함께 종료되었습니다.", diff --git a/i18n/kor/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json b/i18n/kor/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json index 9c417b8c7dd..1da0363bd93 100644 --- a/i18n/kor/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json +++ b/i18n/kor/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json @@ -7,6 +7,5 @@ "stateCapture": "개체 상태는 첫 번재 평가에서 캡처됩니다.", "replVariableAriaLabel": "{0} 변수에 {1} 값이 있습니다. 읽기 평가 인쇄 루프, 디버그", "replExpressionAriaLabel": "{0} 식에 {1} 값이 있습니다. 읽기 평가 인쇄 루프, 디버그", - "replValueOutputAriaLabel": "{0}, 읽기 평가 인쇄 루프, 디버그", - "replKeyValueOutputAriaLabel": "출력 변수 {0}에 {1} 값이 있습니다. 읽기 평가 인쇄 루프, 디버그" + "replValueOutputAriaLabel": "{0}, 읽기 평가 인쇄 루프, 디버그" } \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/files/browser/fileActions.i18n.json b/i18n/kor/src/vs/workbench/parts/files/browser/fileActions.i18n.json index 8bcb4133777..1f93f5ae2c6 100644 --- a/i18n/kor/src/vs/workbench/parts/files/browser/fileActions.i18n.json +++ b/i18n/kor/src/vs/workbench/parts/files/browser/fileActions.i18n.json @@ -37,8 +37,6 @@ "openToSide": "측면에서 열기", "compareSource": "비교를 위해 선택", "globalCompareFile": "활성 파일을 다음과 비교...", - "pickHistory": "비교할 이전에 연 파일 선택", - "unableToFileToCompare": "선택한 파일을 '{0}'과(와) 비교할 수 없습니다.", "openFileToCompare": "첫 번째 파일을 열어서 다른 파일과 비교합니다.", "compareWith": "'{0}'과(와) '{1}' 비교", "compareFiles": "파일 비교", @@ -47,7 +45,6 @@ "saveAs": "다른 이름으로 저장...", "saveAll": "모두 저장", "saveAllInGroup": "그룹의 모든 항목 저장", - "saveFiles": "더티 파일 저장", "revert": "파일 되돌리기", "focusOpenEditors": "열려 있는 편집기 뷰에 포커스", "focusFilesExplorer": "파일 탐색기에 포커스", diff --git a/i18n/kor/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json b/i18n/kor/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json index 643334c6aba..42fb83a81b7 100644 --- a/i18n/kor/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json +++ b/i18n/kor/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json @@ -6,6 +6,6 @@ { "noWorkspace": "열린 폴더 없음", "explorerSection": "파일 탐색기 섹션", - "noWorkspaceHelp": "아직 폴더를 열지 않았습니다.", + "noFolderHelp": "아직 폴더를 열지 않았습니다.", "openFolder": "폴더 열기" } \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/markers/browser/markersFileDecorations.i18n.json b/i18n/kor/src/vs/workbench/parts/markers/browser/markersFileDecorations.i18n.json new file mode 100644 index 00000000000..9a5ca02f322 --- /dev/null +++ b/i18n/kor/src/vs/workbench/parts/markers/browser/markersFileDecorations.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. +{ + "label": "문제" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json b/i18n/kor/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json index 393b61b671f..fa4d6d2e024 100644 --- a/i18n/kor/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json +++ b/i18n/kor/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json @@ -5,7 +5,6 @@ // Do not edit this file. It is machine generated. { "emptyUserSettingsHeader": "설정을 여기에 넣어서 기본 설정을 덮어씁니다.", - "errorInvalidConfiguration": "설정에 쓸 수 없습니다. 파일에서 오류/경고를 해결하고 다시 시도하세요.", "emptyWorkspaceSettingsHeader": "설정을 여기에 넣어서 사용자 설정을 덮어씁니다.", "emptyFolderSettingsHeader": "폴더 설정을 여기에 넣어서 작업 영역 설정을 덮어씁니다.", "defaultFolderSettingsTitle": "기본 폴더 설정", diff --git a/i18n/kor/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json b/i18n/kor/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json index 3643111bada..a8785cacb78 100644 --- a/i18n/kor/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json +++ b/i18n/kor/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json @@ -13,7 +13,6 @@ "actionNotEnabled": "'{0}' 명령은 현재 컨텍스트에서 사용할 수 없습니다.", "recentlyUsed": "최근에 사용한 항목", "morecCommands": "기타 명령", - "commandLabel": "{0}: {1}", "cat.title": "{0}: {1}", "noCommandsMatching": "일치하는 명령 없음" } \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/search/browser/search.contribution.i18n.json b/i18n/kor/src/vs/workbench/parts/search/browser/search.contribution.i18n.json index eda87691830..4b214694baf 100644 --- a/i18n/kor/src/vs/workbench/parts/search/browser/search.contribution.i18n.json +++ b/i18n/kor/src/vs/workbench/parts/search/browser/search.contribution.i18n.json @@ -17,7 +17,6 @@ "exclude": "검색에서 파일 및 폴더를 제외하도록 GLOB 패턴을 구성합니다. files.exclude 설정에서 모든 GLOB 패턴을 상속합니다.", "exclude.boolean": "파일 경로를 일치시킬 GLOB 패턴입니다. 패턴을 사용하거나 사용하지 않도록 설정하려면 true 또는 false로 설정하세요.", "exclude.when": "일치하는 파일의 형제에 대한 추가 검사입니다. $(basename)을 일치하는 파일 이름에 대한 변수로 사용하세요.", - "useRipgrep": "텍스트 검색에서 ripgrep 사용 여부를 제어합니다.", "useIgnoreFilesByDefault": "새 작업 영역에서 검색할 때 기본적으로 .gitignore 파일 및 .ignore 파일을 사용할지 여부를 제어합니다.", "search.quickOpen.includeSymbols": "Quick Open에 대한 파일 결과에 전역 기호 검색 결과를 포함하도록 구성합니다." } \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/search/browser/searchActions.i18n.json b/i18n/kor/src/vs/workbench/parts/search/browser/searchActions.i18n.json index d8c432f8bb0..ffeb7ea9a94 100644 --- a/i18n/kor/src/vs/workbench/parts/search/browser/searchActions.i18n.json +++ b/i18n/kor/src/vs/workbench/parts/search/browser/searchActions.i18n.json @@ -19,7 +19,6 @@ "ClearSearchResultsAction.label": "검색 결과 지우기", "FocusNextSearchResult.label": "다음 검색 결과에 포커스", "FocusPreviousSearchResult.label": "이전 검색 결과에 포커스", - "RemoveAction.label": "제거", "file.replaceAll.label": "모두 바꾸기", "match.replace.label": "바꾸기" } \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json b/i18n/kor/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json index b80c311ab32..0bac25f2467 100644 --- a/i18n/kor/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json +++ b/i18n/kor/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json @@ -10,8 +10,8 @@ "vscode.extension.contributes.snippets": "코드 조각을 적용합니다.", "vscode.extension.contributes.snippets-language": "이 코드 조각이 적용되는 언어 식별자입니다.", "vscode.extension.contributes.snippets-path": "코드 조각 파일의 경로입니다. 이 경로는 확장 폴더의 상대 경로이며 일반적으로 './snippets/'로 시작합니다.", - "badFile": "코드 조각 파일 \"{0}\"을(를) 읽을 수 없습니다.", "badVariableUse": "\"{0}\"-snippet은 snippet-variables 및 snippet-placeholders와 혼동하기 쉽습니다. 자세한 내용은\n https://code.visualstudio.com/docs/editor/userdefinedsnippets#_snippet-syntax를 참조하세요.", + "badFile": "코드 조각 파일 \"{0}\"을(를) 읽을 수 없습니다.", "source.snippet": "사용자 코드 조각", "detail.snippet": "{0}({1})", "snippetSuggest.longLabel": "{0}, {1}" diff --git a/i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json b/i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json index 110ad443882..9415bbb9140 100644 --- a/i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json +++ b/i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json @@ -16,7 +16,6 @@ "workbench.action.terminal.new.short": "새 터미널", "workbench.action.terminal.focus": "터미널에 포커스", "workbench.action.terminal.focusNext": "다음 터미널에 포커스", - "workbench.action.terminal.focusAtIndex": "{0} 터미널로 포커스 이동", "workbench.action.terminal.focusPrevious": "이전 터미널에 포커스", "workbench.action.terminal.paste": "활성 터미널에 붙여넣기", "workbench.action.terminal.DefaultShell": "기본 셸 선택", diff --git a/i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json b/i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json index 9fe74c319a1..08eba97a0dc 100644 --- a/i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json +++ b/i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json @@ -5,7 +5,6 @@ // Do not edit this file. It is machine generated. { "copy": "복사", - "createNewTerminal": "새 터미널", "paste": "붙여넣기", "selectAll": "모두 선택", "clear": "지우기" diff --git a/i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json b/i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json index 81c70b66b79..b21bf5b25fa 100644 --- a/i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json +++ b/i18n/kor/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json @@ -10,6 +10,5 @@ "never again": "다시 표시 안 함", "terminal.integrated.chooseWindowsShell": "기본으로 설정할 터미널 셸을 선택하세요. 나중에 설정에서 이 셸을 변경할 수 있습니다.", "terminalService.terminalCloseConfirmationSingular": "활성 터미널 세션이 있습니다. 종료할까요?", - "terminalService.terminalCloseConfirmationPlural": "{0}개의 활성 터미널 세션이 있습니다. 종료할까요?", - "yes": "예" + "terminalService.terminalCloseConfirmationPlural": "{0}개의 활성 터미널 세션이 있습니다. 종료할까요?" } \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/services/configuration/common/configurationExtensionPoint.i18n.json b/i18n/kor/src/vs/workbench/services/configuration/common/configurationExtensionPoint.i18n.json new file mode 100644 index 00000000000..4fdc20a33c2 --- /dev/null +++ b/i18n/kor/src/vs/workbench/services/configuration/common/configurationExtensionPoint.i18n.json @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "vscode.extension.contributes.configuration.title": "설정을 요약합니다. 이 레이블은 설정 파일에서 구분 주석으로 사용됩니다.", + "vscode.extension.contributes.configuration.properties": "구성 속성에 대한 설명입니다.", + "scope.window.description": "[사용자] 설정 또는 [작업 영역] 설정에서 구성할 수 있는 창 특정 구성입니다.", + "scope.resource.description": "사용자, 작업 영역 또는 폴더 설정에서 구성할 수 있는 리소스 특정 구성", + "scope.description": "구성이 적용되는 범위입니다. 사용 가능 범위는 '창'과 '리소스'입니다.", + "vscode.extension.contributes.configuration": "구성 설정을 적용합니다.", + "invalid.title": "'configuration.title'은 문자열이어야 합니다.", + "vscode.extension.contributes.defaultConfiguration": "언어별로 기본 편집기 구성 설정을 적용합니다.", + "invalid.properties": "'configuration.properties'는 개체여야 합니다.", + "invalid.allOf": "'configuration.allOf'는 사용되지 않으며 더 이상 사용해서는 안됩니다. 대신 여러 구성 섹션을 배열로 'configuration' 기여 지점에 전달하세요.", + "workspaceConfig.folders.description": "작업 영역에 로드되는 폴더 목록입니다.", + "workspaceConfig.path.description": "파일 경로입니다. 예: `/root/folderA` 또는 `./folderA`(작업 영역 파일의 위치를 기준으로 확인할 상대 경로인 경우)", + "workspaceConfig.name.description": "폴더에 대한 선택적 이름입니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/services/editor/common/editorService.i18n.json b/i18n/kor/src/vs/workbench/services/editor/common/editorService.i18n.json new file mode 100644 index 00000000000..50e968f8ee3 --- /dev/null +++ b/i18n/kor/src/vs/workbench/services/editor/common/editorService.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. +{ + "compareLabels": "{0} ↔ {1}" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/services/themes/electron-browser/colorThemeStore.i18n.json b/i18n/kor/src/vs/workbench/services/themes/electron-browser/colorThemeStore.i18n.json new file mode 100644 index 00000000000..4bcd1e891d3 --- /dev/null +++ b/i18n/kor/src/vs/workbench/services/themes/electron-browser/colorThemeStore.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "vscode.extension.contributes.themes": "Contributes textmate color themes.", + "vscode.extension.contributes.themes.id": "사용자 설정에 사용된 아이콘 테마의 ID입니다.", + "vscode.extension.contributes.themes.label": "UI에 표시되는 색 테마의 레이블입니다.", + "vscode.extension.contributes.themes.uiTheme": "편집기 주변의 색을 정의하는 기본 테마입니다. 'vs'는 밝은색 테마이고, 'vs-dark'는 어두운색 테마입니다. 'hc-black'은 어두운 고대비 테마입니다.", + "vscode.extension.contributes.themes.path": "tmTheme 파일의 경로입니다. 확장 폴더의 상대 경로이며 일반적으로 './themes/themeFile.tmTheme'입니다.", + "reqarray": "Extension point `{0}` must be an array.", + "reqpath": "`contributes.{0}.path`에 문자열이 필요합니다. 제공된 값: {1}", + "invalid.path.1": "확장 폴더({2})에 포함할 `contributes.{0}.path`({1})가 필요합니다. 확장이 이식 불가능해질 수 있습니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.i18n.json b/i18n/kor/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.i18n.json new file mode 100644 index 00000000000..b0f9faf6b52 --- /dev/null +++ b/i18n/kor/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.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. +{ + "error.cannotparseicontheme": "Problems parsing file icons file: {0}" +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.i18n.json b/i18n/kor/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.i18n.json new file mode 100644 index 00000000000..10f74d38b89 --- /dev/null +++ b/i18n/kor/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "vscode.extension.contributes.iconThemes": "Contributes file icon themes.", + "vscode.extension.contributes.iconThemes.id": "사용자 설정에 사용된 아이콘 테마의 ID입니다.", + "vscode.extension.contributes.iconThemes.label": "UI에 표시된 아이콘 테마의 레이블입니다.", + "vscode.extension.contributes.iconThemes.path": "아이콘 테마 정의 파일의 경로입니다. 확장 폴더의 상대 경로이며 일반적으로 './icons/awesome-icon-theme.json'입니다.", + "reqarray": "Extension point `{0}` must be an array.", + "reqpath": "`contributes.{0}.path`에 문자열이 필요합니다. 제공된 값: {1}", + "reqid": "`contributes.{0}.id`에 문자열이 필요합니다. 제공된 값: {1}", + "invalid.path.1": "확장 폴더({2})에 포함할 `contributes.{0}.path`({1})가 필요합니다. 확장이 이식 불가능해질 수 있습니다." +} \ No newline at end of file diff --git a/i18n/kor/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json b/i18n/kor/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json index 3f05d41e6bb..c7ed3a3fe46 100644 --- a/i18n/kor/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json +++ b/i18n/kor/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json @@ -4,31 +4,15 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "vscode.extension.contributes.themes": "Contributes textmate color themes.", - "vscode.extension.contributes.themes.id": "사용자 설정에 사용된 아이콘 테마의 ID입니다.", - "vscode.extension.contributes.themes.label": "UI에 표시되는 색 테마의 레이블입니다.", - "vscode.extension.contributes.themes.uiTheme": "편집기 주변의 색을 정의하는 기본 테마입니다. 'vs'는 밝은색 테마이고, 'vs-dark'는 어두운색 테마입니다. 'hc-black'은 어두운 고대비 테마입니다.", - "vscode.extension.contributes.themes.path": "tmTheme 파일의 경로입니다. 확장 폴더의 상대 경로이며 일반적으로 './themes/themeFile.tmTheme'입니다.", - "vscode.extension.contributes.iconThemes": "Contributes file icon themes.", - "vscode.extension.contributes.iconThemes.id": "사용자 설정에 사용된 아이콘 테마의 ID입니다.", - "vscode.extension.contributes.iconThemes.label": "UI에 표시된 아이콘 테마의 레이블입니다.", - "vscode.extension.contributes.iconThemes.path": "아이콘 테마 정의 파일의 경로입니다. 확장 폴더의 상대 경로이며 일반적으로 './icons/awesome-icon-theme.json'입니다.", "migration.completed": "새 테마 설정이 사용자 설정에 추가되었습니다. {0}에서 백업을 사용할 수 있습니다.", "error.cannotloadtheme": "Unable to load {0}: {1}", - "reqarray": "Extension point `{0}` must be an array.", - "reqpath": "`contributes.{0}.path`에 문자열이 필요합니다. 제공된 값: {1}", - "invalid.path.1": "확장 폴더({2})에 포함할 `contributes.{0}.path`({1})가 필요합니다. 확장이 이식 불가능해질 수 있습니다.", - "reqid": "`contributes.{0}.id`에 문자열이 필요합니다. 제공된 값: {1}", "error.cannotloadicontheme": "Unable to load {0}", - "error.cannotparseicontheme": "Problems parsing file icons file: {0}", "colorTheme": "Specifies the color theme used in the workbench.", "colorThemeError": "Theme is unknown or not installed.", "iconTheme": "워크벤치에서 사용되는 아이콘 테마를 지정합니다. 'null'로 지정하면 파일 아이콘을 표시하지 않습니다.", "noIconThemeDesc": "No file icons", "iconThemeError": "File icon theme is unknown or not installed.", "workbenchColors": "현재 선택한 색 테마에서 색을 재정의합니다.", - "workbenchColors.deprecated": "이 설정은 더 이상 실험적 설정이 아니며 이름이\n 'workbench.colorCustomizations'로 변경되었습니다.", - "workbenchColors.deprecatedDescription": "대신 'workbench.colorCustomizations'를 사용합니다.", "editorColors": "현재 선택된 색 테마에서 편집기 색상과 글꼴 스타일을 재정의합니다.", "editorColors.comments": "주석의 색 및 스타일을 설정합니다.", "editorColors.strings": "문자열 리터럴의 색 및 스타일을 설정합니다.", diff --git a/i18n/kor/src/vs/workbench/services/workspace/node/workspaceEditingService.i18n.json b/i18n/kor/src/vs/workbench/services/workspace/node/workspaceEditingService.i18n.json new file mode 100644 index 00000000000..32a5b42e309 --- /dev/null +++ b/i18n/kor/src/vs/workbench/services/workspace/node/workspaceEditingService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "openWorkspaceConfigurationFile": "작업 영역 구성 파일 열기", + "close": "닫기" +} \ No newline at end of file diff --git a/i18n/ptb/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json b/i18n/ptb/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json index ab0c10765f7..c86627bbf9c 100644 --- a/i18n/ptb/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json +++ b/i18n/ptb/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json @@ -4,13 +4,13 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "activeEditorShort": "por exemplo meuArquivo.txt", - "activeEditorMedium": "e.g. minhaPasta/meuArquivo.txt", - "activeEditorLong": "por exemplo /Usuários/Desenvolvimento/meuProjeto/minhaPasta/meuArquivo/txt", - "rootName": "por exemplo: minhaPasta1, minhaPasta2, minhaPasta3", - "rootPath": "por exemplo /Usuários/desenvolvimento/meuProjeto", - "folderName": "por exemplo: minhaPasta", - "folderPath": "por exemplo, /Users/Development/myFolder", + "activeEditorShort": "o nome do arquivo (por exemplo, MyFile. txt)", + "activeEditorMedium": "o caminho do arquivo relativo à pasta de trabalho (por exemplo, myFolder/myFile.txt)", + "activeEditorLong": "o caminho completo do arquivo (por exemplo, /Users/Development/myProject/myFolder/myFile.txt)", + "rootName": "nome da área de trabalho (por exemplo, minhaPasta ou minhaAreadeTrabalho)", + "rootPath": "caminho do arquivo da área de trabalho (por exemplo, /Usuarios/Desenvolvimento/minhaAreadeTrabalho)", + "folderName": "nome do diretório da área de trabalho onde arquivo está localizado (por exemplo, myFolder)", + "folderPath": "caminho do arquivo no diretório da área de trabalho onde o arquivo está localizado (por exemplo, /Users/Development/myFolder)", "appName": "e.g. VS Code", "dirty": "Um indicador de alteração se o editor ativo foi alterado", "separator": "um separador condicional (' - ') que somente é mostrado quando envolvido por variáveis com valores", diff --git a/i18n/ptb/extensions/emmet/package.i18n.json b/i18n/ptb/extensions/emmet/package.i18n.json index 07589c8fc9d..582e68c8fa1 100644 --- a/i18n/ptb/extensions/emmet/package.i18n.json +++ b/i18n/ptb/extensions/emmet/package.i18n.json @@ -28,13 +28,6 @@ "command.incrementNumberByTen": "Incremento de 10", "command.decrementNumberByTen": "Decrementar por 10", "emmetSyntaxProfiles": "Definir o perfil para a sintaxe especificada ou usar seu próprio perfil com regras específicas.", - "emmetExclude": "Uma matriz de línguagens onde abreviaturas emmet não devem ser expandidas.", - "emmetExtensionsPath": "Caminho para uma pasta que contém os perfis de emmet e trechos de código.'", - "emmetShowExpandedAbbreviation": "Mostra abreviaturas emmet expandidas como sugestões.\nA opção \"inMarkupAndStylesheetFilesOnly\" aplica-se a html, haml, jade, slim, xml, xsl, css, scss, sass, less e stylus.\nA opção \"always\" se aplica a todas as partes do arquivo independentemente de marcação/css.", - "emmetShowAbbreviationSuggestions": "Mostra possíveis abreviaturas emmet como sugestões. Não aplicável em folhas de estilo ou quando emmet.showExpandedAbbreviation é definido como \"nunca\".", - "emmetIncludeLanguages": "Habilita as abreviaturas do emmet em idiomas que não são suportados por padrão. Adicione um mapeamento aqui entre a linguagem e a linguagem emmet suportada.\n Por exemplo: {\"vue-html\": \"html\", \"javascript\": \"javascriptreact\"}", - "emmetVariables": "Variáveis a serem usadas em trechos de código emmet", - "emmetTriggerExpansionOnTab": "Quando habilitado, abreviações emmet são expandidas ao pressionar TAB.", "emmetPreferences": "Preferências usadas para modificar o comportamento de algumas ações e resolvedores de Emmet.", "emmetPreferencesIntUnit": "Unidade padrão para valores inteiros", "emmetPreferencesFloatUnit": "Unidade padrão para valores float", @@ -44,5 +37,7 @@ "emmetPreferencesCssBetween": "Símbolo a ser colocado entre a propriedade CSS e o valor quando expandir abreviaturas CSS", "emmetPreferencesSassBetween": "Símbolo a ser colocado entre a propriedade CSS e o valor quando expandir abreviaturas CSS em arquivos Sass", "emmetPreferencesStylusBetween": "Símbolo a ser colocado entre a propriedade CSS e o valor quando expandir abreviaturas CSS em arquivos Stylus", - "emmetShowSuggestionsAsSnippets": "Se verdadeiro, então as sugestões emmet aparecerão como trechos, permitindo você requisitá-los conforme a configuração de editor.snippetSuggestions." + "emmetPreferencesFilterCommentBefore": "Uma definição de comentário que deve ser colocado antes de elemento correspondente quando um filtro de comentário é aplicado.", + "emmetPreferencesFilterCommentAfter": "Uma definição de comentário que deve ser colocado após o elemento correspondente quando um filtro de comentário é aplicado.", + "emmetPreferencesFilterCommentTrigger": "Uma lista separada por vírgulas de nomes de atributo que deve existir em abreviações para o filtro de comentário a ser aplicado" } \ No newline at end of file diff --git a/i18n/ptb/extensions/git/out/commands.i18n.json b/i18n/ptb/extensions/git/out/commands.i18n.json index 4c72afe876f..d3e314a0e15 100644 --- a/i18n/ptb/extensions/git/out/commands.i18n.json +++ b/i18n/ptb/extensions/git/out/commands.i18n.json @@ -12,8 +12,9 @@ "cloning": "Clonando repositório do Git...", "openrepo": "Abrir Repositório", "proposeopen": "Gostaria de abrir o repositório clonado?", - "path to init": "Caminho da pasta", - "provide path": "Por favor, forneça um caminho de pasta para inicializar um repositório Git", + "init repo": "Inicializar Repositório", + "create repo": "Inicializar Repositório", + "are you sure": "Isto irá criar um repositório Git em '{0}'. Tem certeza que deseja continuar?", "HEAD not available": "Versão HEAD de '{0}' não está disponível.", "confirm stage files with merge conflicts": "Tem certeza que deseja processar {0} arquivos com conflitos de mesclagem?", "confirm stage file with merge conflicts": "Tem certeza que deseja processar {0} com conflitos de mesclagem?", diff --git a/i18n/ptb/extensions/git/out/repository.i18n.json b/i18n/ptb/extensions/git/out/repository.i18n.json index d9855ba9df2..6c47d5f327f 100644 --- a/i18n/ptb/extensions/git/out/repository.i18n.json +++ b/i18n/ptb/extensions/git/out/repository.i18n.json @@ -21,6 +21,8 @@ "deleted by us": "Excluído por nós", "both added": "Ambos adicionados", "both modified": "Ambos modificados", + "untracked, short": "U", + "modified, short": "M", "commit": "Confirmar", "merge changes": "Mesclar Alterações", "staged changes": "Alterações em Etapas", diff --git a/i18n/ptb/extensions/git/package.i18n.json b/i18n/ptb/extensions/git/package.i18n.json index f45cd09fb8b..8ca31f76ec1 100644 --- a/i18n/ptb/extensions/git/package.i18n.json +++ b/i18n/ptb/extensions/git/package.i18n.json @@ -15,6 +15,8 @@ "command.stageAll": "Estagiar Todas Alterações", "command.stageSelectedRanges": "Estagiar Faixas Selecionadas", "command.revertSelectedRanges": "Reverter Faixas Selecionadas", + "command.stageChange": "Mudança de fase", + "command.revertChange": "Reverter a alteração", "command.unstage": "Desestagiar Alterações", "command.unstageAll": "Desestagiar Todas Alterações", "command.unstageSelectedRanges": "Desestagiar Faixas Selecionadas", diff --git a/i18n/ptb/extensions/typescript/package.i18n.json b/i18n/ptb/extensions/typescript/package.i18n.json index 8b7415813f2..4bc0448e7a9 100644 --- a/i18n/ptb/extensions/typescript/package.i18n.json +++ b/i18n/ptb/extensions/typescript/package.i18n.json @@ -44,7 +44,8 @@ "typescript.npm": "Especifica o caminho para o executável do NPM usado para Aquisição de Tipo Automático. Requer TypeScript > = 2.3.4.", "typescript.check.npmIsInstalled": "Verificar se o NPM está instalado para aquisição automática de tipo.", "javascript.nameSuggestions": "Habilitar/desabilitar incluindo nomes exclusivos do arquivo nas listas de sugestão de JavaScript.", - "typescript.tsc.autoDetect": "Controla se a auto-detecção de tarefas tsc estão ligadas ou desligadas.", + "typescript.tsc.autoDetect": "Controla a auto deteção das tarefas de tsc. 'off' desativa esse recurso. 'build' só cria tarefas de compilação de execução única. 'watch' cria apenas tarefas de compilação e monitoramento. 'on' cria ambas as tarefas de compilar e monitoramento. Padrão é 'on'.", "typescript.problemMatchers.tsc.label": "Problemas TypeScript", - "typescript.problemMatchers.tscWatch.label": "Problemas TypeScript (modo observação)" + "typescript.problemMatchers.tscWatch.label": "Problemas TypeScript (modo observação)", + "typescript.quickSuggestionsForPaths": "Ativar/desativar sugestões rápidas quando estiver digitando um caminho de importação." } \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/contrib/find/browser/findWidget.i18n.json b/i18n/ptb/src/vs/editor/contrib/find/browser/findWidget.i18n.json index 473543c0850..fcd5681301a 100644 --- a/i18n/ptb/src/vs/editor/contrib/find/browser/findWidget.i18n.json +++ b/i18n/ptb/src/vs/editor/contrib/find/browser/findWidget.i18n.json @@ -15,7 +15,6 @@ "label.replaceButton": "Substituir", "label.replaceAllButton": "Substituir Tudo", "label.toggleReplaceButton": "Ativar/desativar modo Substituir", - "title.matchesCountLimit": "Somente os primeiros 999 resultados são realçados, mas todas as operações de pesquisa funcionam em todo o texto.", "label.matchesLocation": "{0} de {1}", "label.noResults": "Nenhum resultado" } \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/contrib/find/common/findController.i18n.json b/i18n/ptb/src/vs/editor/contrib/find/common/findController.i18n.json index 93f4b43b45c..ce656c20f36 100644 --- a/i18n/ptb/src/vs/editor/contrib/find/common/findController.i18n.json +++ b/i18n/ptb/src/vs/editor/contrib/find/common/findController.i18n.json @@ -10,12 +10,6 @@ "nextSelectionMatchFindAction": "Localizar Próxima Seleção", "previousSelectionMatchFindAction": "Localizar Seleção Anterior", "startReplace": "Substituir", - "addSelectionToNextFindMatch": "Adicionar Seleção ao Próximo Localizar Correspondência", - "addSelectionToPreviousFindMatch": "Adicionar Seleção à Correspondência de Localização Anterior", - "moveSelectionToNextFindMatch": "Mover Última Seleção para Próximo Localizar Correspondência", - "moveSelectionToPreviousFindMatch": "Mover Última Seleção para Correspondência de Localização Anterior", - "selectAllOccurrencesOfFindMatch": "Selecionar Todas as Ocorrências de Localizar Correspondência", - "changeAll.label": "Alterar todas as ocorrências", "showNextFindTermAction": "Mostrar Próximo Termo de Busca", "showPreviousFindTermAction": "Mostrar Termo de Busca Anterior" } \ No newline at end of file diff --git a/i18n/ptb/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json b/i18n/ptb/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json index 583be5b3e5c..790c737c8eb 100644 --- a/i18n/ptb/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json +++ b/i18n/ptb/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json @@ -6,5 +6,11 @@ { "mutlicursor.insertAbove": "Inserir cursor acima", "mutlicursor.insertBelow": "Inserir cursor abaixo", - "mutlicursor.insertAtEndOfEachLineSelected": "Adicionar Cursores ao Final das Linhas" + "mutlicursor.insertAtEndOfEachLineSelected": "Adicionar Cursores ao Final das Linhas", + "addSelectionToNextFindMatch": "Adicionar Seleção ao Próximo Localizar Correspondência", + "addSelectionToPreviousFindMatch": "Adicionar Seleção à Correspondência de Localização Anterior", + "moveSelectionToNextFindMatch": "Mover Última Seleção para Próximo Localizar Correspondência", + "moveSelectionToPreviousFindMatch": "Mover Última Seleção para Correspondência de Localização Anterior", + "selectAllOccurrencesOfFindMatch": "Selecionar Todas as Ocorrências de Localizar Correspondência", + "changeAll.label": "Alterar todas as ocorrências" } \ No newline at end of file diff --git a/i18n/ptb/src/vs/platform/theme/common/colorRegistry.i18n.json b/i18n/ptb/src/vs/platform/theme/common/colorRegistry.i18n.json index 2c5e3a22276..a6b7b377cac 100644 --- a/i18n/ptb/src/vs/platform/theme/common/colorRegistry.i18n.json +++ b/i18n/ptb/src/vs/platform/theme/common/colorRegistry.i18n.json @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "invalid.color": "Formato inválido de cor. Use #RGB, #RGBA, #RRGGBB ou #RRGGBBAA", "schema.colors": "Cores usadas no workbench.", "foreground": "Cor de primeiro plano geral. Essa cor é só usada se não for substituída por um componente.", "errorForeground": "Cor de primeiro plano geral para mensagens de erro. Essa cor é só usada se não for substituída por um componente.", 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 f9516787ac6..5a005571d2b 100644 --- a/i18n/ptb/src/vs/workbench/browser/actions/workspaceActions.i18n.json +++ b/i18n/ptb/src/vs/workbench/browser/actions/workspaceActions.i18n.json @@ -9,14 +9,17 @@ "addFolderToWorkspace": "Adicionar pasta ao espaço de trabalho...", "add": "&& Adicionar", "addFolderToWorkspaceTitle": "Adicionar pasta ao espaço de trabalho", + "globalRemoveFolderFromWorkspace": "Remover pasta da área de trabalho", "newWorkspace": "Novo espaço de trabalho...", "select": "&&Selecionar", "selectWorkspace": "Selecionar pastas para espaço de trabalho", "removeFolderFromWorkspace": "Remover pasta da área de trabalho", + "openFolderSettings": "Abrir configurações da pasta", "saveWorkspaceAsAction": "Salvar o espaço de trabalho como...", "save": "&&Salvar", "saveWorkspace": "Salvar o espaço de trabalho", "openWorkspaceAction": "Abrir o Espaço de Trabalho...", "openWorkspaceConfigFile": "Abrir o Arquivo de Configuração do Espaço de Trabalho", + "openFolderAsWorkspaceInNewWindow": "Abrir a pasta como espaço de trabalho em nova janela", "workspaceFolderPickerPlaceholder": "Selecione a pasta de trabalho" } \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json b/i18n/ptb/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json index 577ad33d0b4..f42981ec157 100644 --- a/i18n/ptb/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json +++ b/i18n/ptb/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json @@ -5,6 +5,5 @@ // Do not edit this file. It is machine generated. { "hideActivitBar": "Ocultar a Barra de Atividades", - "activityBarAriaLabel": "Chave do Modo de exibição Ativo", "globalActions": "Ações globais" } \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/browser/parts/compositebar/compositeBar.i18n.json b/i18n/ptb/src/vs/workbench/browser/parts/compositebar/compositeBar.i18n.json new file mode 100644 index 00000000000..4a4304f83f0 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/browser/parts/compositebar/compositeBar.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. +{ + "activityBarAriaLabel": "Chave do Modo de exibição Ativo" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/browser/parts/compositebar/compositeBarActions.i18n.json b/i18n/ptb/src/vs/workbench/browser/parts/compositebar/compositeBarActions.i18n.json new file mode 100644 index 00000000000..ab4fa3904b3 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/browser/parts/compositebar/compositeBarActions.i18n.json @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "badgeTitle": "{0} - {1}", + "additionalViews": "Visualizações Adicionais", + "numberBadge": "{0} ({1})", + "manageExtension": "Gerenciar Extensão", + "titleKeybinding": "{0} ({1})", + "hide": "Ocultar", + "toggle": "Alternar Visualização Fixa" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json b/i18n/ptb/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json index e318ceb9434..d1d8a425227 100644 --- a/i18n/ptb/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json +++ b/i18n/ptb/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json @@ -12,5 +12,6 @@ "groupTwoPicker": "Mostrar editores no segundo grupo", "groupThreePicker": "Mostrar editores no terceiro grupo", "allEditorsPicker": "Mostrar todos editores abertos", - "view": "Exibir" + "view": "Exibir", + "file": "Arquivo" } \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json b/i18n/ptb/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json index 4a2adabf5a6..b997093f4f8 100644 --- a/i18n/ptb/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json +++ b/i18n/ptb/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json @@ -4,6 +4,5 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "canNotRun": "O comando '{0}' não está habilitado e não pode ser executado.", "manageExtension": "Gerenciar Extensão" } \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/electron-browser/main.contribution.i18n.json b/i18n/ptb/src/vs/workbench/electron-browser/main.contribution.i18n.json index 4f521d06910..8f50f5262de 100644 --- a/i18n/ptb/src/vs/workbench/electron-browser/main.contribution.i18n.json +++ b/i18n/ptb/src/vs/workbench/electron-browser/main.contribution.i18n.json @@ -10,18 +10,15 @@ "workspaces": "Espaços de trabalho", "developer": "Desenvolvedor", "showEditorTabs": "Controla se os editores abertos devem ou não serem exibidos em abas.", - "workbench.editor.labelFormat.default": "Mostrar o nome do arquivo. Quando as abas estão habilitadas e dois arquivos têm o mesmo nome em um grupo são adicionadas as seções de diferenciação do caminho de cada arquivo. Quando as abas estão desativadas, o caminho relativo para a raiz do espaço de trabalho é mostrado se o editor está ativo.", "workbench.editor.labelFormat.short": "Mostrar o nome do arquivo seguido pelo nome do diretório.", - "workbench.editor.labelFormat.medium": "Mostrar o nome do arquivo seguido pelo seu caminho relativo à raiz do espaço de trabalho.", "workbench.editor.labelFormat.long": "Mostrar o nome do arquivo seguido pelo seu caminho absoluto.", "tabDescription": "Controla o formato do rótulo para um editor. Alterar essa configuração pode por exemplo tornar mais fácil entender a localização de um arquivo:\n- curto: 'parent'\n- médio: 'workspace/src/parent'\n- longa: '/ home/user/workspace/src/parent'\n- padrão: '... /parent, quando outra guia compartilha o mesmo título, ou o caminho relativo do espaço de trabalho se as guias estão desabilitadas", "editorTabCloseButton": "Controla a posição dos botões de fechar das abas do editor ou os desabilita quando configurados para 'desligado'.", "showIcons": "Controla se os editores abertos devem ou não ser exibidos com um ícone. Requer um tema de ícone para ser habilitado. ", "enablePreview": "Controla se editores abertos mostram uma visualização. Editores de visualização são reutilizados até que eles sejam mantidos (por exemplo, através do duplo clique ou edição) e aparecerem com um estilo de fonte em itálico.", "enablePreviewFromQuickOpen": "Controla se os editores abertos da Abertura Rápida são exibidos como visualização. Os editores de visualização são reutilizados até serem preservados (por exemplo, através de um duplo clique ou edição).", - "editorOpenPositioning": "Controla onde os editores serão abertos. Escolha 'esquerda' ou 'direita' para abrir os editores à esquerda ou à direita do \neditor ativo. Selecione 'primeiro' ou 'último' para abrir os editores independentemente do atual.", "revealIfOpen": "Controla se um editor é exibido em qualquer um dos grupos, se aberto. Se desabilitado, um editor será aberto preferencialmente no grupo de editores ativo. Se habilitado, um editor já aberto será exibido no grupo de editores ativo, ao invés de ser aberto novamente. Note que há alguns casos onde esta configuração é ignorada, por exemplo, quando for forçada a abertura de um editor em um grupo específico ou ao lado do grupo atualmente ativo.", - "commandHistory": "Controla o número de comandos recentemente usados mantidos no histórico para a paleta de comandos. Definir como 0 para desativar o histórico de comandos.", + "commandHistory": "Controla o número de comandos usados recentemente a serem mantidos no histórico da paleta de comando. Definir como 0 para desativar o histórico de comandos.", "preserveInput": "Controla se a última entrada digitada na paleta de comandos deve ser restaurada ao abri-la da próxima vez.", "closeOnFocusLost": "Controla se Abertura Rápida deve fechar automaticamente caso perca o foco.", "openDefaultSettings": "Controla se a abertura de configurações também abre um editor mostrando todas as configurações padrão.", @@ -50,7 +47,7 @@ "restoreWindows": "Controla como as janelas serão reabertas após uma reinicialização. Selecione 'nenhum' para sempre iniciar com uma área de trabalho vazia, 'um' para reabrir a última janela que você trabalhou, 'pastas' para reabrir todas as janelas que tinham pastas abertas ou 'todos' para reabrir todas as janelas da sua última sessão.", "restoreFullscreen": "Controla se uma janela deve ser restaurada em modo de tela cheia se ela foi finalizada em modo de tela cheia.", "zoomLevel": "Ajusta o nível de zoom da janela. O tamanho original é 0 e cada aumento (por exemplo, 1) ou redução (por exemplo, -1) representa um zoom 20% maior ou menor. Você também pode digitar decimais para ajustar o nível de zoom com uma granularidade mais fina.", - "title": "Controla o título de janela baseado no editor do ativo. Variáveis são substituídas com base no contexto:\n${activeEditorShort}: por exemplo, MyFile.txt \n${activeEditorMedium}: por exemplo, myFolder/myFile.txt \n${activeEditorLong}: por exemplo, /Users/Development/myProject/myFolder/myFile.txt \n${folderName}: por exemplo myFolder \n${folderPath}: por exemplo, /Users/Development/myFolder \n${rootName}: por exemplo, myFolder1, myFolder2, myFolder3 \n${rootPath}: por exemplo, /Users/Development/myWorkspace \n${appName}: por exemplo, VS Code \n${dirty}: um indicador que mostra se o editor ativo está modificado\n${separator}: um separador condicional (\"-\") que é mostrado apenas quando cercado por variáveis com valores", + "title": "Controla o título de janela baseado no editor ativo. Variáveis são substituídas com base no contexto: \n${activeEditorShort}: o nome do arquivo (por exemplo, MyFile txt)\n${activeEditorMedium}: o caminho do arquivo relativo à pasta da área de trabalho (por exemplo, myFolder/myFile.txt) \n${activeEditorLong}: o caminho completo do arquivo (por exemplo, /Users/Development/myProject/myFolder/myFile.txt) \n${folderName}: nome da pasta de trabalho em que o arquivo está contido (por exemplo, myFolder) \n${folderPath}: caminho do arquivo da pasta de trabalho em que o arquivo está contido (por exemplo, /Users/Development/myFolder) {\n$(rootName}: nome do espaço de trabalho (por exemplo, myFolder ou myWorkspace)\n${rootPath}: caminho do espaço de trabalho (por exemplo, /Users/Development/myWorkspace) \n${appName}: por exemplo, VS Code\n${dirty}: um indicador se o editor ativo foi modificado\n${separator}: um separador condicional (\"-\") que é mostrado apenas quando cercado por variáveis com valores", "window.newWindowDimensions.default": "Abrir novas janelas no centro da tela.", "window.newWindowDimensions.inherit": "Abrir novas janelas com a mesma dimensão da última janela ativa.", "window.newWindowDimensions.maximized": "Abrir novas janelas maximizadas.", diff --git a/i18n/ptb/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json b/i18n/ptb/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json index 01955c44965..dd268ebad23 100644 --- a/i18n/ptb/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json +++ b/i18n/ptb/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json @@ -6,6 +6,7 @@ { "entryAriaLabel": "depurar {0}", "debugAriaLabel": "Digite um nome de uma configuração de lançamento para ser executado.", + "addConfiguration": "Adicionar Configuração...", "noConfigurationsMatching": "Não há configurações de depuração correspondentes", "noConfigurationsFound": "Configurações de depuração não encontradas. Por favor, crie um arquivo 'launch.json'." } \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/debug/browser/debugStatus.i18n.json b/i18n/ptb/src/vs/workbench/parts/debug/browser/debugStatus.i18n.json new file mode 100644 index 00000000000..7a6db36a29e --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/debug/browser/debugStatus.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. +{ + "debug": "Depurar" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/debug/browser/debugViewlet.i18n.json b/i18n/ptb/src/vs/workbench/parts/debug/browser/debugViewlet.i18n.json new file mode 100644 index 00000000000..86ae4403f16 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/debug/browser/debugViewlet.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "debugFocusVariablesView": "Foco em Variáveis", + "debugFocusWatchView": "Foco em Monitoramento", + "debugFocusCallStackView": "Foco em Pilha de Chamadas", + "debugFocusBreakpointsView": "Foco em Pontos de Interrupção" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json b/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json index 05e76d32185..3908acc5028 100644 --- a/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json +++ b/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json @@ -15,7 +15,6 @@ "vscode.extension.contributes.debuggers.initialConfigurations": "Configurações para gerar o 'launch.json' inicial.", "vscode.extension.contributes.debuggers.languages": "Lista de idiomas para os quais a extensão de depuração pode ser considerada o \"depurador padrão\".", "vscode.extension.contributes.debuggers.adapterExecutableCommand": "Se especificado VS Code chamará este comando para determinar o caminho do executável do adaptador de depuração e os argumentos para passar.", - "vscode.extension.contributes.debuggers.startSessionCommand": "Se especificado VS Code chamará este comando para as ações de \"depurar\" ou \"executar\" direcionadas para esta extensão.", "vscode.extension.contributes.debuggers.configurationSnippets": "Trechos de código para adicionar novas configurações em 'launch.json'.", "vscode.extension.contributes.debuggers.configurationAttributes": "Configurações de esquema JSON para validar 'launch.json'.", "vscode.extension.contributes.debuggers.windows": "Configurações específicas do Windows.", diff --git a/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json b/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json index aaf52e5dedb..5e4eb26c208 100644 --- a/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json +++ b/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json @@ -12,10 +12,10 @@ "breakpointRemoved": "Ponto de interrupção removido, linha {0}, arquivo {1}", "compoundMustHaveConfigurations": "Composição deve ter o atributo \"configurations\" definido para iniciar várias configurações.", "configMissing": "Configuração '{0}' não tem 'launch.json'.", - "debugRequestNotSupported": "Configuração de depuração escolhido possui um valor de atributo não suportado '{0}': '{1}'.", - "debugRequesMissing": "Atributo '{0}' está faltando para a configuração de depuração escolhida.", + "debugRequestNotSupported": "Atributo '{0}' tem um valor sem suporte '{1}' na configuração de depuração escolhida.", + "debugRequesMissing": "Atributo '{0}' está faltando na configuração de depuração escolhida.", "debugTypeNotSupported": "Tipo de depuração configurado '{0}' não é suportado.", - "debugTypeMissing": "Falta a propriedade 'type' para a configuração de lançamento escolhida.", + "debugTypeMissing": "Falta a propriedade 'tipo' para a configuração de lançamento escolhida.", "preLaunchTaskErrors": "Erros de build foram detectados durante a preLaunchTask '{0}'.", "preLaunchTaskError": "Erro de build foi detectado durante a preLaunchTask '{0}'.", "preLaunchTaskExitCode": "A preLaunchTask '{0}' encerrada com código de saída {1}.", diff --git a/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json b/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json index 4afd6d64d82..5f2a144a70e 100644 --- a/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json +++ b/i18n/ptb/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json @@ -8,5 +8,5 @@ "replVariableAriaLabel": "Variável {0} tem valor {1}, ler a impressão do valor do loop, depurar", "replExpressionAriaLabel": "Expressão {0} tem valor {1}, ler o laço de avaliação de impressão, depurar", "replValueOutputAriaLabel": " impressão da avaliação do laço de leitura, depurar", - "replKeyValueOutputAriaLabel": "Variável de saída {0} tem valor {1}, ler o loop de avaliação de impressão, depurar" + "replRawObjectAriaLabel": "Variável repl {0} tem valor {1}, ler o loop de avaliação de impressão e depuração" } \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json b/i18n/ptb/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json index 39deecf379b..464da97e4e9 100644 --- a/i18n/ptb/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json +++ b/i18n/ptb/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "filesCategory": "Arquivos", + "filesCategory": "Arquivo", "revealInSideBar": "Revelar na Barra Lateral", "acceptLocalChanges": "Usar suas alterações e substituir o conteúdo do disco", "revertLocalChanges": "Descartar as alterações e reverter para o conteúdo no disco" diff --git a/i18n/ptb/src/vs/workbench/parts/files/browser/fileActions.i18n.json b/i18n/ptb/src/vs/workbench/parts/files/browser/fileActions.i18n.json index 99af18bd13e..674a4dedbf2 100644 --- a/i18n/ptb/src/vs/workbench/parts/files/browser/fileActions.i18n.json +++ b/i18n/ptb/src/vs/workbench/parts/files/browser/fileActions.i18n.json @@ -23,6 +23,7 @@ "confirmMoveTrashMessageFile": "Tem certeza de que deseja excluir '{0}'?", "undoBin": "Você pode restaurar da lixeira.", "undoTrash": "Você pode restaurar a partir do lixo.", + "doNotAskAgain": "Não me pergunte novamente", "confirmDeleteMessageFolder": "Tem certeza de que deseja excluir permanentemente '{0}' e seu conteúdo?", "confirmDeleteMessageFile": "Tem certeza de que deseja excluir permanentemente '{0}'?", "irreversible": "Esta ação é irreversível!", @@ -37,8 +38,6 @@ "openToSide": "Aberto para o lado", "compareSource": "Selecione para comparar", "globalCompareFile": "Compare o Arquivo Ativo Com...", - "pickHistory": "Selecione um arquivo previamente aberto para comparar com", - "unableToFileToCompare": "O arquivo selecionado não pode ser comparado com '{0}'.", "openFileToCompare": "Abrir um arquivo primeiro para compará-lo com outro arquivo.", "compareWith": "Comparar '{0}' com '{1}'", "compareFiles": "Comparar Arquivos", @@ -47,7 +46,7 @@ "saveAs": "Salvar como...", "saveAll": "Salvar Todos", "saveAllInGroup": "Salvar Todos no Grupo", - "saveFiles": "Salvar Arquivos Sujos", + "saveFiles": "Salvar todos os arquivos", "revert": "Reverter Arquivo", "focusOpenEditors": "Foco na Visualização dos Editores Abertos", "focusFilesExplorer": "Foco no Explorador de Arquivos", diff --git a/i18n/ptb/src/vs/workbench/parts/files/browser/files.contribution.i18n.json b/i18n/ptb/src/vs/workbench/parts/files/browser/files.contribution.i18n.json index 8100b458672..0e95d226253 100644 --- a/i18n/ptb/src/vs/workbench/parts/files/browser/files.contribution.i18n.json +++ b/i18n/ptb/src/vs/workbench/parts/files/browser/files.contribution.i18n.json @@ -40,10 +40,14 @@ "dynamicHeight": "Controla se a altura da seção de editores abertos deve adaptar-se dinamicamente para o número de elementos ou não.", "autoReveal": "Controla se o explorador deve automaticamente revelar e selecionar arquivos ao abri-los.", "enableDragAndDrop": "Controla se o explorador deve permitir mover arquivos e pastas através de arrastar e soltar.", + "confirmDragAndDrop": "Controla se o explorer deve pedir a confirmação ao mover arquivos ou pastas através de arrastar e soltar.", + "confirmDelete": "Controla se o explorador deve pedir a confirmação ao excluir um arquivo por meio do lixo.", "sortOrder.default": "Arquivos e pastas são classificadas por seus nomes, em ordem alfabética. Pastas são exibidas acima dos arquivos.", "sortOrder.mixed": "Arquivos e pastas são classificadas por seus nomes, em ordem alfabética. Arquivos são misturados com pastas.", "sortOrder.filesFirst": "Arquivos e pastas são classificadas por seus nomes, em ordem alfabética. Os arquivos são exibidos acima das pastas.", "sortOrder.type": "Arquivos e pastas são classificadas de acordo com suas extensões, em ordem alfabética. Pastas são exibidas acima dos arquivos.", "sortOrder.modified": "Arquivos e pastas são classificados de acordo com a data da última modificação, em ordem decrescente. Pastas são exibidas acima dos arquivos.", - "sortOrder": "Controla a ordem de classificação dos arquivos e pastas no explorador. Além da classificação padrão, você pode definir a ordem para 'mixed' (arquivos e pastas misturados), 'type' (por tipo de arquivo), 'modified' (pela data da última modificação) ou 'filesFirst' (exibe os arquivos acima das pastas)." + "sortOrder": "Controla a ordem de classificação dos arquivos e pastas no explorador. Além da classificação padrão, você pode definir a ordem para 'mixed' (arquivos e pastas misturados), 'type' (por tipo de arquivo), 'modified' (pela data da última modificação) ou 'filesFirst' (exibe os arquivos acima das pastas).", + "explorer.decorations.colors": "Controles se as decorações de arquivo devem usar cores.", + "explorer.decorations.badges": "Controles se as decorações de arquivo devem usar identificações." } \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json b/i18n/ptb/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json index 38f98ed9420..69f48bde47d 100644 --- a/i18n/ptb/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json +++ b/i18n/ptb/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json @@ -6,6 +6,8 @@ { "noWorkspace": "Nenhuma Pasta Aberta", "explorerSection": "Seção de Explorador de Arquivos", - "noWorkspaceHelp": "Você ainda não abriu uma pasta.", + "noWorkspaceHelp": "Você ainda não adicionou uma pasta no espaço de trabalho.", + "addFolder": "Adicionar pasta", + "noFolderHelp": "Você ainda não abriu uma pasta.", "openFolder": "Abrir Pasta" } \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json b/i18n/ptb/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json index d321f8c15ce..f5321d10375 100644 --- a/i18n/ptb/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json +++ b/i18n/ptb/src/vs/workbench/parts/files/browser/views/explorerViewer.i18n.json @@ -4,12 +4,15 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { + "canNotResolve": "Não pode resolver a pasta {0}", "fileInputAriaLabel": "Digite o Nome do arquivo. Pressione Enter para confirmar ou Escape para cancelar.", "filesExplorerViewerAriaLabel": "{0}, Explorador de Arquivos", "dropFolders": "Você quer adicionar as pastas no espaço de trabalho?", "dropFolder": "Você quer adicionar a pasta no espaço de trabalho?", "addFolders": "&& Adicionar pastas", "addFolder": "&& Adicionar pasta", + "confirmMove": "Tem certeza que deseja mover '{0}'?", + "doNotAskAgain": "Não me pergunte novamente", "confirmOverwriteMessage": "'{0}' já existe na pasta de destino. Deseja substituí-lo?", "irreversible": "Esta ação é irreversível!", "replaceButtonLabel": "&&Substituir" diff --git a/i18n/ptb/src/vs/workbench/parts/markers/browser/markersFileDecorations.i18n.json b/i18n/ptb/src/vs/workbench/parts/markers/browser/markersFileDecorations.i18n.json new file mode 100644 index 00000000000..62c14983ac7 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/parts/markers/browser/markersFileDecorations.i18n.json @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "label": "Problemas", + "tooltip.1": "1 problema neste arquivo", + "tooltip.N": "{0} problemas neste arquivo", + "markers.showOnFile": "Mostre erros e avisos no arquivos e pasta." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json b/i18n/ptb/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json index 1b5d84180e9..10a0c7ab0f2 100644 --- a/i18n/ptb/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json +++ b/i18n/ptb/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json @@ -5,7 +5,6 @@ // Do not edit this file. It is machine generated. { "emptyUserSettingsHeader": "Coloque as suas configurações aqui para substituir as configurações padrão.", - "errorInvalidConfiguration": "Não é possível gravar em configurações. Corrija erros/avisos no arquivo e tente novamente.", "emptyWorkspaceSettingsHeader": "Coloque as suas configurações aqui para substituir as configurações de usuário.", "emptyFolderSettingsHeader": "Coloque as suas configurações de pasta aqui para substituir aqueles das configurações do espaço de trabalho.", "defaultFolderSettingsTitle": "Configurações de pasta padrão", diff --git a/i18n/ptb/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json b/i18n/ptb/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json index b6d5cdbd2e5..e45b8e421e8 100644 --- a/i18n/ptb/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json +++ b/i18n/ptb/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json @@ -13,7 +13,6 @@ "actionNotEnabled": "O comando '{0}' não está habilitado no contexto atual.", "recentlyUsed": "usados recentemente", "morecCommands": "outros comandos", - "commandLabel": "{0}: {1}", "cat.title": "{0}: {1}", "noCommandsMatching": "Não há comandos correspondentes" } \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json b/i18n/ptb/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json index f9640a94d5a..2ab571fe333 100644 --- a/i18n/ptb/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json +++ b/i18n/ptb/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.i18n.json @@ -4,6 +4,10 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { + "changes": "{0} de {1} mudanças", + "change": "{0} de {1} mudança", + "show previous change": "Mostrar a Alteração Anterior", + "show next change": "Mostrar a Próxima Alteração", "editorGutterModifiedBackground": "Cor de fundo da dobra do editor para as linhas que estão modificadas.", "editorGutterAddedBackground": "Cor de fundo da dobra do editor para as linhas que estão adicionadas.", "editorGutterDeletedBackground": "Cor de fundo da dobra do editor para as linhas que estão excluídas.", diff --git a/i18n/ptb/src/vs/workbench/parts/search/browser/search.contribution.i18n.json b/i18n/ptb/src/vs/workbench/parts/search/browser/search.contribution.i18n.json index 6e7cef32f55..477df0fd694 100644 --- a/i18n/ptb/src/vs/workbench/parts/search/browser/search.contribution.i18n.json +++ b/i18n/ptb/src/vs/workbench/parts/search/browser/search.contribution.i18n.json @@ -17,7 +17,7 @@ "exclude": "Configure os padrões glob para excluir arquivos e pastas nas pesquisas. Herda todos os padrões glob da configuração files.exclude.", "exclude.boolean": "O padrão glob com o qual combinar os caminhos de arquivo. Defina para verdadeiro ou falso para habilitar ou desabilitar o padrão.", "exclude.when": "Verificação adicional nos irmãos de um arquivo correspondente. Use $(basename) como variável para o nome do arquivo correspondente.", - "useRipgrep": "Controla se deve utilizar ripgrep na pesquisa de texto", + "useRipgrep": "Controla se utiliza ripgrep em buscas de texto e de arquivo", "useIgnoreFilesByDefault": "Controla se deve utilizar arquivos .gitignore e .ignore por padrão ao fazer pesquisas em um novo espaço de trabalho.", "search.quickOpen.includeSymbols": "Configurar para incluir resultados de uma pesquisa símbolo global nos resultados do arquivo para Abertura Rápida." } \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/search/browser/searchActions.i18n.json b/i18n/ptb/src/vs/workbench/parts/search/browser/searchActions.i18n.json index cd879d71747..933840a7fc3 100644 --- a/i18n/ptb/src/vs/workbench/parts/search/browser/searchActions.i18n.json +++ b/i18n/ptb/src/vs/workbench/parts/search/browser/searchActions.i18n.json @@ -19,7 +19,6 @@ "ClearSearchResultsAction.label": "Limpar os Resultados da Pesquisa", "FocusNextSearchResult.label": "Focalizar o Próximo Resultado da Pesquisa", "FocusPreviousSearchResult.label": "Focalizar o Resultado da Pesquisa Anterior", - "RemoveAction.label": "Remover", "file.replaceAll.label": "Substituir Tudo", "match.replace.label": "Substituir" } \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json b/i18n/ptb/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json index d1afc5ef25a..61f582e57b8 100644 --- a/i18n/ptb/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json +++ b/i18n/ptb/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json @@ -10,8 +10,8 @@ "vscode.extension.contributes.snippets": "Contribui aos trechos de código.", "vscode.extension.contributes.snippets-language": "Identificador de linguagem para o qual este trecho de código contribui.", "vscode.extension.contributes.snippets-path": "Caminho do arquivo de trechos de código. O caminho é relativo à pasta de extensão e normalmente começa com '. /snippets/'.", - "badFile": "O arquivo de trechos \"{0}\" não pôde ser lido.", "badVariableUse": "O trecho de código \"{0}\" muito provavelmente confunde as variáveis de trecho de código e espaços reservados do trecho de código. Consulte https://code.visualstudio.com/docs/editor/userdefinedsnippets#_snippet-syntax para obter mais detalhes.", + "badFile": "O arquivo de trechos \"{0}\" não pôde ser lido.", "source.snippet": "Trecho de código do usuário", "detail.snippet": "{0} ({1})", "snippetSuggest.longLabel": "{0}, {1}" 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 526a6233a3d..7841731c435 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 @@ -14,6 +14,7 @@ "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", + "TaskServer.folderIgnored": "A pasta {0} é ignorada desde que use a versão de tarefas 0.1.0", "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.", @@ -30,6 +31,7 @@ "TaskSystem.activeSame.noBackground": "A tarefa '{0}' já está ativa. Para finalizá-la use 'Finalizar Tarefa' no menu Tarefas.", "TaskSystem.active": "Já existe uma tarefa sendo executada. Finalize-a antes de executar outra tarefa.", "TaskSystem.restartFailed": "Falha ao finalizar e reiniciar a tarefa {0}", + "TaskService.noConfiguration": "Erro: A deteção de tarefa {0} não contribuiu para uma tarefa para a seguinte configuração: {1} a tarefa será ignorada.\n", "TaskSystem.configurationErrors": "Erro: A configuração da tarefa informada possui erros de validação e não pode ser utilizada. Por favor, corrija os erros primeiro.", "taskService.ignoreingFolder": "Ignorar as configurações de tarefa para a pasta de trabalho {0}. Suporte a tarefas de espaço de trabalho de múltiplas pastas requer que todas as pastas usem a versão de tarefas 2.0.0\n", "TaskSystem.invalidTaskJson": "Erro: O conteúdo do arquivo tasks.json possui erros de sintaxe. Por favor, corrija-os antes de executar uma tarefa.\n", diff --git a/i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json b/i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json index b075d6f0b92..f4a78b8d461 100644 --- a/i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json +++ b/i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.i18n.json @@ -17,6 +17,7 @@ "terminal.integrated.fontFamily": "Controla a família de fontes do terminal, este padrão é o valor do editor.fontFamily.", "terminal.integrated.fontSize": "Controla o tamanho da fonte em pixels do terminal.", "terminal.integrated.lineHeight": "Controles a altura da linha do terminal, este número é multiplicada pelo tamanho da fonte terminal para obter a altura real da linha em pixels.", + "terminal.integrated.enableBold": "Se deseja habilitar o texto em negrito dentro do terminal, note que isso requer o apoio do shell do terminal.", "terminal.integrated.cursorBlinking": "Controla se o cursor do terminal pisca.", "terminal.integrated.cursorStyle": "Controla o estilo do cursor do terminal.", "terminal.integrated.scrollback": "Controla a quantidade máxima de linhas que o terminal mantém em seu buffer.", diff --git a/i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json b/i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json index 7379c161275..2ee45b6deb3 100644 --- a/i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json +++ b/i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json @@ -16,7 +16,6 @@ "workbench.action.terminal.new.short": "Novo Terminal", "workbench.action.terminal.focus": "Focalizar Terminal", "workbench.action.terminal.focusNext": "Focalizar Próximo Terminal", - "workbench.action.terminal.focusAtIndex": "Focalizar Terminal {0}", "workbench.action.terminal.focusPrevious": "Focalizar Terminal Anterior", "workbench.action.terminal.paste": "Colar no Terminal Ativo", "workbench.action.terminal.DefaultShell": "Selecionar Shell Padrão", diff --git a/i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json b/i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json index 2bbfefe2297..ef05288e718 100644 --- a/i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json +++ b/i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json @@ -5,7 +5,6 @@ // Do not edit this file. It is machine generated. { "copy": "Copiar", - "createNewTerminal": "Novo Terminal", "paste": "Colar", "selectAll": "Selecionar Tudo", "clear": "Limpar" diff --git a/i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json b/i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json index 08ec4bd9777..83214ad4be8 100644 --- a/i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json +++ b/i18n/ptb/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json @@ -10,6 +10,5 @@ "never again": "Ok, Nunca Mostrar Novamente", "terminal.integrated.chooseWindowsShell": "Selecione o seu terminal shell preferido, você pode alterar isso mais tarde em suas configurações", "terminalService.terminalCloseConfirmationSingular": "Há uma sessão ativa de terminal, você quer finalizá-la?", - "terminalService.terminalCloseConfirmationPlural": "Existem {0} sessões ativas de terminal, você quer finalizá-las?", - "yes": "Sim" + "terminalService.terminalCloseConfirmationPlural": "Existem {0} sessões ativas de terminal, você quer finalizá-las?" } \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/services/configuration/common/configurationExtensionPoint.i18n.json b/i18n/ptb/src/vs/workbench/services/configuration/common/configurationExtensionPoint.i18n.json new file mode 100644 index 00000000000..5d3bc21b50f --- /dev/null +++ b/i18n/ptb/src/vs/workbench/services/configuration/common/configurationExtensionPoint.i18n.json @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "vscode.extension.contributes.configuration.title": "Um resumo das configurações. Este rótulo será usado no arquivo de configurações como um comentário de separação.", + "vscode.extension.contributes.configuration.properties": "Descrição das propriedades de configuração.", + "scope.window.description": "Janela de configuração específica que pode ser configurada nas configurações do usuário ou área de trabalho.", + "scope.resource.description": "Configuração específica do recurso que pode ser configurada nas configurações do usuário, espaço de trabalho ou pasta.", + "vscode.extension.contributes.configuration": "Contribui às definições de configuração.", + "invalid.title": "'configuration.title' deve ser um string", + "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 a serem carregadas 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", + "workspaceConfig.extensions.description": "Extensões para o espaço de trabalho", + "unknownWorkspaceProperty": "Propriedade de configuração do espaço de trabalho desconhecida" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/services/editor/common/editorService.i18n.json b/i18n/ptb/src/vs/workbench/services/editor/common/editorService.i18n.json new file mode 100644 index 00000000000..50e968f8ee3 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/services/editor/common/editorService.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. +{ + "compareLabels": "{0} ↔ {1}" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json b/i18n/ptb/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json index 0f7a663082c..ca77d82093c 100644 --- a/i18n/ptb/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json +++ b/i18n/ptb/src/vs/workbench/services/themes/common/colorThemeSchema.i18n.json @@ -6,6 +6,7 @@ { "schema.token.settings": "Cores e estilos para o token.", "schema.token.foreground": "Cor do primeiro plano para o token.", + "schema.token.background.warning": "Atualmente as cores de fundo do token não são suportadas.", "schema.token.fontStyle": "Estilo da fonte da regra: um estilo ou uma combinação de 'itálico', 'negrito' e 'sublinhado'", "schema.fontStyle.error": "O estilo da fonte deve ser uma combinação de 'itálico', 'negrito' e 'sublinhado'", "schema.properties.name": "Descrição da regra.", diff --git a/i18n/ptb/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json b/i18n/ptb/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json index 3cf20c82de2..ddfc13e6a19 100644 --- a/i18n/ptb/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json +++ b/i18n/ptb/src/vs/workbench/services/themes/common/fileIconThemeSchema.i18n.json @@ -33,5 +33,6 @@ "schema.fontSize": "Quando estiver utilizando uma fonte: O tamanho da fonte em porcentagem para a fonte de texto. Se não for definido, o padrão é o tamanho na definição de fonte.", "schema.fontId": "Quando estiver utilizando uma fonte: A identificação da fonte. Se não for definido, o padrão é a primeira definição de fonte.", "schema.light": "Associações opcionais para ícones de arquivo em temas de cor clara.", - "schema.highContrast": "Associações opcionais para ícones de arquivo em temas de alto contraste." + "schema.highContrast": "Associações opcionais para ícones de arquivo em temas de alto contraste.", + "schema.hidesExplorerArrows": "Define se as setas do explorador de arquivos devem ser ocultadas quando este tema está ativo." } \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/services/themes/electron-browser/colorThemeStore.i18n.json b/i18n/ptb/src/vs/workbench/services/themes/electron-browser/colorThemeStore.i18n.json new file mode 100644 index 00000000000..938c7bbea81 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/services/themes/electron-browser/colorThemeStore.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "vscode.extension.contributes.themes": "Contribui com temas de cores do textmate.", + "vscode.extension.contributes.themes.id": "ID do tema do ícone como usado em configurações do usuário.", + "vscode.extension.contributes.themes.label": "Etiqueta da cor do tema como mostrado na interface do usuário.", + "vscode.extension.contributes.themes.uiTheme": "Tema base de definição das cores do editor: 'vs' é o tema de cor clara, 'vs-dark' é o tema de cor escura. 'hc preto' é o tema escuro de alto contraste.", + "vscode.extension.contributes.themes.path": "Caminho do arquivo tmTheme. O caminho é relativo à pasta de extensão e é normalmente './themes/themeFile.tmTheme'.", + "reqarray": "Ponto de extensão '{0}' deve ser uma matriz.", + "reqpath": "Esperada uma string em `contributes.{0}.path`. Valor informado: {1}", + "invalid.path.1": "É esperado que `contributes.{0}.path` ({1}) seja incluído na pasta da extensão ({2}). Isto pode tornar a extensão não portável." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.i18n.json b/i18n/ptb/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.i18n.json new file mode 100644 index 00000000000..f78a56c0249 --- /dev/null +++ b/i18n/ptb/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.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. +{ + "error.cannotparseicontheme": "Problemas de análise do arquivo de ícones: {0}" +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.i18n.json b/i18n/ptb/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.i18n.json new file mode 100644 index 00000000000..0181b35ac2a --- /dev/null +++ b/i18n/ptb/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "vscode.extension.contributes.iconThemes": "Contribui com temas de ícones de arquivo.", + "vscode.extension.contributes.iconThemes.id": "ID do tema do ícone como usado em configurações do usuário.", + "vscode.extension.contributes.iconThemes.label": "Etiqueta do tema do ícone como mostrado na interface do usuário.", + "vscode.extension.contributes.iconThemes.path": "Caminho do arquivo de definição do tema do ícone. O caminho é relativo à pasta de extensão e é normalmente './icons/awesome-icon-theme.json'.", + "reqarray": "Ponto de extensão '{0}' deve ser uma matriz.", + "reqpath": "Esperada uma string em `contributes.{0}.path`. Valor informado: {1}", + "reqid": "Esperada sequência em 'contributes.{0}.ID'. Valor fornecido: {1}", + "invalid.path.1": "É esperado que `contributes.{0}.path` ({1}) seja incluído na pasta da extensão ({2}). Isto pode tornar a extensão não portável." +} \ No newline at end of file diff --git a/i18n/ptb/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json b/i18n/ptb/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json index db248225f67..d27ccd6f4ab 100644 --- a/i18n/ptb/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json +++ b/i18n/ptb/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json @@ -4,31 +4,15 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "vscode.extension.contributes.themes": "Contribui com temas de cores do textmate.", - "vscode.extension.contributes.themes.id": "ID do tema do ícone conforme usado em configurações do usuário.", - "vscode.extension.contributes.themes.label": "Etiqueta da cor do tema como mostrado na interface do usuário.", - "vscode.extension.contributes.themes.uiTheme": "Tema base de definição das cores do editor: 'vs' é o tema de cor clara, 'vs-dark' é o tema de cor escura. 'hc preto' é o tema escuro de alto contraste.", - "vscode.extension.contributes.themes.path": "Caminho do arquivo tmTheme. O caminho é relativo à pasta de extensão e é normalmente './themes/themeFile.tmTheme'.", - "vscode.extension.contributes.iconThemes": "Contribui com temas de ícones de arquivo.", - "vscode.extension.contributes.iconThemes.id": "ID do tema do ícone como usado em configurações do usuário.", - "vscode.extension.contributes.iconThemes.label": "Etiqueta do tema do ícone como mostrado na interface do usuário.", - "vscode.extension.contributes.iconThemes.path": "Caminho do arquivo de definição do tema do ícone. O caminho é relativo à pasta de extensão e é normalmente './icons/awesome-icon-theme.json'.", "migration.completed": "Foram adicionadas novas configurações de tema para as configurações de usuário. Backup está disponível em {0}.", "error.cannotloadtheme": "Não é possível carregar {0}: {1}", - "reqarray": "Ponto de extensão '{0}' deve ser uma matriz.", - "reqpath": "Esperada uma string em `contributes.{0}.path`. Valor informado: {1}", - "invalid.path.1": "É esperado que `contributes.{0}.path` ({1}) seja incluído na pasta da extensão ({2}). Isto pode tornar a extensão não portável.", - "reqid": "Esperada sequência em 'contributes.{0}.ID'. Valor fornecido: {1}", "error.cannotloadicontheme": "Não é possível carregar {0}", - "error.cannotparseicontheme": "Problemas de análise do arquivo de ícones: {0}", "colorTheme": "Especifica o tema de cores usado no espaço de trabalho.", "colorThemeError": "Tema é desconhecido ou não está instalado.", "iconTheme": "Especifica o tema de ícones usado no espaço de trabalho ou 'null' para não mostrar qualquer arquivo de ícones.", "noIconThemeDesc": "Nenhum arquivo de ícones", "iconThemeError": "Arquivo de tema de ícones é desconhecido ou não está instalado.", "workbenchColors": "Substitui as cores do tema do tema de cores atualmente selecionado.", - "workbenchColors.deprecated": "A configuração não é mais experimental e foi renomeada para 'workbench.colorCustomizations'", - "workbenchColors.deprecatedDescription": "Use 'workbench.colorCustomizations'", "editorColors": "Substitui as cores e o estilo da fonte do editor do tema de cores atualmente selecionado.", "editorColors.comments": "Define as cores e estilos para os comentários", "editorColors.strings": "Define as cores e estilos para textos literais.", diff --git a/i18n/ptb/src/vs/workbench/services/workspace/node/workspaceEditingService.i18n.json b/i18n/ptb/src/vs/workbench/services/workspace/node/workspaceEditingService.i18n.json new file mode 100644 index 00000000000..ddf43c7774f --- /dev/null +++ b/i18n/ptb/src/vs/workbench/services/workspace/node/workspaceEditingService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "openWorkspaceConfigurationFile": "Abrir o Arquivo de Configuração do Espaço de Trabalho", + "close": "Fechar" +} \ No newline at end of file diff --git a/i18n/rus/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json b/i18n/rus/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json index 8760c57e674..a691501ffe0 100644 --- a/i18n/rus/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json +++ b/i18n/rus/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json @@ -4,13 +4,6 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "activeEditorShort": "например, myFile.txt", - "activeEditorMedium": "например, myFolder/myFile.txt", - "activeEditorLong": "например, /Users/Development/myProject/myFolder/myFile.txt", - "rootName": "например, myFolder1, myFolder2, myFolder3", - "rootPath": "например, /Users/Development/myProject", - "folderName": "например, myFolder", - "folderPath": "например, /Users/Development/myFolder", "appName": "например, VS Code", "dirty": "индикатор dirty, если активный редактор является \"грязным\"", "separator": "условный разделитель (-), который отображается, только если окружен переменными со значениями", diff --git a/i18n/rus/extensions/emmet/package.i18n.json b/i18n/rus/extensions/emmet/package.i18n.json index 5df2b4e5410..3c7e7099a9d 100644 --- a/i18n/rus/extensions/emmet/package.i18n.json +++ b/i18n/rus/extensions/emmet/package.i18n.json @@ -28,13 +28,6 @@ "command.incrementNumberByTen": "Увеличить значение на 10", "command.decrementNumberByTen": "Уменьшить значение на 10", "emmetSyntaxProfiles": "Задайте профиль для указанного синтаксиса или используйте свой собственный профиль с определенными правилами.", - "emmetExclude": "Массив языков, в которых не должны развертываться сокращения Emmet.", - "emmetExtensionsPath": "Путь к папке, содержащей профили Emmet и фрагменты кода.", - "emmetShowExpandedAbbreviation": "Отображает развернутые сокращения Emmet в виде предложений.\nПараметр \"inMarkupAndStylesheetFilesOnly\" применяется к html, haml, jade, slim, xml, xsl, css, scss, sass, less и stylus .\nПараметр \"always\" применяется ко всем частям файла независимо от разметки и стилей.", - "emmetShowAbbreviationSuggestions": "Отображает возможные сокращения Emmet в виде предложений. Не применяется в таблицах стилей или если параметр emmet.showExpandedAbbreviation имеет значение \"never\".", - "emmetIncludeLanguages": "Включает сокращения Emmet в языках, которые не поддерживаются по умолчанию. Здесь можно указать связь между не поддерживаемым и поддерживаемым языками.\nПример: {\"vue-html\": \"html\", \"javascript\": \"javascriptreact\"}", - "emmetVariables": "Переменные, которые будут использоваться во фрагментах Emmet", - "emmetTriggerExpansionOnTab": "Если включено, сокращения Emmet разворачиваются при нажатии клавиши TAB.", "emmetPreferences": "Настройки, которые используются для изменения поведения некоторых действий и сопоставителей Emmet.", "emmetPreferencesIntUnit": "Единица по умолчанию для целочисленных значений", "emmetPreferencesFloatUnit": "Единица по умолчанию для значений с плавающей запятой", @@ -43,6 +36,5 @@ "emmetPreferencesStylusAfter": "Символ, который будет помещен в конце свойства CSS при развертывании сокращений CSS в файлах Stylus", "emmetPreferencesCssBetween": "Символ, который будет помещен между свойством CSS и значением при развертывании сокращений CSS", "emmetPreferencesSassBetween": "Символ, который будет помещен между свойством CSS и значением при развертывании сокращений CSS в файлах Sass", - "emmetPreferencesStylusBetween": "Символ, который будет помещен между свойством CSS и значением при развертывании сокращений CSS в файлах Stylus", - "emmetShowSuggestionsAsSnippets": "Если этот параметр имеет значение true, предложения Emmet будут отображаться в виде фрагментов, которые можно упорядочить с помощью параметра editor.snippetSuggestions." + "emmetPreferencesStylusBetween": "Символ, который будет помещен между свойством CSS и значением при развертывании сокращений CSS в файлах Stylus" } \ No newline at end of file diff --git a/i18n/rus/extensions/git/out/commands.i18n.json b/i18n/rus/extensions/git/out/commands.i18n.json index 633cd74962b..f8b4fce36b0 100644 --- a/i18n/rus/extensions/git/out/commands.i18n.json +++ b/i18n/rus/extensions/git/out/commands.i18n.json @@ -12,8 +12,8 @@ "cloning": "Клонируется репозиторий Git...", "openrepo": "Открыть репозиторий", "proposeopen": "Вы хотите открыть клонированный репозиторий?", - "path to init": "Путь к папке", - "provide path": "Укажите путь к папке для инициализации репозитория Git", + "init repo": "Инициализировать репозиторий", + "create repo": "Инициализировать репозиторий", "HEAD not available": "Версия HEAD '{0}' недоступна.", "confirm stage files with merge conflicts": "Вы действительно хотите перенести в промежуточный этап файлы с конфликтами слияния ({0})?", "confirm stage file with merge conflicts": "Вы действительно хотите перенести в промежуточный этап файлы с конфликтами слияния ({0})?", diff --git a/i18n/rus/extensions/git/out/repository.i18n.json b/i18n/rus/extensions/git/out/repository.i18n.json index 053f8f8b45f..019265640e5 100644 --- a/i18n/rus/extensions/git/out/repository.i18n.json +++ b/i18n/rus/extensions/git/out/repository.i18n.json @@ -21,6 +21,8 @@ "deleted by us": "Удалено нами", "both added": "Добавлено обеими сторонами", "both modified": "Изменено обеими сторонами", + "untracked, short": "U", + "modified, short": "M", "commit": "Commit", "merge changes": "Объединить изменения", "staged changes": "Промежуточно сохраненные изменения", diff --git a/i18n/rus/extensions/typescript/package.i18n.json b/i18n/rus/extensions/typescript/package.i18n.json index aeaeec49a6a..15940c3fc59 100644 --- a/i18n/rus/extensions/typescript/package.i18n.json +++ b/i18n/rus/extensions/typescript/package.i18n.json @@ -44,7 +44,6 @@ "typescript.npm": "Указывает путь к исполняемому файлу NPM, используемому для автоматического получения типа. Требуется TypeScript версии 2.3.4 или более поздней версии.", "typescript.check.npmIsInstalled": "Проверяет, установлен ли NPM для автоматического получения типов.", "javascript.nameSuggestions": "Включить/отключить использование уникальных имен из файла в списках предложений JavaScript.", - "typescript.tsc.autoDetect": "Включает или отключает автоматическое определние заданий tsc.", "typescript.problemMatchers.tsc.label": "Проблемы TypeScript", "typescript.problemMatchers.tscWatch.label": "Проблемы TypeScript (режим наблюдения)" } \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/contrib/find/browser/findWidget.i18n.json b/i18n/rus/src/vs/editor/contrib/find/browser/findWidget.i18n.json index 56455258496..32fb3e012c8 100644 --- a/i18n/rus/src/vs/editor/contrib/find/browser/findWidget.i18n.json +++ b/i18n/rus/src/vs/editor/contrib/find/browser/findWidget.i18n.json @@ -15,7 +15,6 @@ "label.replaceButton": "Заменить", "label.replaceAllButton": "Заменить все", "label.toggleReplaceButton": "Режим \"Переключение замены\"", - "title.matchesCountLimit": "Отображаются только первые 999 результатов, но все операции поиска выполняются со всем текстом.", "label.matchesLocation": "{0} из {1}", "label.noResults": "Нет результатов" } \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/contrib/find/common/findController.i18n.json b/i18n/rus/src/vs/editor/contrib/find/common/findController.i18n.json index 5cbc15bce8a..5c6f87afd29 100644 --- a/i18n/rus/src/vs/editor/contrib/find/common/findController.i18n.json +++ b/i18n/rus/src/vs/editor/contrib/find/common/findController.i18n.json @@ -10,12 +10,6 @@ "nextSelectionMatchFindAction": "Найти следующее выделение", "previousSelectionMatchFindAction": "Найти предыдущее выделение", "startReplace": "Заменить", - "addSelectionToNextFindMatch": "Добавить выделение в следующее найденное совпадение", - "addSelectionToPreviousFindMatch": "Добавить выделенный фрагмент в предыдущее найденное совпадение", - "moveSelectionToNextFindMatch": "Переместить последнее выделение в следующее найденное совпадение", - "moveSelectionToPreviousFindMatch": "Переместить последний выделенный фрагмент в предыдущее найденное совпадение", - "selectAllOccurrencesOfFindMatch": "Выбрать все вхождения найденных совпадений", - "changeAll.label": "Изменить все вхождения", "showNextFindTermAction": "Показать следующий найденный термин", "showPreviousFindTermAction": "Показать предыдущий найденный термин" } \ No newline at end of file diff --git a/i18n/rus/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json b/i18n/rus/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json index de410095204..660193a943e 100644 --- a/i18n/rus/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json +++ b/i18n/rus/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json @@ -6,5 +6,11 @@ { "mutlicursor.insertAbove": "Добавить курсор выше", "mutlicursor.insertBelow": "Добавить курсор ниже", - "mutlicursor.insertAtEndOfEachLineSelected": "Добавить курсоры к окончаниям строк" + "mutlicursor.insertAtEndOfEachLineSelected": "Добавить курсоры к окончаниям строк", + "addSelectionToNextFindMatch": "Добавить выделение в следующее найденное совпадение", + "addSelectionToPreviousFindMatch": "Добавить выделенный фрагмент в предыдущее найденное совпадение", + "moveSelectionToNextFindMatch": "Переместить последнее выделение в следующее найденное совпадение", + "moveSelectionToPreviousFindMatch": "Переместить последний выделенный фрагмент в предыдущее найденное совпадение", + "selectAllOccurrencesOfFindMatch": "Выбрать все вхождения найденных совпадений", + "changeAll.label": "Изменить все вхождения" } \ No newline at end of file diff --git a/i18n/rus/src/vs/platform/theme/common/colorRegistry.i18n.json b/i18n/rus/src/vs/platform/theme/common/colorRegistry.i18n.json index dbab65f5dc7..4de61ec7ef4 100644 --- a/i18n/rus/src/vs/platform/theme/common/colorRegistry.i18n.json +++ b/i18n/rus/src/vs/platform/theme/common/colorRegistry.i18n.json @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "invalid.color": "Недопустимый формат цвета. Используйте #RGB, #RGBA, #RRGGBB или #RRGGBBAA", "schema.colors": "Цвета, используемые на рабочем месте.", "foreground": "Общий цвет переднего плана. Этот цвет используется, только если его не переопределит компонент.", "errorForeground": "Общий цвет переднего плана для сообщений об ошибках. Этот цвет используется только если его не переопределяет компонент.", 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 3dc67610b94..8ad801af2f1 100644 --- a/i18n/rus/src/vs/workbench/browser/actions/workspaceActions.i18n.json +++ b/i18n/rus/src/vs/workbench/browser/actions/workspaceActions.i18n.json @@ -13,6 +13,7 @@ "select": "&&Выбрать", "selectWorkspace": "Выбрать папки для рабочей области", "removeFolderFromWorkspace": "Удалить папку из рабочей области", + "openFolderSettings": "Открыть параметры папок", "saveWorkspaceAsAction": "Сохранить рабочую область как...", "save": "Сохранить", "saveWorkspace": "Сохранить рабочую область", diff --git a/i18n/rus/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json b/i18n/rus/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json index cdc81741e9e..946bb84393f 100644 --- a/i18n/rus/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json +++ b/i18n/rus/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json @@ -5,6 +5,5 @@ // Do not edit this file. It is machine generated. { "hideActivitBar": "Скрыть панель действий", - "activityBarAriaLabel": "Переключатель активного представления", "globalActions": "Глобальные действия" } \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/browser/parts/compositebar/compositeBar.i18n.json b/i18n/rus/src/vs/workbench/browser/parts/compositebar/compositeBar.i18n.json new file mode 100644 index 00000000000..c9f39eaf3a5 --- /dev/null +++ b/i18n/rus/src/vs/workbench/browser/parts/compositebar/compositeBar.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. +{ + "activityBarAriaLabel": "Переключатель активного представления" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/browser/parts/compositebar/compositeBarActions.i18n.json b/i18n/rus/src/vs/workbench/browser/parts/compositebar/compositeBarActions.i18n.json new file mode 100644 index 00000000000..0e338824c87 --- /dev/null +++ b/i18n/rus/src/vs/workbench/browser/parts/compositebar/compositeBarActions.i18n.json @@ -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. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "badgeTitle": "{0} - {1}", + "additionalViews": "Дополнительные представления", + "numberBadge": "{0} ({1})", + "manageExtension": "Управление расширениями", + "titleKeybinding": "{0} ({1})", + "toggle": "Переключить закрепленное представление" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json b/i18n/rus/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json index 75bbb3e9519..3c123d9ce41 100644 --- a/i18n/rus/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json +++ b/i18n/rus/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json @@ -12,5 +12,6 @@ "groupTwoPicker": "Показать редакторы во второй группе", "groupThreePicker": "Показать редакторы в третьей группе", "allEditorsPicker": "Показать все открытые редакторы", - "view": "Просмотр" + "view": "Просмотр", + "file": "Файл" } \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json b/i18n/rus/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json index 000e440a7b7..2fc29a94bab 100644 --- a/i18n/rus/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json +++ b/i18n/rus/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json @@ -4,6 +4,5 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "canNotRun": "Команда \"{0}\" сейчас неактивна, и ее невозможно выполнить.", "manageExtension": "Управление расширениями" } \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/electron-browser/main.contribution.i18n.json b/i18n/rus/src/vs/workbench/electron-browser/main.contribution.i18n.json index 1de29e6dd0e..6887c80a117 100644 --- a/i18n/rus/src/vs/workbench/electron-browser/main.contribution.i18n.json +++ b/i18n/rus/src/vs/workbench/electron-browser/main.contribution.i18n.json @@ -10,18 +10,14 @@ "workspaces": "Рабочие области", "developer": "Разработчик", "showEditorTabs": "Определяет, должны ли открытые редакторы отображаться на вкладках или нет.", - "workbench.editor.labelFormat.default": "Отображать имя файла. Если вкладки включены и в одной группе есть два файла с одинаковыми именами, будут добавлены различающиеся части пути к каждому из этих файлов. Если вкладки отключены, то для активного редактора отображается путь по отношению к корневому каталогу рабочей области.", "workbench.editor.labelFormat.short": "Отображать имя файла и имя каталога.", - "workbench.editor.labelFormat.medium": "Отображать имя файла и путь к файлу относительно корневого каталога рабочей области.", "workbench.editor.labelFormat.long": "Отображать имя файла и абсолютный путь.", "tabDescription": "Определяет формат метки редактора. Изменив этот параметр, можно сделать более наглядным расположение файла:\n- короткий формат: 'parent'\n- средний формат: 'workspace/src/parent'\n- длинный формат: '/home/user/workspace/src/parent'\n- по умолчанию: '.../parent', если другая вкладка имеет такой же заголовок или относительный путь к рабочей области, если вкладки отключены", "editorTabCloseButton": "Определяет положение кнопок закрытия вкладок редактора или отключает их, если задано значение off.", "showIcons": "Определяет, должны ли открытые редакторы отображаться со значком. Требует включить тему значков.", "enablePreview": "Определяет, отображаются ли открытые редакторы в режиме предварительного просмотра. Редакторы в режиме предварительного просмотра можно использовать, пока они открыты (например, с помощью двойного щелчка мыши или изменения). Текст в таких редакторах отображается курсивом.", "enablePreviewFromQuickOpen": "Определяет, отображаются ли редакторы из Quick Open в режиме предварительного просмотра. Редакторы в режиме предварительного просмотра повторно используются до сохранения (например, с помощью двойного щелчка или изменения).", - "editorOpenPositioning": "Определяет место открытия редакторов. Выберите \"Слева\" или \"Справа\", чтобы открывать редакторы слева или справа от активного сейчас редактора. Выберите \"Первый\" или \"Последний\", чтобы открывать редакторы независимо от активного сейчас редактора.", "revealIfOpen": "Определяет, отображается ли редактор в какой-либо из видимых групп при открытии. Если функция отключена, редактор открывается в текущей активной группе редакторов. Если функция включена, вместо открытия уже открытый редактор будет отображен в текущей активной группе редакторов. Обратите внимание, что в некоторых случаях этот параметр игнорируется, например при принудительном открытии редактора в определенной группе или сбоку от текущей активной группы редакторов.", - "commandHistory": "Определяет количество недавно использованных команд, которые следует хранить в журнале палитры команд. Установите значение 0, чтобы отключить журнал команд.", "preserveInput": "Определяет, следует ли восстановить последнюю введенную команду в палитре команд при следующем открытии палитры.", "closeOnFocusLost": "Управляет автоматическим закрытием Quick Open при потере фокуса.", "openDefaultSettings": "Управляет открытием редактора с отображением всех настроек по умолчанию при открытии настроек.", @@ -50,7 +46,6 @@ "restoreWindows": "Управляет повторным открытием окон после перезапуска. Выберите 'none', чтобы всегда начинать с пустой рабочей области; 'one', чтобы открыть последнее окно, с которым вы работали; 'folders', чтобы открыть все окна с открытыми папками, и 'all', чтобы открыть все окна последнего сеанса.", "restoreFullscreen": "Определяет, должно ли окно восстанавливаться в полноэкранном режиме, если оно было закрыто в полноэкранном режиме.", "zoomLevel": "Настройте масштаб окна. Исходный размер равен 0. Увеличение или уменьшение значения на 1 означает увеличение или уменьшение окна на 20 %. Чтобы более точно задать масштаб, можно также ввести десятичное число.", - "title": "Определяет заголовок окна в зависимости от активного редактора. Подстановка переменных выполняется на основе контекста:\n${activeEditorShort}: например, myFile.txt\n${activeEditorMedium}: например, myFolder/myFile.txt\n${activeEditorLong}: например, /Users/Development/myProject/myFolder/myFile.txt\n${folderName}: например, myFolder\n${folderPath}: например, /Users/Development/myFolder\n${rootName}: например, myFolder1, myFolder2, myFolder3\n${rootPath}: например, /Users/Development/myWorkspace\n${appName}: например, VS Code\n${dirty}: индикатор dirty, если активный редактор является \"грязным\"\n${separator}: условный разделитель (\" - \"), который отображается, только если окружен переменными со значениями ", "window.newWindowDimensions.default": "Открывать новые окна в центре экрана.", "window.newWindowDimensions.inherit": "Открывать новые окна того же размера, что и последнее активное окно.", "window.newWindowDimensions.maximized": "Открывать новые окна в развернутом состоянии.", diff --git a/i18n/rus/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json b/i18n/rus/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json index 820bfa6a8c8..4c4a6981903 100644 --- a/i18n/rus/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json +++ b/i18n/rus/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json @@ -6,6 +6,8 @@ { "entryAriaLabel": "Отладка: {0}", "debugAriaLabel": "Введите имя используемой конфигурации запуска.", + "addConfigTo": "Добавить конфигурацию ({0})...", + "addConfiguration": "Добавить конфигурацию...", "noConfigurationsMatching": "Нет соответствующих конфигураций отладки.", "noConfigurationsFound": "Конфигурации отладки не найдены. Создайте файл \"launch.json\"." } \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/debug/browser/debugStatus.i18n.json b/i18n/rus/src/vs/workbench/parts/debug/browser/debugStatus.i18n.json new file mode 100644 index 00000000000..255ae8e1d50 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/debug/browser/debugStatus.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. +{ + "debug": "Отладка" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/debug/browser/debugViewlet.i18n.json b/i18n/rus/src/vs/workbench/parts/debug/browser/debugViewlet.i18n.json new file mode 100644 index 00000000000..8b6ad71cd4e --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/debug/browser/debugViewlet.i18n.json @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json b/i18n/rus/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json index 7574689b0ef..63d876aca0d 100644 --- a/i18n/rus/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json +++ b/i18n/rus/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json @@ -15,7 +15,6 @@ "vscode.extension.contributes.debuggers.initialConfigurations": "Конфигурации для создания первоначального файла launch.json.", "vscode.extension.contributes.debuggers.languages": "Список языков, для которых расширение отладки может считаться \"отладчиком по умолчанию\".", "vscode.extension.contributes.debuggers.adapterExecutableCommand": "Если задано, VS Code будет вызывать эту команду, чтобы определить путь к исполняемому файлу адаптера отладки и передаваемые аргументы.", - "vscode.extension.contributes.debuggers.startSessionCommand": "Если задано, VS Code будет вызывать эту команду для действий \"отладка\" или \"запуск\", предназначенных для этого расширения.", "vscode.extension.contributes.debuggers.configurationSnippets": "Фрагменты для добавления новых конфигураций в launch.json.", "vscode.extension.contributes.debuggers.configurationAttributes": "Конфигурации схемы JSON для проверки launch.json.", "vscode.extension.contributes.debuggers.windows": "Параметры, связанные с Windows.", diff --git a/i18n/rus/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json b/i18n/rus/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json index 3333c4ffeb2..94b75507832 100644 --- a/i18n/rus/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json +++ b/i18n/rus/src/vs/workbench/parts/debug/electron-browser/debugService.i18n.json @@ -12,10 +12,7 @@ "breakpointRemoved": "Удалена точка останова: строка {0}, файл {1}", "compoundMustHaveConfigurations": "Для составного элемента должен быть задан атрибут configurations для запуска нескольких конфигураций.", "configMissing": "Конфигурация \"{0}\" отсутствует в launch.json.", - "debugRequestNotSupported": "В выбранной конфигурации отладки указано неподдерживаемое значение атрибута '{0}': '{1}'.", - "debugRequesMissing": "В выбранной конфигурации отладки отсутствует атрибут '{0}'.", "debugTypeNotSupported": "Настроенный тип отладки \"{0}\" не поддерживается.", - "debugTypeMissing": "Отсутствует свойство \"type\" для выбранной конфигурации запуска.", "preLaunchTaskErrors": "При выполнении предварительной задачи \"{0}\" обнаружены ошибки.", "preLaunchTaskError": "При выполнении предварительной задачи \"{0}\" обнаружена ошибка.", "preLaunchTaskExitCode": "Выполнение предварительной задачи \"{0}\" завершено с кодом выхода {1}.", diff --git a/i18n/rus/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json b/i18n/rus/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json index 4c742d270df..c7556643f1a 100644 --- a/i18n/rus/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json +++ b/i18n/rus/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json @@ -7,6 +7,5 @@ "stateCapture": "Состояние объекта записывается после первого вычисления", "replVariableAriaLabel": "Переменная \"{0}\" имеет значение \"{1}\", read–eval–print loop, отладка", "replExpressionAriaLabel": "Выражение \"{0}\" имеет значение \"{1}\", read–eval–print loop, отладка", - "replValueOutputAriaLabel": "{0}, read–eval–print loop, отладка", - "replKeyValueOutputAriaLabel": "Выходная переменная \"{0}\" имеет значение \"{1}\", read–eval–print loop, отладка" + "replValueOutputAriaLabel": "{0}, read–eval–print loop, отладка" } \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json b/i18n/rus/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json index 9a2b3bf6390..821930bfc87 100644 --- a/i18n/rus/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json +++ b/i18n/rus/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "filesCategory": "Файлы", + "filesCategory": "Файл", "revealInSideBar": "Показать в боковой панели", "acceptLocalChanges": "Использовать изменения и перезаписать содержимое диска", "revertLocalChanges": "Отменить изменения и вернуться к содержимому на диске" diff --git a/i18n/rus/src/vs/workbench/parts/files/browser/fileActions.i18n.json b/i18n/rus/src/vs/workbench/parts/files/browser/fileActions.i18n.json index 61840ca236b..b5890f55c3b 100644 --- a/i18n/rus/src/vs/workbench/parts/files/browser/fileActions.i18n.json +++ b/i18n/rus/src/vs/workbench/parts/files/browser/fileActions.i18n.json @@ -37,8 +37,6 @@ "openToSide": "Открыть сбоку", "compareSource": "Выбрать для сравнения", "globalCompareFile": "Сравнить активный файл с...", - "pickHistory": "Выберите предыдущий открытый файл для сравнения.", - "unableToFileToCompare": "Выбранный файл нельзя сравнить с \"{0}\".", "openFileToCompare": "Чтобы сравнить файл с другим файлом, сначала откройте его.", "compareWith": "Сравнить '{0}' с '{1}'", "compareFiles": "Сравнить файлы", @@ -47,7 +45,6 @@ "saveAs": "Сохранить как...", "saveAll": "Сохранить все", "saveAllInGroup": "Сохранить все в группе", - "saveFiles": "Сохранить файлы с изменениями", "revert": "Отменить изменения в файле", "focusOpenEditors": "Фокус на представлении открытых редакторов", "focusFilesExplorer": "Фокус на проводнике", diff --git a/i18n/rus/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json b/i18n/rus/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json index 5eb9bd6927b..5c9b67b2669 100644 --- a/i18n/rus/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json +++ b/i18n/rus/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json @@ -6,6 +6,6 @@ { "noWorkspace": "Нет открытой папки", "explorerSection": "Раздел проводника", - "noWorkspaceHelp": "Вы еще не открыли папку.", + "noFolderHelp": "Вы еще не открыли папку.", "openFolder": "Открыть папку" } \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/markers/browser/markersFileDecorations.i18n.json b/i18n/rus/src/vs/workbench/parts/markers/browser/markersFileDecorations.i18n.json new file mode 100644 index 00000000000..1ffd1cf8897 --- /dev/null +++ b/i18n/rus/src/vs/workbench/parts/markers/browser/markersFileDecorations.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. +{ + "label": "Проблемы" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json b/i18n/rus/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json index 71bf804e800..ef1380536bf 100644 --- a/i18n/rus/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json +++ b/i18n/rus/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json @@ -5,7 +5,6 @@ // Do not edit this file. It is machine generated. { "emptyUserSettingsHeader": "Укажите параметры здесь, чтобы перезаписать параметры по умолчанию.", - "errorInvalidConfiguration": "Не удается записать параметры. Устраните ошибки и предупреждения в файле и повторите попытку.", "emptyWorkspaceSettingsHeader": "Укажите параметры здесь, чтобы перезаписать параметры пользователей.", "emptyFolderSettingsHeader": "Укажите параметры папок здесь, чтобы перезаписать параметры рабочих областей.", "defaultFolderSettingsTitle": "Параметры папок по умолчанию", diff --git a/i18n/rus/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json b/i18n/rus/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json index 631ee068c6b..a7528085d74 100644 --- a/i18n/rus/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json +++ b/i18n/rus/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json @@ -13,7 +13,6 @@ "actionNotEnabled": "Команда {0} не разрешена в текущем контексте.", "recentlyUsed": "недавно использованные", "morecCommands": "другие команды", - "commandLabel": "{0}: {1}", "cat.title": "{0}: {1}", "noCommandsMatching": "Нет соответствующих команд" } \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/search/browser/search.contribution.i18n.json b/i18n/rus/src/vs/workbench/parts/search/browser/search.contribution.i18n.json index f26e342d6b3..6d0870f5800 100644 --- a/i18n/rus/src/vs/workbench/parts/search/browser/search.contribution.i18n.json +++ b/i18n/rus/src/vs/workbench/parts/search/browser/search.contribution.i18n.json @@ -17,7 +17,6 @@ "exclude": "Настройте стандартные маски для исключения файлов и папок при поиске. Все стандартные маски наследуются от параметра file.exclude.", "exclude.boolean": "Стандартная маска, соответствующая путям к файлам. Задайте значение true или false, чтобы включить или отключить маску.", "exclude.when": "Дополнительная проверка элементов того же уровня соответствующего файла. Используйте $(basename) в качестве переменной для соответствующего имени файла.", - "useRipgrep": "Определяет, использовать ли ripgrep в текстовом поиске", "useIgnoreFilesByDefault": "Определяет, следует ли использовать GITIGNORE- и IGNORE-файлы по умолчанию при поиске в новой рабочей области.", "search.quickOpen.includeSymbols": "Настройте для включения результатов поиска глобальных символов в файлы по запросу для Quick Open." } \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/search/browser/searchActions.i18n.json b/i18n/rus/src/vs/workbench/parts/search/browser/searchActions.i18n.json index a60edb8ea77..969c020706c 100644 --- a/i18n/rus/src/vs/workbench/parts/search/browser/searchActions.i18n.json +++ b/i18n/rus/src/vs/workbench/parts/search/browser/searchActions.i18n.json @@ -19,7 +19,6 @@ "ClearSearchResultsAction.label": "Очистить результаты поиска", "FocusNextSearchResult.label": "Перейти к следующему результату поиска.", "FocusPreviousSearchResult.label": "Перейти к предыдущему результату поиска.", - "RemoveAction.label": "Удалить", "file.replaceAll.label": "Заменить все", "match.replace.label": "Заменить" } \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json b/i18n/rus/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json index 8f0ce17a110..d771b1fdbdc 100644 --- a/i18n/rus/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json +++ b/i18n/rus/src/vs/workbench/parts/snippets/electron-browser/snippetsService.i18n.json @@ -10,8 +10,8 @@ "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.", + "badFile": "Не удалось прочитать файл фрагмента \"{0}\".", "source.snippet": "Фрагмент кода пользователя", "detail.snippet": "{0} ({1})", "snippetSuggest.longLabel": "{0}, {1}" diff --git a/i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json b/i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json index 05cb39eed74..18331d6a712 100644 --- a/i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json +++ b/i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminalActions.i18n.json @@ -16,7 +16,6 @@ "workbench.action.terminal.new.short": "Новый терминал", "workbench.action.terminal.focus": "Фокус на терминале", "workbench.action.terminal.focusNext": "Фокус на следующем терминале", - "workbench.action.terminal.focusAtIndex": "Фокус на терминале {0}", "workbench.action.terminal.focusPrevious": "Фокус на предыдущем терминале", "workbench.action.terminal.paste": "Вставить в активный терминал", "workbench.action.terminal.DefaultShell": "Выбрать оболочку по умолчанию", diff --git a/i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json b/i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json index 92eaeea4fb9..80ae6596199 100644 --- a/i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json +++ b/i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json @@ -5,7 +5,6 @@ // Do not edit this file. It is machine generated. { "copy": "Копировать", - "createNewTerminal": "Новый терминал", "paste": "Вставить", "selectAll": "Выбрать все", "clear": "Очистить" diff --git a/i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json b/i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json index 77b64a5be2d..440d374ef82 100644 --- a/i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json +++ b/i18n/rus/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json @@ -10,6 +10,5 @@ "never again": "ОК. Больше не показывать", "terminal.integrated.chooseWindowsShell": "Выберите предпочитаемую оболочку терминала. Ее можно позже изменить в параметрах", "terminalService.terminalCloseConfirmationSingular": "Есть активный сеанс терминала, завершить его?", - "terminalService.terminalCloseConfirmationPlural": "Есть несколько активных сеансов терминала ({0}), завершить их?", - "yes": "Да" + "terminalService.terminalCloseConfirmationPlural": "Есть несколько активных сеансов терминала ({0}), завершить их?" } \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/services/configuration/common/configurationExtensionPoint.i18n.json b/i18n/rus/src/vs/workbench/services/configuration/common/configurationExtensionPoint.i18n.json new file mode 100644 index 00000000000..42442e0f21a --- /dev/null +++ b/i18n/rus/src/vs/workbench/services/configuration/common/configurationExtensionPoint.i18n.json @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "vscode.extension.contributes.configuration.title": "Краткая сводка параметров. Эта метка будет использоваться в файле параметров в качестве разделяющего комментария.", + "vscode.extension.contributes.configuration.properties": "Описание свойств конфигурации.", + "scope.window.description": "Конфигурация окна, которая может быть задана в параметрах пользователя или рабочей области.", + "scope.resource.description": "Конфигурации ресурсов, которые могут быть заданы в параметрах пользователей, рабочих областей или папок.", + "scope.description": "Область, в которой применяется конфигурация. Доступные области — 'window' и 'resource'.", + "vscode.extension.contributes.configuration": "Добавляет параметры конфигурации.", + "invalid.title": "configuration.title должно быть строкой", + "vscode.extension.contributes.defaultConfiguration": "Предоставляет параметры конфигурации редактора по умолчанию в соответствии с языком.", + "invalid.properties": "configuration.properties должно быть объектом", + "invalid.allOf": "Параметр 'configuration.allOf' является устаревшим, и использовать его не рекомендуется. Вместо этого передайте несколько параметров в виде массива в точку вклада 'configuration'.", + "workspaceConfig.folders.description": "Список папок, которые будут загружены в рабочую область.", + "workspaceConfig.path.description": "Путь к файлу, например, \"/root/folderA\" или \"./folderA\" для пути по отношению к файлу рабочей области.", + "workspaceConfig.name.description": "Необязательное имя папки.", + "workspaceConfig.extensions.description": "Расширения рабочей области", + "unknownWorkspaceProperty": "Неизвестное свойство рабочей области" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/services/editor/common/editorService.i18n.json b/i18n/rus/src/vs/workbench/services/editor/common/editorService.i18n.json new file mode 100644 index 00000000000..50e968f8ee3 --- /dev/null +++ b/i18n/rus/src/vs/workbench/services/editor/common/editorService.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. +{ + "compareLabels": "{0} ↔ {1}" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/services/themes/electron-browser/colorThemeStore.i18n.json b/i18n/rus/src/vs/workbench/services/themes/electron-browser/colorThemeStore.i18n.json new file mode 100644 index 00000000000..c91d95429ee --- /dev/null +++ b/i18n/rus/src/vs/workbench/services/themes/electron-browser/colorThemeStore.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "vscode.extension.contributes.themes": "Contributes textmate color themes.", + "vscode.extension.contributes.themes.id": "Идентификатор темы значка, как используется в параметрах пользователя.", + "vscode.extension.contributes.themes.label": "Метка цветовой схемы, отображаемая в пользовательском интерфейсе.", + "vscode.extension.contributes.themes.uiTheme": "Базовая тема, определяющая цвета оформления редактора: \"vs\" — светлая цветовая тема, \"vs-dark\" — темная цветовая тема. \"hc-black\" — темная высококонтрастная тема.", + "vscode.extension.contributes.themes.path": "Путь к файлу tmTheme. Путь указывается относительно папки расширения и имеет вид \"./themes/themeFile.tmTheme\".", + "reqarray": "Extension point `{0}` must be an array.", + "reqpath": "В contributes.{0}.path требуется строка. Указанное значение: {1}", + "invalid.path.1": "contributes.{0}.path ({1}) должен был быть включен в папку расширения ({2}). Это может сделать расширение непереносимым." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.i18n.json b/i18n/rus/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.i18n.json new file mode 100644 index 00000000000..b0f9faf6b52 --- /dev/null +++ b/i18n/rus/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.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. +{ + "error.cannotparseicontheme": "Problems parsing file icons file: {0}" +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.i18n.json b/i18n/rus/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.i18n.json new file mode 100644 index 00000000000..d03dc07b9a9 --- /dev/null +++ b/i18n/rus/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "vscode.extension.contributes.iconThemes": "Contributes file icon themes.", + "vscode.extension.contributes.iconThemes.id": "Идентификатор темы значка, как используется в параметрах пользователя.", + "vscode.extension.contributes.iconThemes.label": "Метка темы значка, как отображается в пользовательском интерфейсе.", + "vscode.extension.contributes.iconThemes.path": "Путь к файлу определения темы значка. Путь задается относительно папки расширения и, как правило, имеет следующий вид: \"./icons/awesome-icon-theme.json\".", + "reqarray": "Extension point `{0}` must be an array.", + "reqpath": "В contributes.{0}.path требуется строка. Указанное значение: {1}", + "reqid": "Ожидалась строка в \"contributes.{0}.id\". Указанное значение: {1}", + "invalid.path.1": "contributes.{0}.path ({1}) должен был быть включен в папку расширения ({2}). Это может сделать расширение непереносимым." +} \ No newline at end of file diff --git a/i18n/rus/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json b/i18n/rus/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json index 8caee5d0eae..e2aef412ef5 100644 --- a/i18n/rus/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json +++ b/i18n/rus/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json @@ -4,31 +4,15 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "vscode.extension.contributes.themes": "Contributes textmate color themes.", - "vscode.extension.contributes.themes.id": "Идентификатор темы значка, как используется в параметрах пользователя.", - "vscode.extension.contributes.themes.label": "Метка цветовой схемы, отображаемая в пользовательском интерфейсе.", - "vscode.extension.contributes.themes.uiTheme": "Базовая тема, определяющая цвета оформления редактора: \"vs\" — светлая цветовая тема, \"vs-dark\" — темная цветовая тема. \"hc-black\" — темная высококонтрастная тема.", - "vscode.extension.contributes.themes.path": "Путь к файлу tmTheme. Путь указывается относительно папки расширения и имеет вид \"./themes/themeFile.tmTheme\".", - "vscode.extension.contributes.iconThemes": "Contributes file icon themes.", - "vscode.extension.contributes.iconThemes.id": "Идентификатор темы значка, как используется в параметрах пользователя.", - "vscode.extension.contributes.iconThemes.label": "Метка темы значка, как отображается в пользовательском интерфейсе.", - "vscode.extension.contributes.iconThemes.path": "Путь к файлу определения темы значка. Путь задается относительно папки расширения и, как правило, имеет следующий вид: \"./icons/awesome-icon-theme.json\".", "migration.completed": "В параметры пользователя были добавлены новые параметры темы. Резервная копия доступна в {0}.", "error.cannotloadtheme": "Unable to load {0}: {1}", - "reqarray": "Extension point `{0}` must be an array.", - "reqpath": "В contributes.{0}.path требуется строка. Указанное значение: {1}", - "invalid.path.1": "contributes.{0}.path ({1}) должен был быть включен в папку расширения ({2}). Это может сделать расширение непереносимым.", - "reqid": "Ожидалась строка в \"contributes.{0}.id\". Указанное значение: {1}", "error.cannotloadicontheme": "Unable to load {0}", - "error.cannotparseicontheme": "Problems parsing file icons file: {0}", "colorTheme": "Specifies the color theme used in the workbench.", "colorThemeError": "Theme is unknown or not installed.", "iconTheme": "Указывает тему значков, используемую в рабочей области. Чтобы значки файлов не отображались, используйте значение 'null'.", "noIconThemeDesc": "No file icons", "iconThemeError": "File icon theme is unknown or not installed.", "workbenchColors": "Переопределяет цвета из выбранной цветовой темы.", - "workbenchColors.deprecated": "Параметр больше не является экспериментальным и был переименован в 'workbench.colorCustomizations'", - "workbenchColors.deprecatedDescription": "Используйте параметр 'workbench.colorCustomizations'", "editorColors": "Переопределяет цвета редактора и стиль шрифта из текущей выбранной цветовой темы.", "editorColors.comments": "Задает цвета и стили для комментариев", "editorColors.strings": "Задает цвета и стили для строковых литералов.", diff --git a/i18n/rus/src/vs/workbench/services/workspace/node/workspaceEditingService.i18n.json b/i18n/rus/src/vs/workbench/services/workspace/node/workspaceEditingService.i18n.json new file mode 100644 index 00000000000..585cdb62fd6 --- /dev/null +++ b/i18n/rus/src/vs/workbench/services/workspace/node/workspaceEditingService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "openWorkspaceConfigurationFile": "Открыть файл конфигурации рабочей области", + "close": "Закрыть" +} \ No newline at end of file diff --git a/i18n/trk/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json b/i18n/trk/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json index cffb38fa05c..4f4f8629e96 100644 --- a/i18n/trk/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json +++ b/i18n/trk/extensions/configuration-editing/out/settingsDocumentHelper.i18n.json @@ -4,13 +4,6 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "activeEditorShort": "ör. myFile.txt", - "activeEditorMedium": "ör. myFolder/myFile.txt", - "activeEditorLong": "ör. /Users/Development/myProject/myFolder/myFile.txt", - "rootName": "ör: myFolder1, myFolder2, myFolder3", - "rootPath": "ör. /Users/Development/myProject", - "folderName": "ör: myFolder", - "folderPath": "ör: /Users/Development/myFolder", "appName": "ör. VS Code", "dirty": "değişiklik göstergesi, aktif düzenleyici kaydedilmemiş değişiklikler içeriyorsa", "separator": "koşullu ayırıcı ('-') sadece değişkenler tarafından değerlerle çevrildiğinde gösterir", diff --git a/i18n/trk/extensions/emmet/package.i18n.json b/i18n/trk/extensions/emmet/package.i18n.json index e1f898b22d3..d22e3044f1e 100644 --- a/i18n/trk/extensions/emmet/package.i18n.json +++ b/i18n/trk/extensions/emmet/package.i18n.json @@ -28,13 +28,6 @@ "command.incrementNumberByTen": "10 Arttır", "command.decrementNumberByTen": "10 Azalt", "emmetSyntaxProfiles": "Belirtilen sentaks için profil tanımlayın veya kendi profilinizi belirli kurallarla kullanın.", - "emmetExclude": "Emmet kısaltmalarının genişletilmeyeceği bir diller dizisi.", - "emmetExtensionsPath": "Emmet profileri ve parçacıklarını içeren bir klasör yolu.'", - "emmetShowExpandedAbbreviation": "Genişletilmiş emmet kısaltmalarını öneriler olarak gösterir.\n\"inMarkupAndStylesheetFilesOnly\" seçeneği\" html, haml, jade, xml, xsl, css, scss, sass, less ve stylus'a uygulanır.\n\"always\" seçeneği işaretleme/css'den bağımsız olarak dosyanın tüm bölümlerine uygulanır.", - "emmetShowAbbreviationSuggestions": "Olası emmet kısaltmalarını öneriler olarak gösterir. Stil dosyalarında veya emmet.showExpandedAbbreviation, \"never\" olarak ayarlandığında uygulanamaz.", - "emmetIncludeLanguages": "Varsayılan olarak desteklenmeyen dillerde emmet kısaltmalarını etkinleştirin. Burada dil ile desteklenen emmet destekli dil arasında eşleme ekleyin.", - "emmetVariables": "Emmet parçacıklarında kullanılacak değişkenler", - "emmetTriggerExpansionOnTab": "Etkinleştirildiğinde, emmet kısaltmaları TAB tuşuna basıldığında genişletilir.", "emmetPreferences": "Emmet'in bazı eylemleri ve çözümleyicilerinin davranışını değiştirmek için kullanılacak tercihler.", "emmetPreferencesIntUnit": "Tam sayı değerleri için varsayılan birim", "emmetPreferencesFloatUnit": "Ondalık sayı değerleri için varsayılan birim", @@ -43,6 +36,5 @@ "emmetPreferencesStylusAfter": "Stylus dosyalarında CSS kısaltmaları genişletilirken CSS özelliğinin sonuna koyulacak sembol", "emmetPreferencesCssBetween": "CSS kısaltmaları genişletilirken CSS özelliği ve değerinin arasına koyulacak sembol", "emmetPreferencesSassBetween": "Sass dosyalarında CSS kısaltmaları genişletilirken CSS özelliği ve değerinin arasına koyulacak sembol", - "emmetPreferencesStylusBetween": "Stylus dosyalarında CSS kısaltmaları genişletilirken CSS özelliği ve değerinin arasına koyulacak sembol", - "emmetShowSuggestionsAsSnippets": "Doğru ise, emmet önerileri, editor.snippetSuggestions ayarı ile sıralayabilmenizi sağlayan parçacıklar olarak gösterilir." + "emmetPreferencesStylusBetween": "Stylus dosyalarında CSS kısaltmaları genişletilirken CSS özelliği ve değerinin arasına koyulacak sembol" } \ No newline at end of file diff --git a/i18n/trk/extensions/git/out/commands.i18n.json b/i18n/trk/extensions/git/out/commands.i18n.json index 062e690a02d..324d93134ea 100644 --- a/i18n/trk/extensions/git/out/commands.i18n.json +++ b/i18n/trk/extensions/git/out/commands.i18n.json @@ -12,8 +12,8 @@ "cloning": "Git deposu kopyalanıyor...", "openrepo": "Depoyu Aç", "proposeopen": "Kopyalanan depoyu açmak ister misiniz?", - "path to init": "Klasör yolu", - "provide path": "Git deposu oluşturmak için lütfen bir klasör yolu belirtin", + "init repo": "Depo Oluştur", + "create repo": "Depo Oluştur", "HEAD not available": "'{0}'e ait HEAD sürümü mevcut değil.", "confirm stage files with merge conflicts": "Birleştirme çakışmaları bulunan {0} dosyayı hazırlamak istediğinizden emin misiniz?", "confirm stage file with merge conflicts": "Birleştirme çakışmaları bulunan {0} klasörünü hazırlamak istediğinizden emin misiniz?", diff --git a/i18n/trk/extensions/git/out/repository.i18n.json b/i18n/trk/extensions/git/out/repository.i18n.json index 4f0f8a1a395..77b9fa01352 100644 --- a/i18n/trk/extensions/git/out/repository.i18n.json +++ b/i18n/trk/extensions/git/out/repository.i18n.json @@ -21,6 +21,8 @@ "deleted by us": "Bizim Tarafımızdan Silindi", "both added": "Her İkimiz de Ekledik", "both modified": "Her İkimiz de Değiştirdik", + "untracked, short": "Z", + "modified, short": "D", "commit": "Commit'le", "merge changes": "Değişiklikleri Birleştir", "staged changes": "Hazırlanmış Değişiklikler", diff --git a/i18n/trk/extensions/typescript/package.i18n.json b/i18n/trk/extensions/typescript/package.i18n.json index 6a51d6c3ed7..a4900ef3eea 100644 --- a/i18n/trk/extensions/typescript/package.i18n.json +++ b/i18n/trk/extensions/typescript/package.i18n.json @@ -44,7 +44,6 @@ "typescript.npm": "Otomatik Tür Kazanımı için kullanılacak NPM yürütülebilir dosyasının yolunu belirtir. TypeScript >= 2.3.4 gerektirir.", "typescript.check.npmIsInstalled": "Otomatik Tür Kazanımı için NPM'in yüklü olup olmadığını kontrol et.", "javascript.nameSuggestions": "JavaScript öneri listelerindeki dosyadan benzersiz adları eklemeyi etkinleştir veya devre dışı bırak.", - "typescript.tsc.autoDetect": "Tsc görevlerinin otomatik olarak algılanıp algılanmayacağını denetler. Varsayılan olarak açıktır.", "typescript.problemMatchers.tsc.label": "TypeScript sorunları", "typescript.problemMatchers.tscWatch.label": "TypeScript sorunları (izleme modu)" } \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/contrib/find/browser/findWidget.i18n.json b/i18n/trk/src/vs/editor/contrib/find/browser/findWidget.i18n.json index e483c295f19..52b4e3de03f 100644 --- a/i18n/trk/src/vs/editor/contrib/find/browser/findWidget.i18n.json +++ b/i18n/trk/src/vs/editor/contrib/find/browser/findWidget.i18n.json @@ -15,7 +15,6 @@ "label.replaceButton": "Değiştir", "label.replaceAllButton": "Tümünü Değiştir", "label.toggleReplaceButton": "Değiştirme modunu değiştir", - "title.matchesCountLimit": "Yalnızca ilk 999 sonuç vurgulandı, ancak tüm bulma işlemleri metnin tamamı üzerinde çalışıyor.", "label.matchesLocation": "{0}/{1}", "label.noResults": "Sonuç Yok" } \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/contrib/find/common/findController.i18n.json b/i18n/trk/src/vs/editor/contrib/find/common/findController.i18n.json index e30fbb8a6d5..d6d3fc6ccf3 100644 --- a/i18n/trk/src/vs/editor/contrib/find/common/findController.i18n.json +++ b/i18n/trk/src/vs/editor/contrib/find/common/findController.i18n.json @@ -10,12 +10,6 @@ "nextSelectionMatchFindAction": "Sonraki Seçimi Bul", "previousSelectionMatchFindAction": "Önceki Seçimi Bul", "startReplace": "Değiştir", - "addSelectionToNextFindMatch": "Seçimi Sonraki Bulunan Eşleşmeye Ekle", - "addSelectionToPreviousFindMatch": "Seçimi Önceki Bulunan Eşleşmeye Ekle", - "moveSelectionToNextFindMatch": "Son Seçimi Sonraki Bulunan Eşleşmeye Taşı", - "moveSelectionToPreviousFindMatch": "Son Seçimi Önceki Bulunan Eşleşmeye Taşı", - "selectAllOccurrencesOfFindMatch": "Bulunan Eşleşmenin Tüm Tekrarlamalarını Seç", - "changeAll.label": "Tüm Tekrarlamaları Değiştir", "showNextFindTermAction": "Sonraki Arama Terimini Göster", "showPreviousFindTermAction": "Önceki Arama Terimini Göster" } \ No newline at end of file diff --git a/i18n/trk/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json b/i18n/trk/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json index f3fa18b4184..899e10e4b02 100644 --- a/i18n/trk/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json +++ b/i18n/trk/src/vs/editor/contrib/multicursor/common/multicursor.i18n.json @@ -6,5 +6,11 @@ { "mutlicursor.insertAbove": "Yukarıya İmleç Ekle", "mutlicursor.insertBelow": "Aşağıya İmleç Ekle", - "mutlicursor.insertAtEndOfEachLineSelected": "Satır Sonlarına İmleç Ekle" + "mutlicursor.insertAtEndOfEachLineSelected": "Satır Sonlarına İmleç Ekle", + "addSelectionToNextFindMatch": "Seçimi Sonraki Bulunan Eşleşmeye Ekle", + "addSelectionToPreviousFindMatch": "Seçimi Önceki Bulunan Eşleşmeye Ekle", + "moveSelectionToNextFindMatch": "Son Seçimi Sonraki Bulunan Eşleşmeye Taşı", + "moveSelectionToPreviousFindMatch": "Son Seçimi Önceki Bulunan Eşleşmeye Taşı", + "selectAllOccurrencesOfFindMatch": "Bulunan Eşleşmenin Tüm Tekrarlamalarını Seç", + "changeAll.label": "Tüm Tekrarlamaları Değiştir" } \ No newline at end of file diff --git a/i18n/trk/src/vs/platform/theme/common/colorRegistry.i18n.json b/i18n/trk/src/vs/platform/theme/common/colorRegistry.i18n.json index 690e08f5f05..4a14af64319 100644 --- a/i18n/trk/src/vs/platform/theme/common/colorRegistry.i18n.json +++ b/i18n/trk/src/vs/platform/theme/common/colorRegistry.i18n.json @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "invalid.color": "Geçersiz renk biçimi. #RGB, #RGBA, #RRGGBB veya #RRGGBBAA kullanın", "schema.colors": "Çalışma ekranında kullanılan renkler.", "foreground": "Genel ön plan rengi. Bu renk, bir bileşen tarafından geçersiz kılınmadıkça kullanılır.", "errorForeground": "Hata mesajları için genel ön plan rengi. Bu renk, bir bileşen tarafından geçersiz kılınmadıkça kullanılır.", 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 ed0caa5b58f..006f3eee320 100644 --- a/i18n/trk/src/vs/workbench/browser/actions/workspaceActions.i18n.json +++ b/i18n/trk/src/vs/workbench/browser/actions/workspaceActions.i18n.json @@ -9,10 +9,12 @@ "addFolderToWorkspace": "Çalışma Alanına Klasör Ekle...", "add": "&&Ekle", "addFolderToWorkspaceTitle": "Çalışma Alanına Klasör Ekle", + "globalRemoveFolderFromWorkspace": "Çalışma Alanından Klasör Kaldır...", "newWorkspace": "Yeni Çalışma Alanı...", "select": "&&Seç", "selectWorkspace": "Çalışma Alanı İçin Klasörleri Seçin", "removeFolderFromWorkspace": "Çalışma Alanından Klasör Kaldır", + "openFolderSettings": "Klasör Ayarlarını Aç", "saveWorkspaceAsAction": "Çalışma Alanını Farklı Kaydet...", "save": "&&Kaydet", "saveWorkspace": "Çalışma Alanını Kaydet", diff --git a/i18n/trk/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json b/i18n/trk/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json index 32c2c08ce4c..832ee472cb0 100644 --- a/i18n/trk/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json +++ b/i18n/trk/src/vs/workbench/browser/parts/activitybar/activitybarPart.i18n.json @@ -5,6 +5,5 @@ // Do not edit this file. It is machine generated. { "hideActivitBar": "Etkinlik Çubuğunu Gizle", - "activityBarAriaLabel": "Aktif Görünüm Değiştirici", "globalActions": "Global Eylemler" } \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/browser/parts/compositebar/compositeBar.i18n.json b/i18n/trk/src/vs/workbench/browser/parts/compositebar/compositeBar.i18n.json new file mode 100644 index 00000000000..99a95a1980c --- /dev/null +++ b/i18n/trk/src/vs/workbench/browser/parts/compositebar/compositeBar.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. +{ + "activityBarAriaLabel": "Aktif Görünüm Değiştirici" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/browser/parts/compositebar/compositeBarActions.i18n.json b/i18n/trk/src/vs/workbench/browser/parts/compositebar/compositeBarActions.i18n.json new file mode 100644 index 00000000000..fcbc6260597 --- /dev/null +++ b/i18n/trk/src/vs/workbench/browser/parts/compositebar/compositeBarActions.i18n.json @@ -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. + *--------------------------------------------------------------------------------------------*/ +// Do not edit this file. It is machine generated. +{ + "badgeTitle": "{0} - {1}", + "additionalViews": "Ek Görünümler", + "numberBadge": "{0} ({1})", + "manageExtension": "Eklentiyi Yönet", + "titleKeybinding": "{0} ({1})", + "toggle": "Görünüm Sabitlemeyi Aç/Kapat" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json b/i18n/trk/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json index 9059d7f92c9..5ff77e04882 100644 --- a/i18n/trk/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json +++ b/i18n/trk/src/vs/workbench/browser/parts/editor/editor.contribution.i18n.json @@ -12,5 +12,6 @@ "groupTwoPicker": "İkinci Gruptaki Düzenleyicileri Göster", "groupThreePicker": "Üçüncü Gruptaki Düzenleyicileri Göster", "allEditorsPicker": "Açık Tüm Düzenleyicileri Göster", - "view": "Görüntüle" + "view": "Görüntüle", + "file": "Dosya" } \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json b/i18n/trk/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json index ea87cdb6229..38591433f64 100644 --- a/i18n/trk/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json +++ b/i18n/trk/src/vs/workbench/browser/parts/statusbar/statusbarPart.i18n.json @@ -4,6 +4,5 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "canNotRun": "'{0}' komutu şu an etkin değildir ve çalıştırılamaz.", "manageExtension": "Eklentiyi Yönet" } \ 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 efdf40af46b..947272ee097 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,18 +10,14 @@ "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 ve italik yazı tipiyle gösterilirler.", "enablePreviewFromQuickOpen": "Hızlı Aç'taki 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.", - "editorOpenPositioning": "Düzenleyicilerin nerede açılacağını denetler. Düzenleyicileri, geçerli olanın soluna veya sağına açmak için 'left' veya 'right' seçeneklerinden birini seçin. Düzenleyicileri, geçerli olandan bağımsız bir şekilde açmak için 'first' veya 'last' seçeneklerinden birini seçin.", "revealIfOpen": "Düzenleyicinin görünen gruplardan herhangi birinde açıldıysa ortaya çıkarılıp çıkarılmayacağını denetler. Devre dışı bırakılırsa; bir düzenleyici, o an aktif düzenleyici grubunda açılmayı tercih edecektir. Etkinleştirilirse; o an aktif düzenleyici grubunda tekrar açılmak yerine, zaten açık olan düzenleyici ortaya çıkarılacaktır. Bu ayarın yok sayılacağı bazı durumların olduğunu unutmayın, ör. bir düzenleyiciyi, belirli bir grupta veya o an aktif grubun yanına açmaya zorladığınızda. ", - "commandHistory": "Komut paleti geçmişinde tutulacak son kullanılan komutların sayısını denetler. Komut geçmişini kapatmak için 0 olarak ayarlayın.", "preserveInput": "Komut paletine son girilen girdinin, bir sonraki açılışta tekrar yer alıp almayacağını denetler.", "closeOnFocusLost": "Hızlı Aç'ın odağını kaybettiğinde otomatik olarak kapanıp kapanmayacağını denetler.", "openDefaultSettings": "Ayarları açmanın ayrıca tüm varsayılan ayarları gösteren bir düzenleyici açıp açmayacağını denetler.", @@ -50,7 +46,6 @@ "restoreWindows": "Pencerelerin, bir yeniden başlatma sonrası nasıl yeniden açılacağını denetler. Her zaman boş bir çalışma alanı ile başlamak için 'none', üzerinde çalıştığınız son pencereyi yeniden açmak için 'one', açık klasör bulunduran tüm pencereleri yeniden açmak için 'folders' veya son oturumunuzdaki tüm pencereleri yeniden açmak için 'all' seçeneğini seçin.", "restoreFullscreen": "Bir pencere tam ekran modundayken çıkıldıysa, bu pencerenin tam ekran moduna geri dönüp dönmeyeceğini denetler.", "zoomLevel": "Pencerenin yakınlaştırma düzeyini ayarlayın. Orijinal boyut 0'dır ve üstündeki (ör. 1) veya altındaki (ör. -1) her artırma 20% daha fazla veya az yakınlaştırmayı temsil eder. Yakınlaştırma düzeyini daha ince ayrıntılarla ayarlamak için ondalık değerler de girebilirsiniz.", - "title": "Pencere başlığını aktif düzenleyiciye bağlı olarak denetler. Değişkenler, bağlama göre değiştirilir:\n${activeEditorShort}: ör. myFile.txt\n${activeEditorMedium}: ör. myFolder/myFile.txt\n${activeEditorLong}: ör. /Users/Development/myProject/myFolder/myFile.txt\n${folderName}: ör. myFolder\n${folderPath}: ör. /Users/Development/myFolder\n${rootName}: ör. myFolder1, myFolder2, myFolder3\n${rootPath}: ör. /Users/Development/myWorkspace\n${appName}: ör. VS Code\n${dirty}: etkin düzenleyici kaydedilmemiş değişiklikler içeriyorsa, değişiklik göstergesi\n${separator}: şartlı ayırıcı (\" - \") yalnızca değer içeren değişkenlerle çevrili olduğunda gösterilir", "window.newWindowDimensions.default": "Yeni pencereleri ekranın ortasında açın.", "window.newWindowDimensions.inherit": "Yeni pencereleri son aktif pencere ile aynı ölçülerde açın.", "window.newWindowDimensions.maximized": "Yeni pencereleri ekranı kapla modunda açın.", diff --git a/i18n/trk/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json b/i18n/trk/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json index 6a22d71e97d..480910c4883 100644 --- a/i18n/trk/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json +++ b/i18n/trk/src/vs/workbench/parts/debug/browser/debugQuickOpen.i18n.json @@ -6,6 +6,8 @@ { "entryAriaLabel": "{0}, hata ayıklama", "debugAriaLabel": "Çalıştırılacak bir başlatma yapılandırması adı girin.", + "addConfigTo": "Yapılandırma Ekle ({0})...", + "addConfiguration": "Yapı&&landırma Ekle...", "noConfigurationsMatching": "Eşleyen hata ayıklama yapılandırması yok", "noConfigurationsFound": "Hiçbir hata ayıklama yapılandırması bulunamadı. Lütfen bir 'launch.json' dosyası oluşturun." } \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/debug/browser/debugStatus.i18n.json b/i18n/trk/src/vs/workbench/parts/debug/browser/debugStatus.i18n.json new file mode 100644 index 00000000000..5319c1251cb --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/debug/browser/debugStatus.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. +{ + "debug": "Hata Ayıklama" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/debug/browser/debugViewlet.i18n.json b/i18n/trk/src/vs/workbench/parts/debug/browser/debugViewlet.i18n.json new file mode 100644 index 00000000000..8b6ad71cd4e --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/debug/browser/debugViewlet.i18n.json @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json b/i18n/trk/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json index 0fbc09f8cc1..ea94460b89a 100644 --- a/i18n/trk/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json +++ b/i18n/trk/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.i18n.json @@ -15,7 +15,6 @@ "vscode.extension.contributes.debuggers.initialConfigurations": "İlk 'launch.json' dosyasının üretimi için yapılandırmalar.", "vscode.extension.contributes.debuggers.languages": "Hata ayıklama eklentisinin, \"varsayılan hata ayıklayıcı\" olarak değerlendirilebileceği diller listesi.", "vscode.extension.contributes.debuggers.adapterExecutableCommand": "Belirtilirse; VS Code, hata ayıklama bağdaştırıcısı yürütülebilir dosyasının yolunu ve ona gönderilecek argümanları belirlemek için bu komutu çağırır.", - "vscode.extension.contributes.debuggers.startSessionCommand": "Belirtilirse; VS Code, bu eklenti için hedeflenen \"hata ayıklama\" ve \"çalıştır\" eylemleri için bu komutu çağırır.", "vscode.extension.contributes.debuggers.configurationSnippets": "'launch.json' dosyasına yeni yapılandırmalar ekleme parçacıkları.", "vscode.extension.contributes.debuggers.configurationAttributes": "'launch.json' dosyasını doğrulayacak JSON şema yapılandırmaları.", "vscode.extension.contributes.debuggers.windows": "Windows'a özel ayarlar.", 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 25a3a60a2ae..8d0432122a3 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,10 +12,10 @@ "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": "Seçilen hata ayıklama yapılandırması desteklenmeyen öznitelik değeri `{0}` içeriyor: '{1}'.", - "debugRequesMissing": "'{0}' özniteliği seçilen hata ayıklama yapılandırılmasında eksik.", + "debugRequestNotSupported": "Seçilen hata ayıklama yapılandırılmasındaki `{0}` özniteliği desteklenmeyen `{1}` değeri içeriyor.", + "debugRequesMissing": "`{0}` özniteliği seçilen hata ayıklama 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.", + "debugTypeMissing": "Seçilen başlatma yapılandırmasında `type` özelliği eksik.", "preLaunchTaskErrors": "'{0}' ön başlatma görevi sırasında derleme hataları algılandı.", "preLaunchTaskError": "'{0}' ön başlatma görevi sırasında derleme hatası algılandı.", "preLaunchTaskExitCode": "'{0}' ön başlatma görevi {1} çıkış koduyla sonlandı.", diff --git a/i18n/trk/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json b/i18n/trk/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json index 3ee7a241566..59b7c7fc1a3 100644 --- a/i18n/trk/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json +++ b/i18n/trk/src/vs/workbench/parts/debug/electron-browser/replViewer.i18n.json @@ -7,6 +7,5 @@ "stateCapture": "Nesne durumu ilk değerlendirmeden alındı", "replVariableAriaLabel": "{0} değişkeni, {1} değerine sahip, oku değerlendir yaz döngüsü, hata ayıklama", "replExpressionAriaLabel": "{0} ifadesi, {1} değerine sahip, oku değerlendir yaz döngüsü, hata ayıklama", - "replValueOutputAriaLabel": "{0}, oku değerlendir yaz döngüsü, hata ayıklama", - "replKeyValueOutputAriaLabel": "{0} çıktı değişkeni, {1} değerine sahip, oku değerlendir yaz döngüsü, hata ayıklama" + "replValueOutputAriaLabel": "{0}, oku değerlendir yaz döngüsü, hata ayıklama" } \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json b/i18n/trk/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json index c907a874d19..09b50f5c819 100644 --- a/i18n/trk/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json +++ b/i18n/trk/src/vs/workbench/parts/files/browser/fileActions.contribution.i18n.json @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "filesCategory": "Dosyalar", + "filesCategory": "Dosya", "revealInSideBar": "Kenar Çubuğunda Ortaya Çıkar", "acceptLocalChanges": "Değişikliklerinizi kullanın ve diskteki içeriklerin üzerine yazın", "revertLocalChanges": "Değişikliklerinizi göz ardı edin ve diskteki içeriğe geri dönün" diff --git a/i18n/trk/src/vs/workbench/parts/files/browser/fileActions.i18n.json b/i18n/trk/src/vs/workbench/parts/files/browser/fileActions.i18n.json index 84098dc8d04..36b6a59d2b2 100644 --- a/i18n/trk/src/vs/workbench/parts/files/browser/fileActions.i18n.json +++ b/i18n/trk/src/vs/workbench/parts/files/browser/fileActions.i18n.json @@ -37,8 +37,6 @@ "openToSide": "Yana Aç", "compareSource": "Karşılaştırma İçin Seç", "globalCompareFile": "Aktif Dosyayı Karşılaştır...", - "pickHistory": "Karşılaştırmak için daha önce açılan bir dosyayı seçin", - "unableToFileToCompare": "Seçtiğiniz dosya, '{0}' ile karşılaştırılamaz.", "openFileToCompare": "Bir başka dosya ile karşılaştırmak için ilk olarak bir dosya açın.", "compareWith": "'{0}' dosyasını '{1}' ile karşılaştır", "compareFiles": "Dosyaları Karşılaştır", @@ -47,7 +45,6 @@ "saveAs": "Farklı Kaydet...", "saveAll": "Tümünü Kaydet", "saveAllInGroup": "Gruptaki Tümünü Kadet", - "saveFiles": "Kaydedilmemiş Değişiklikler İçeren Dosyaları Kaydet", "revert": "Dosyayı Geri Döndür", "focusOpenEditors": "Açık Düzenleyiciler Görünümüne Odakla", "focusFilesExplorer": "Dosya Gezginine Odakla", diff --git a/i18n/trk/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json b/i18n/trk/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json index d14973027f7..21fd7b44865 100644 --- a/i18n/trk/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json +++ b/i18n/trk/src/vs/workbench/parts/files/browser/views/emptyView.i18n.json @@ -6,6 +6,6 @@ { "noWorkspace": "Açık Klasör Yok", "explorerSection": "Dosya Gezgini Bölümü", - "noWorkspaceHelp": "Henüz bir klasör açmadınız.", + "noFolderHelp": "Henüz bir klasör açmadınız.", "openFolder": "Klasör Aç" } \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/markers/browser/markersFileDecorations.i18n.json b/i18n/trk/src/vs/workbench/parts/markers/browser/markersFileDecorations.i18n.json new file mode 100644 index 00000000000..706a42a2e0d --- /dev/null +++ b/i18n/trk/src/vs/workbench/parts/markers/browser/markersFileDecorations.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. +{ + "label": "Sorunlar" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json b/i18n/trk/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json index 5992f38bfb5..f7b1f83c99e 100644 --- a/i18n/trk/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json +++ b/i18n/trk/src/vs/workbench/parts/preferences/browser/preferencesRenderers.i18n.json @@ -5,7 +5,6 @@ // Do not edit this file. It is machine generated. { "emptyUserSettingsHeader": "Varsayılan ayarların üzerine yazmak için ayarlarınızı buraya yerleştirin.", - "errorInvalidConfiguration": "Ayarlara yazılamıyor. Lütfen dosyadaki hataları/uyarıları düzeltin ve tekrar deneyin.", "emptyWorkspaceSettingsHeader": "Varsayılan kullanıcı ayarlarının üzerine yazmak için ayarlarınızı buraya yerleştirin.", "emptyFolderSettingsHeader": "Çalışma alanı ayarlarındakilerin üzerine yazmak için klasör ayarlarınızı buraya yerleştirin.", "defaultFolderSettingsTitle": "Varsayılan Klasör Ayarları", diff --git a/i18n/trk/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json b/i18n/trk/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json index bef4db281ad..3164aed2f83 100644 --- a/i18n/trk/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json +++ b/i18n/trk/src/vs/workbench/parts/quickopen/browser/commandsHandler.i18n.json @@ -13,7 +13,6 @@ "actionNotEnabled": "'{0}' komutu geçerli bağlamda etkin değil.", "recentlyUsed": "yakınlarda kullanıldı", "morecCommands": "diğer komutlar", - "commandLabel": "{0}: {1}", "cat.title": "{0}: {1}", "noCommandsMatching": "Eşleşen komut yok" } \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/search/browser/search.contribution.i18n.json b/i18n/trk/src/vs/workbench/parts/search/browser/search.contribution.i18n.json index 803061b0af7..7c4f78fe197 100644 --- a/i18n/trk/src/vs/workbench/parts/search/browser/search.contribution.i18n.json +++ b/i18n/trk/src/vs/workbench/parts/search/browser/search.contribution.i18n.json @@ -17,7 +17,6 @@ "exclude": "Aramalarda dosyaları ve klasörleri hariç tutmak için glob desenlerini yapılandırın. files.exclude ayarından, tüm glob desenlerini devralır.", "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.", "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.", - "useRipgrep": "Metin aramasında Ripgrep kullanılıp kullanılmayacağını denetler", "useIgnoreFilesByDefault": "Yeni bir çalışma alanında arama yaparken .gitignore ve .ignore dosyalarının varsayılan olarak kullanılıp kullanılmayacağını denetler.", "search.quickOpen.includeSymbols": "Dosya sonuçlarındaki bir global sembol aramasının sonuçlarının Hızlı Aç'a dahil edilip edilmeyeceğini yapılandırın." } \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/parts/search/browser/searchActions.i18n.json b/i18n/trk/src/vs/workbench/parts/search/browser/searchActions.i18n.json index 0af6fe9976d..fb6486c2e36 100644 --- a/i18n/trk/src/vs/workbench/parts/search/browser/searchActions.i18n.json +++ b/i18n/trk/src/vs/workbench/parts/search/browser/searchActions.i18n.json @@ -19,7 +19,6 @@ "ClearSearchResultsAction.label": "Arama Sonuçlarını Temizle", "FocusNextSearchResult.label": "Sonraki Arama Sonucuna Odakla", "FocusPreviousSearchResult.label": "Önceki Arama Sonucuna Odakla", - "RemoveAction.label": "Kaldır", "file.replaceAll.label": "Tümünü Değiştir", "match.replace.label": "Değiştir" } \ 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 52c84a18c00..2f0e0bda449 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,8 +10,8 @@ "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.", + "badFile": "Parçacık dosyası \"{0}\" okunamadı.", "source.snippet": "Kullanıcı Parçacığı", "detail.snippet": "{0} ({1})", "snippetSuggest.longLabel": "{0}, {1}" 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 8d8d4cb77d3..539713491df 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 @@ -16,7 +16,6 @@ "workbench.action.terminal.new.short": "Yeni Terminal", "workbench.action.terminal.focus": "Terminale Odakla", "workbench.action.terminal.focusNext": "Sonraki Terminale Odakla", - "workbench.action.terminal.focusAtIndex": "{0}. Terminale Odakla", "workbench.action.terminal.focusPrevious": "Önceki Terminale Odakla", "workbench.action.terminal.paste": "Aktif Terminale Yapıştır", "workbench.action.terminal.DefaultShell": "Varsayılan Kabuğu Seç", diff --git a/i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json b/i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json index be9f370adec..92d6df37712 100644 --- a/i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json +++ b/i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.i18n.json @@ -5,7 +5,6 @@ // Do not edit this file. It is machine generated. { "copy": "Kopyala", - "createNewTerminal": "Yeni Terminal", "paste": "Yapıştır", "selectAll": "Tümünü Seç", "clear": "Temizle" diff --git a/i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json b/i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json index 28fa93d323a..a30794054e1 100644 --- a/i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json +++ b/i18n/trk/src/vs/workbench/parts/terminal/electron-browser/terminalService.i18n.json @@ -10,6 +10,5 @@ "never again": "Tamam, Tekrar Gösterme", "terminal.integrated.chooseWindowsShell": "Tercih ettiğiniz terminal kabuğunu seçin, bunu daha sonra ayarlarınızdan değiştirebilirsiniz", "terminalService.terminalCloseConfirmationSingular": "Aktif bir terminal oturumu var, sonlandırmak istiyor musunuz?", - "terminalService.terminalCloseConfirmationPlural": "{0} aktif terminal oturumu var, bunları sonlandırmak istiyor musunuz?", - "yes": "Evet" + "terminalService.terminalCloseConfirmationPlural": "{0} aktif terminal oturumu var, bunları sonlandırmak istiyor musunuz?" } \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/services/configuration/common/configurationExtensionPoint.i18n.json b/i18n/trk/src/vs/workbench/services/configuration/common/configurationExtensionPoint.i18n.json new file mode 100644 index 00000000000..4c2ceda75b6 --- /dev/null +++ b/i18n/trk/src/vs/workbench/services/configuration/common/configurationExtensionPoint.i18n.json @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "vscode.extension.contributes.configuration.title": "Ayarların bir özeti. Bu etiket ayar dosyasında ayırıcı yorum olarak kullanılacaktır.", + "vscode.extension.contributes.configuration.properties": "Yapılandırma özelliklerinin açıklaması.", + "scope.window.description": "Kullanıcı veya çalışma alanında yapılandırılabilen Windows'a özel yapılandırma.", + "scope.resource.description": "Kullanıcı veya çalışma alanında yapılandırılabilen kaynağa özel yapılandırma.", + "scope.description": "Yapılandırmanın uygulanabilir olduğu kapsam. Mevcut kapsamlar 'window' ve 'resource'tır.", + "vscode.extension.contributes.configuration": "Yapılandırma ayarlarına ekleme yapar.", + "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", + "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", + "unknownWorkspaceProperty": "Bilinmeyen çalışma alanı yapılandırması özelliği" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/services/editor/common/editorService.i18n.json b/i18n/trk/src/vs/workbench/services/editor/common/editorService.i18n.json new file mode 100644 index 00000000000..50e968f8ee3 --- /dev/null +++ b/i18n/trk/src/vs/workbench/services/editor/common/editorService.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. +{ + "compareLabels": "{0} ↔ {1}" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/services/themes/electron-browser/colorThemeStore.i18n.json b/i18n/trk/src/vs/workbench/services/themes/electron-browser/colorThemeStore.i18n.json new file mode 100644 index 00000000000..ab403ef3493 --- /dev/null +++ b/i18n/trk/src/vs/workbench/services/themes/electron-browser/colorThemeStore.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "vscode.extension.contributes.themes": "Textmate renk temalarına ekleme yapar.", + "vscode.extension.contributes.themes.id": "Kullanıcı ayarlarında kullanılan simge teması Id'si.", + "vscode.extension.contributes.themes.label": "Kullanıcı arayüzünde görünen renk temasının etiketi.", + "vscode.extension.contributes.themes.uiTheme": "Editördeki renkleri tanımlayan temel tema: 'vs' açık renk temasıdır, 'vs-dark' koyu renk temasıdır. 'hc-black' ise yüksek kontrast temasıdır.", + "vscode.extension.contributes.themes.path": "tmLanguage dosyasının yolu. Yol, eklenti klasörüne görecelidir ve genellikle './themes/themeFile.tmTheme'dir.", + "reqarray": "Eklenti noktası `{0}` bir dizi olmalıdır.", + "reqpath": "`contributes.{0}.path` ögesinde dize bekleniyor. Sağlanan değer: {1}", + "invalid.path.1": "`contributes.{0}.path` ögesinin ({1}) eklentinin klasöründe ({2}) yer alması bekleniyor. Bu, eklentiyi taşınamaz yapabilir." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.i18n.json b/i18n/trk/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.i18n.json new file mode 100644 index 00000000000..c7286a77b94 --- /dev/null +++ b/i18n/trk/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.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. +{ + "error.cannotparseicontheme": "Dosya simgeleri dosyasını ayrıştırma sorunları: {0}" +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.i18n.json b/i18n/trk/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.i18n.json new file mode 100644 index 00000000000..65e81b3fdbf --- /dev/null +++ b/i18n/trk/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.i18n.json @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "vscode.extension.contributes.iconThemes": "Dosya simgesi temalarına ekleme yapar.", + "vscode.extension.contributes.iconThemes.id": "Kullanıcı ayarlarında kullanılan simge teması Id'si.", + "vscode.extension.contributes.iconThemes.label": "Kullanıcı arayüzünde görünen simge temasının etiketi.", + "vscode.extension.contributes.iconThemes.path": "Simge teması tanımlama dosyasının yolu. Yol, eklenti klasörüne görecelidir ve genellikle './icons/awesome-icon-theme.json'dur.", + "reqarray": "Eklenti noktası `{0}` bir dizi olmalıdır.", + "reqpath": "`contributes.{0}.path` ögesinde dize bekleniyor. Sağlanan değer: {1}", + "reqid": "`contributes.{0}.id` ögesinde dize bekleniyordu. Belirtilen değer: {1}", + "invalid.path.1": "`contributes.{0}.path` ögesinin ({1}) eklentinin klasöründe ({2}) yer alması bekleniyor. Bu, eklentiyi taşınamaz yapabilir." +} \ No newline at end of file diff --git a/i18n/trk/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json b/i18n/trk/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json index 44429da31b5..6715de58332 100644 --- a/i18n/trk/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json +++ b/i18n/trk/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.i18n.json @@ -4,31 +4,15 @@ *--------------------------------------------------------------------------------------------*/ // Do not edit this file. It is machine generated. { - "vscode.extension.contributes.themes": "Textmate renk temalarına ekleme yapar.", - "vscode.extension.contributes.themes.id": "Kullanıcı ayarlarında kullanılan simge teması Id'si.", - "vscode.extension.contributes.themes.label": "Kullanıcı arayüzünde görünen renk temasının etiketi.", - "vscode.extension.contributes.themes.uiTheme": "Editördeki renkleri tanımlayan temel tema: 'vs' açık renk temasıdır, 'vs-dark' koyu renk temasıdır. 'hc-black' ise yüksek kontrast temasıdır.", - "vscode.extension.contributes.themes.path": "tmLanguage dosyasının yolu. Yol, eklenti klasörüne görecelidir ve genellikle './themes/themeFile.tmTheme'dir.", - "vscode.extension.contributes.iconThemes": "Dosya simgesi temalarına ekleme yapar.", - "vscode.extension.contributes.iconThemes.id": "Kullanıcı ayarlarında kullanılan simge teması Id'si.", - "vscode.extension.contributes.iconThemes.label": "Kullanıcı arayüzünde görünen simge temasının etiketi.", - "vscode.extension.contributes.iconThemes.path": "Simge teması tanımlama dosyasının yolu. Yol, eklenti klasörüne görecelidir ve genellikle './icons/awesome-icon-theme.json'dur.", "migration.completed": "Yeni tema ayarları kullanıcı ayarlarına eklendi. Yedek, {0} konumunda mevcuttur.", "error.cannotloadtheme": "{0} yüklenemedi: {1}", - "reqarray": "Eklenti noktası `{0}` bir dizi olmalıdır.", - "reqpath": "`contributes.{0}.path` ögesinde dize bekleniyor. Sağlanan değer: {1}", - "invalid.path.1": "`contributes.{0}.path` ögesinin ({1}) eklentinin klasöründe ({2}) yer alması bekleniyor. Bu, eklentiyi taşınamaz yapabilir.", - "reqid": "`contributes.{0}.id` ögesinde dize bekleniyordu. Belirtilen değer: {1}", "error.cannotloadicontheme": "{0} yüklenemedi", - "error.cannotparseicontheme": "Dosya simgeleri dosyasını ayrıştırma sorunları: {0}", "colorTheme": "Çalışma ekranında kullanılan renk temasını belirtir.", "colorThemeError": "Tema bilinmiyor veya yüklenmemiş.", "iconTheme": "Çalışma ekranında kullanılan simge temasını veya hiçbir dosya simgesi göstermemek için 'null' belirtir.", "noIconThemeDesc": "Dosya simgesi yok", "iconThemeError": "Dosya simgesi teması bilinmiyor veya yüklenmemiş.", "workbenchColors": "Şu an seçili renk temasındaki renkleri geçersiz kılar.", - "workbenchColors.deprecated": "Ayar, artık deneysel değildir ve 'workbench.colorCustomizations' olarak yeniden adlandırılmıştır", - "workbenchColors.deprecatedDescription": "Bunun yerine 'workbench.colorCustomizations' kullanın", "editorColors": "Şu an seçili renk temasındaki düzenleyici renklerini ve yazı tipi stilini geçersiz kılar.", "editorColors.comments": "Yorumların rengini ve stillerini ayarlar", "editorColors.strings": "Dizelerin rengini ve stillerini ayarlar.", diff --git a/i18n/trk/src/vs/workbench/services/workspace/node/workspaceEditingService.i18n.json b/i18n/trk/src/vs/workbench/services/workspace/node/workspaceEditingService.i18n.json new file mode 100644 index 00000000000..776050fe34c --- /dev/null +++ b/i18n/trk/src/vs/workbench/services/workspace/node/workspaceEditingService.i18n.json @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * 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. +{ + "openWorkspaceConfigurationFile": "Çalışma Alanı Yapılandırma Dosyasını Aç", + "close": "Kapat" +} \ No newline at end of file diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index a3b4ae130d4..f855a4d012d 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -566,6 +566,28 @@ "from": "winreg@1.2.0", "resolved": "https://registry.npmjs.org/winreg/-/winreg-1.2.0.tgz" }, + "windows-foreground-love": { + "version": "0.1.0", + "from": "windows-foreground-love@0.1.0", + "resolved": "https://registry.npmjs.org/windows-foreground-love/-/windows-foreground-love-0.1.0.tgz" + }, + "windows-mutex": { + "version": "0.2.0", + "from": "windows-mutex@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/windows-mutex/-/windows-mutex-0.2.0.tgz", + "dependencies": { + "bindings": { + "version": "1.3.0", + "from": "bindings@>=1.2.1 <2.0.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.3.0.tgz" + }, + "nan": { + "version": "2.7.0", + "from": "nan@>=2.1.0 <3.0.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.7.0.tgz" + } + } + }, "windows-process-tree": { "version": "0.1.6", "from": "windows-process-tree@0.1.6", @@ -574,7 +596,7 @@ "xterm": { "version": "2.9.1", "from": "Tyriar/xterm.js#vscode-release/1.18", - "resolved": "git+https://github.com/Tyriar/xterm.js.git#14b0137accaf350565a005d68e91f03c96a241be" + "resolved": "git+https://github.com/Tyriar/xterm.js.git#80718295b8f73d809686741a251aa529317c1d35" }, "yauzl": { "version": "2.8.0", @@ -582,4 +604,4 @@ "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.8.0.tgz" } } -} +} \ No newline at end of file diff --git a/package.json b/package.json index 2133570b711..e3b9e1a4bfe 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "name": "code-oss-dev", "version": "1.18.0", - "electronVersion": "1.7.7", - "distro": "59a68f7780bb6571a1460ea00ca7b994126baeda", + "electronVersion": "1.7.9", + "distro": "c5f654b74299fd467966e51a217094851a6fc239", "author": { "name": "Microsoft Corporation" }, diff --git a/src/typings/electron.d.ts b/src/typings/electron.d.ts index 75bf1761b46..ce604fb1bf0 100644 --- a/src/typings/electron.d.ts +++ b/src/typings/electron.d.ts @@ -1,4 +1,4 @@ -// Type definitions for Electron 1.7.7 +// Type definitions for Electron 1.7.9 // Project: http://electron.atom.io/ // Definitions by: The Electron Team // Definitions: https://github.com/electron/electron-typescript-definitions @@ -37,91 +37,81 @@ declare namespace Electron { shiftKey?: boolean; altKey?: boolean; } + interface CommonInterface { - clipboard: Electron.Clipboard; - crashReporter: Electron.CrashReporter; - nativeImage: typeof Electron.NativeImage; - screen: Electron.Screen; - shell: Electron.Shell; + clipboard: Clipboard; + crashReporter: CrashReporter; + nativeImage: typeof NativeImage; + screen: Screen; + shell: Shell; } interface MainInterface extends CommonInterface { - app: Electron.App; - autoUpdater: Electron.AutoUpdater; - BrowserView: typeof Electron.BrowserView; - BrowserWindow: typeof Electron.BrowserWindow; - ClientRequest: typeof Electron.ClientRequest; - contentTracing: Electron.ContentTracing; - Cookies: typeof Electron.Cookies; - Debugger: typeof Electron.Debugger; - dialog: Electron.Dialog; - DownloadItem: typeof Electron.DownloadItem; - globalShortcut: Electron.GlobalShortcut; - IncomingMessage: typeof Electron.IncomingMessage; - ipcMain: Electron.IpcMain; - Menu: typeof Electron.Menu; - MenuItem: typeof Electron.MenuItem; - net: Electron.Net; - Notification: typeof Electron.Notification; - powerMonitor: Electron.PowerMonitor; - powerSaveBlocker: Electron.PowerSaveBlocker; - protocol: Electron.Protocol; - session: typeof Electron.Session; - systemPreferences: Electron.SystemPreferences; - TouchBar: typeof Electron.TouchBar; - Tray: typeof Electron.Tray; - webContents: typeof Electron.WebContents; - WebRequest: typeof Electron.WebRequest; + app: App; + autoUpdater: AutoUpdater; + BrowserView: typeof BrowserView; + BrowserWindow: typeof BrowserWindow; + ClientRequest: typeof ClientRequest; + contentTracing: ContentTracing; + Cookies: typeof Cookies; + Debugger: typeof Debugger; + dialog: Dialog; + DownloadItem: typeof DownloadItem; + globalShortcut: GlobalShortcut; + IncomingMessage: typeof IncomingMessage; + ipcMain: IpcMain; + Menu: typeof Menu; + MenuItem: typeof MenuItem; + net: Net; + Notification: typeof Notification; + powerMonitor: PowerMonitor; + powerSaveBlocker: PowerSaveBlocker; + protocol: Protocol; + session: typeof Session; + systemPreferences: SystemPreferences; + TouchBar: typeof TouchBar; + Tray: typeof Tray; + webContents: typeof WebContents; + WebRequest: typeof WebRequest; } interface RendererInterface extends CommonInterface { - BrowserWindowProxy: typeof Electron.BrowserWindowProxy; - desktopCapturer: Electron.DesktopCapturer; - ipcRenderer: Electron.IpcRenderer; - remote: Electron.Remote; - webFrame: Electron.WebFrame; - webviewTag: Electron.WebviewTag; + BrowserWindowProxy: typeof BrowserWindowProxy; + desktopCapturer: DesktopCapturer; + ipcRenderer: IpcRenderer; + remote: Remote; + webFrame: WebFrame; + webviewTag: WebviewTag; } - interface AllElectron { - app: Electron.App; - autoUpdater: Electron.AutoUpdater; - BrowserView: typeof Electron.BrowserView; - BrowserWindow: typeof Electron.BrowserWindow; - BrowserWindowProxy: typeof Electron.BrowserWindowProxy; - ClientRequest: typeof Electron.ClientRequest; - clipboard: Electron.Clipboard; - contentTracing: Electron.ContentTracing; - Cookies: typeof Electron.Cookies; - crashReporter: Electron.CrashReporter; - Debugger: typeof Electron.Debugger; - desktopCapturer: Electron.DesktopCapturer; - dialog: Electron.Dialog; - DownloadItem: typeof Electron.DownloadItem; - globalShortcut: Electron.GlobalShortcut; - IncomingMessage: typeof Electron.IncomingMessage; - ipcMain: Electron.IpcMain; - ipcRenderer: Electron.IpcRenderer; - Menu: typeof Electron.Menu; - MenuItem: typeof Electron.MenuItem; - nativeImage: typeof Electron.NativeImage; - net: Electron.Net; - Notification: typeof Electron.Notification; - powerMonitor: Electron.PowerMonitor; - powerSaveBlocker: Electron.PowerSaveBlocker; - protocol: Electron.Protocol; - remote: Electron.Remote; - screen: Electron.Screen; - session: typeof Electron.Session; - shell: Electron.Shell; - systemPreferences: Electron.SystemPreferences; - TouchBar: typeof Electron.TouchBar; - Tray: typeof Electron.Tray; - webContents: typeof Electron.WebContents; - webFrame: Electron.WebFrame; - WebRequest: typeof Electron.WebRequest; - webviewTag: Electron.WebviewTag; - } + interface AllElectron extends MainInterface, RendererInterface { } + + const app: App; + const autoUpdater: AutoUpdater; + const clipboard: Clipboard; + const contentTracing: ContentTracing; + const crashReporter: CrashReporter; + const desktopCapturer: DesktopCapturer; + const dialog: Dialog; + const globalShortcut: GlobalShortcut; + const ipcMain: IpcMain; + const ipcRenderer: IpcRenderer; + type nativeImage = NativeImage; + const nativeImage: typeof NativeImage; + const net: Net; + const powerMonitor: PowerMonitor; + const powerSaveBlocker: PowerSaveBlocker; + const protocol: Protocol; + const remote: Remote; + const screen: Screen; + type session = Session; + const session: typeof Session; + const shell: Shell; + const systemPreferences: SystemPreferences; + type webContents = WebContents; + const webContents: typeof WebContents; + const webFrame: WebFrame; + const webviewTag: WebviewTag; interface App extends EventEmitter { @@ -6362,7 +6352,8 @@ declare namespace Electron { ignoreSystemCrashHandler?: boolean; /** * An object you can define that will be sent along with the report. Only string - * properties are sent correctly. Nested objects are not supported. + * properties are sent correctly. Nested objects are not supported and the property + * names and values must be less than 64 characters long. */ extra?: any; /** @@ -8089,12 +8080,11 @@ declare namespace Electron { } declare module 'electron' { - const electron: Electron.AllElectron; - export = electron; + export = Electron; } interface NodeRequireFunction { - (moduleName: 'electron'): Electron.AllElectron; + (moduleName: 'electron'): typeof Electron; } interface File { diff --git a/src/typings/xterm.d.ts b/src/typings/xterm.d.ts index 197f2e7ebc8..40085d10893 100644 --- a/src/typings/xterm.d.ts +++ b/src/typings/xterm.d.ts @@ -7,161 +7,166 @@ * to be stable and consumed by external programs. */ -/** - * An object containing start up options for the terminal. - */ -interface ITerminalOptions { - /** - * A data uri of the sound to use for the bell (needs bellStyle = 'sound'). - */ - bellSound?: string; - - /** - * The type of the bell notification the terminal will use. - */ - bellStyle?: 'none' | 'visual' | 'sound' | 'both'; - - /** - * The number of columns in the terminal. - */ - cols?: number; - - /** - * Whether the cursor blinks. - */ - cursorBlink?: boolean; - - /** - * The style of the cursor. - */ - cursorStyle?: 'block' | 'underline' | 'bar'; - - /** - * Whether input should be disabled. - */ - disableStdin?: boolean; - - /** - * The font size used to render text. - */ - fontSize?: number; - - /** - * The font family used to render text. - */ - fontFamily?: string; - - /** - * The line height used to render text. - */ - lineHeight?: number; - - /** - * The number of rows in the terminal. - */ - rows?: number; - - /** - * The amount of scrollback in the terminal. Scrollback is the amount of rows - * that are retained when lines are scrolled beyond the initial viewport. - */ - scrollback?: number; - - /** - * The size of tab stops in the terminal. - */ - tabStopWidth?: number; - - /** - * The color theme of the terminal. - */ - theme?: ITheme; -} - -/** - * Contains colors to theme the terminal with. - */ -interface ITheme { - /** The default foreground color */ - foreground?: string, - /** The default background color */ - background?: string, - /** The cursor color */ - cursor?: string, - /** The selection color (can be transparent) */ - selection?: string, - /** The accent color of the cursor (used as the foreground color for a block cursor) */ - cursorAccent?: string, - /** ANSI black (eg. `\x1b[30m`) */ - black?: string, - /** ANSI red (eg. `\x1b[31m`) */ - red?: string, - /** ANSI green (eg. `\x1b[32m`) */ - green?: string, - /** ANSI yellow (eg. `\x1b[33m`) */ - yellow?: string, - /** ANSI blue (eg. `\x1b[34m`) */ - blue?: string, - /** ANSI magenta (eg. `\x1b[35m`) */ - magenta?: string, - /** ANSI cyan (eg. `\x1b[36m`) */ - cyan?: string, - /** ANSI white (eg. `\x1b[37m`) */ - white?: string, - /** ANSI bright black (eg. `\x1b[1;30m`) */ - brightBlack?: string, - /** ANSI bright red (eg. `\x1b[1;31m`) */ - brightRed?: string, - /** ANSI bright green (eg. `\x1b[1;32m`) */ - brightGreen?: string, - /** ANSI bright yellow (eg. `\x1b[1;33m`) */ - brightYellow?: string, - /** ANSI bright blue (eg. `\x1b[1;34m`) */ - brightBlue?: string, - /** ANSI bright magenta (eg. `\x1b[1;35m`) */ - brightMagenta?: string, - /** ANSI bright cyan (eg. `\x1b[1;36m`) */ - brightCyan?: string, - /** ANSI bright white (eg. `\x1b[1;37m`) */ - brightWhite?: string -} - -/** - * An object containing options for a link matcher. - */ -interface ILinkMatcherOptions { - /** - * The index of the link from the regex.match(text) call. This defaults to 0 - * (for regular expressions without capture groups). - */ - matchIndex?: number; - - /** - * A callback that validates an individual link, returning true if valid and - * false if invalid. - */ - validationCallback?: (uri: string, callback: (isValid: boolean) => void) => void; - - /** - * A callback that fires when the mouse hovers over a link for a moment. - */ - tooltipCallback?: (event: MouseEvent, uri: string) => boolean | void; - - /** - * A callback that fires when the mouse leaves a link. Note that this can - * happen even when tooltipCallback hasn't fired for the link yet. - */ - leaveCallback?: (event: MouseEvent, uri: string) => boolean | void; - - /** - * The priority of the link matcher, this defines the order in which the link - * matcher is evaluated relative to others, from highest to lowest. The - * default value is 0. - */ - priority?: number; -} - declare module 'xterm' { + /** + * An object containing start up options for the terminal. + */ + interface ITerminalOptions { + /** + * A data uri of the sound to use for the bell (needs bellStyle = 'sound'). + */ + bellSound?: string; + + /** + * The type of the bell notification the terminal will use. + */ + bellStyle?: 'none' | 'visual' | 'sound' | 'both'; + + /** + * The number of columns in the terminal. + */ + cols?: number; + + /** + * Whether the cursor blinks. + */ + cursorBlink?: boolean; + + /** + * The style of the cursor. + */ + cursorStyle?: 'block' | 'underline' | 'bar'; + + /** + * Whether input should be disabled. + */ + disableStdin?: boolean; + + /** + * Whether to enable the rendering of bold text. + */ + enableBold?: boolean; + + /** + * The font size used to render text. + */ + fontSize?: number; + + /** + * The font family used to render text. + */ + fontFamily?: string; + + /** + * The line height used to render text. + */ + lineHeight?: number; + + /** + * The number of rows in the terminal. + */ + rows?: number; + + /** + * The amount of scrollback in the terminal. Scrollback is the amount of rows + * that are retained when lines are scrolled beyond the initial viewport. + */ + scrollback?: number; + + /** + * The size of tab stops in the terminal. + */ + tabStopWidth?: number; + + /** + * The color theme of the terminal. + */ + theme?: ITheme; + } + + /** + * Contains colors to theme the terminal with. + */ + interface ITheme { + /** The default foreground color */ + foreground?: string, + /** The default background color */ + background?: string, + /** The cursor color */ + cursor?: string, + /** The selection color (can be transparent) */ + selection?: string, + /** The accent color of the cursor (used as the foreground color for a block cursor) */ + cursorAccent?: string, + /** ANSI black (eg. `\x1b[30m`) */ + black?: string, + /** ANSI red (eg. `\x1b[31m`) */ + red?: string, + /** ANSI green (eg. `\x1b[32m`) */ + green?: string, + /** ANSI yellow (eg. `\x1b[33m`) */ + yellow?: string, + /** ANSI blue (eg. `\x1b[34m`) */ + blue?: string, + /** ANSI magenta (eg. `\x1b[35m`) */ + magenta?: string, + /** ANSI cyan (eg. `\x1b[36m`) */ + cyan?: string, + /** ANSI white (eg. `\x1b[37m`) */ + white?: string, + /** ANSI bright black (eg. `\x1b[1;30m`) */ + brightBlack?: string, + /** ANSI bright red (eg. `\x1b[1;31m`) */ + brightRed?: string, + /** ANSI bright green (eg. `\x1b[1;32m`) */ + brightGreen?: string, + /** ANSI bright yellow (eg. `\x1b[1;33m`) */ + brightYellow?: string, + /** ANSI bright blue (eg. `\x1b[1;34m`) */ + brightBlue?: string, + /** ANSI bright magenta (eg. `\x1b[1;35m`) */ + brightMagenta?: string, + /** ANSI bright cyan (eg. `\x1b[1;36m`) */ + brightCyan?: string, + /** ANSI bright white (eg. `\x1b[1;37m`) */ + brightWhite?: string + } + + /** + * An object containing options for a link matcher. + */ + interface ILinkMatcherOptions { + /** + * The index of the link from the regex.match(text) call. This defaults to 0 + * (for regular expressions without capture groups). + */ + matchIndex?: number; + + /** + * A callback that validates an individual link, returning true if valid and + * false if invalid. + */ + validationCallback?: (uri: string, callback: (isValid: boolean) => void) => void; + + /** + * A callback that fires when the mouse hovers over a link for a moment. + */ + tooltipCallback?: (event: MouseEvent, uri: string) => boolean | void; + + /** + * A callback that fires when the mouse leaves a link. Note that this can + * happen even when tooltipCallback hasn't fired for the link yet. + */ + leaveCallback?: (event: MouseEvent, uri: string) => boolean | void; + + /** + * The priority of the link matcher, this defines the order in which the link + * matcher is evaluated relative to others, from highest to lowest. The + * default value is 0. + */ + priority?: number; + } + /** * The class that represents an xterm.js terminal. */ @@ -397,7 +402,7 @@ declare module 'xterm' { * Retrieves an option's value from the terminal. * @param key The option key. */ - getOption(key: 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'debug' | 'disableStdin' | 'popOnBell' | 'screenKeys' | 'useFlowControl' | 'visualBell'): boolean; + getOption(key: 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'debug' | 'disableStdin' | 'enableBold' | 'popOnBell' | 'screenKeys' | 'useFlowControl' | 'visualBell'): boolean; /** * Retrieves an option's value from the terminal. * @param key The option key. @@ -447,7 +452,7 @@ declare module 'xterm' { * @param key The option key. * @param value The option value. */ - setOption(key: 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'debug' | 'disableStdin' | 'popOnBell' | 'screenKeys' | 'useFlowControl' | 'visualBell', value: boolean): void; + setOption(key: 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'debug' | 'disableStdin' | 'enableBold' | 'popOnBell' | 'screenKeys' | 'useFlowControl' | 'visualBell', value: boolean): void; /** * Sets an option on the terminal. * @param key The option key. diff --git a/src/vs/base/browser/ui/actionbar/actionbar.ts b/src/vs/base/browser/ui/actionbar/actionbar.ts index de30d225bc9..ab24a7d3995 100644 --- a/src/vs/base/browser/ui/actionbar/actionbar.ts +++ b/src/vs/base/browser/ui/actionbar/actionbar.ts @@ -595,6 +595,22 @@ export class ActionBar extends EventEmitter implements IActionRunner { }); } + public getWidth(index: number): number { + if (index >= 0 && index < this.actionsList.children.length) { + return this.actionsList.children.item(index).clientWidth; + } + + return 0; + } + + public getHeight(index: number): number { + if (index >= 0 && index < this.actionsList.children.length) { + return this.actionsList.children.item(index).clientHeight; + } + + return 0; + } + public pull(index: number): void { if (index >= 0 && index < this.items.length) { this.items.splice(index, 1); diff --git a/src/vs/base/browser/ui/iconLabel/iconLabel.ts b/src/vs/base/browser/ui/iconLabel/iconLabel.ts index 34f285e8c56..89dac41da35 100644 --- a/src/vs/base/browser/ui/iconLabel/iconLabel.ts +++ b/src/vs/base/browser/ui/iconLabel/iconLabel.ts @@ -19,7 +19,6 @@ export interface IIconLabelCreationOptions { } export interface ILabelBadgeOptions { - letter: string; title: string; className: string; } @@ -153,14 +152,11 @@ export class IconLabel { if (options && options.badge) { if (!this.badgeNode) { this.badgeNode = document.createElement('span'); - this.badgeNode.className = 'label-badge'; this.element.style.display = 'flex'; this.element.appendChild(this.badgeNode); } - const { letter, title } = options.badge; - this.badgeNode.innerHTML = letter; - this.badgeNode.title = title; - dom.addClass(this.badgeNode, options.badge.className); + this.badgeNode.title = options.badge.title; + this.badgeNode.className = `label-badge ${options.badge.className}`; dom.show(this.badgeNode); } else if (this.badgeNode) { diff --git a/src/vs/base/browser/ui/iconLabel/iconlabel.css b/src/vs/base/browser/ui/iconLabel/iconlabel.css index a1dda94d93e..259040da2c2 100644 --- a/src/vs/base/browser/ui/iconLabel/iconlabel.css +++ b/src/vs/base/browser/ui/iconLabel/iconlabel.css @@ -48,11 +48,11 @@ align-self: center; height: 12px; min-width: 10px; - line-height: 12px; + line-height: 125%; font-size: 80%; margin: 1px 15px 1px auto; - padding: 2px 4px; - border-radius: 14px; + padding: 2px 3px; + border-radius: 5px; font-weight: normal; text-align: center; } diff --git a/src/vs/base/common/OSSREADME.json b/src/vs/base/common/OSSREADME.json deleted file mode 100644 index e6948b8a57b..00000000000 --- a/src/vs/base/common/OSSREADME.json +++ /dev/null @@ -1,30 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: -[{ - "name": "string_scorer", - "version": "0.1.20", - "license": "MIT", - "repositoryURL": "https://github.com/joshaven/string_score", - "description": "The file quickOpenScorer.ts was inspired by the string_score algorithm from Joshaven Potter.", - "licenseDetail": [ - "This software is released under the MIT license:", - "", - "Copyright (c) Joshaven Potter", - "", - "Permission is hereby granted, free of charge, to any person obtaining a copy of", - "this software and associated documentation files (the \"Software\"), to deal in", - "the Software without restriction, including without limitation the rights to", - "use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of", - "the Software, and to permit persons to whom the Software is furnished to do so,", - "subject to the following conditions:", - "", - "The above copyright notice and this permission notice shall be included in all", - "copies or substantial portions of the Software.", - "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", - "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS", - "FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR", - "COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER", - "IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN", - "CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." - ] -}] diff --git a/src/vs/base/common/filters.ts b/src/vs/base/common/filters.ts index a943fc15c03..64abb96b9ad 100644 --- a/src/vs/base/common/filters.ts +++ b/src/vs/base/common/filters.ts @@ -421,7 +421,7 @@ function printTable(table: number[][], pattern: string, patternLen: number, word return ret; } -export function isSeparatorAtPos(value: string, index: number): boolean { +function isSeparatorAtPos(value: string, index: number): boolean { if (index < 0 || index >= value.length) { return false; } diff --git a/src/vs/base/common/jsonSchema.ts b/src/vs/base/common/jsonSchema.ts index 2aa8c50c9f7..538858e0c0d 100644 --- a/src/vs/base/common/jsonSchema.ts +++ b/src/vs/base/common/jsonSchema.ts @@ -6,6 +6,7 @@ export interface IJSONSchema { id?: string; + $id?: string; $schema?: string; type?: string | string[]; title?: string; @@ -17,7 +18,7 @@ export interface IJSONSchema { additionalProperties?: boolean | IJSONSchema; minProperties?: number; maxProperties?: number; - dependencies?: IJSONSchemaMap | { [name: string]: string[] }; + dependencies?: IJSONSchemaMap | { [prop: string]: string[] }; items?: IJSONSchema | IJSONSchema[]; minItems?: number; maxItems?: number; @@ -28,8 +29,8 @@ export interface IJSONSchema { maxLength?: number; minimum?: number; maximum?: number; - exclusiveMinimum?: boolean; - exclusiveMaximum?: boolean; + exclusiveMinimum?: boolean | number; + exclusiveMaximum?: boolean | number; multipleOf?: number; required?: string[]; $ref?: string; @@ -40,11 +41,19 @@ export interface IJSONSchema { enum?: any[]; format?: string; + // schema draft 06 + const?: any; + contains?: IJSONSchema; + propertyNames?: IJSONSchema; + + // VSCode extensions defaultSnippets?: IJSONSchemaSnippet[]; // VSCode extension errorMessage?: string; // VSCode extension patternErrorMessage?: string; // VSCode extension deprecationMessage?: string; // VSCode extension enumDescriptions?: string[]; // VSCode extension + markdownEnumDescriptions?: string[]; // VSCode extension + markdownDescription?: string; // VSCode extension doNotSuggest?: boolean; // VSCode extension } diff --git a/src/vs/base/common/map.ts b/src/vs/base/common/map.ts index 894f82082b6..bb7f231500b 100644 --- a/src/vs/base/common/map.ts +++ b/src/vs/base/common/map.ts @@ -452,11 +452,11 @@ export class TernarySearchTree { } } - forEach(callback: (entry: [string, E]) => any) { + forEach(callback: (value: E, index: string) => any) { this._forEach(this._root, [], callback); } - private _forEach(node: TernarySearchTreeNode, parts: string[], callback: (entry: [string, E]) => any) { + private _forEach(node: TernarySearchTreeNode, parts: string[], callback: (value: E, index: string) => any) { if (!node) { return; } @@ -465,7 +465,7 @@ export class TernarySearchTree { let newParts = parts.slice(); newParts.push(node.str); if (node.element) { - callback([this._segments.join(newParts), node.element]); + callback(node.element, this._segments.join(newParts)); } this._forEach(node.mid, newParts, callback); } diff --git a/src/vs/base/common/uri.ts b/src/vs/base/common/uri.ts index 921e34f3813..f6dcab7e786 100644 --- a/src/vs/base/common/uri.ts +++ b/src/vs/base/common/uri.ts @@ -73,7 +73,7 @@ const _driveLetter = /^[a-zA-Z]:/; * * */ -export default class URI { +export default class URI implements UriComponents { static isUri(thing: any): thing is URI { if (thing instanceof URI) { @@ -119,15 +119,35 @@ export default class URI { /** * @internal */ - protected 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 || _empty; - this.authority = authority || _empty; - this.path = path || _empty; - this.query = query || _empty; - this.fragment = fragment || _empty; + /** + * @internal + */ + protected constructor(components: UriComponents); - _validateUri(this); + /** + * @internal + */ + protected constructor(schemeOrData: string | UriComponents, authority?: string, path?: string, query?: string, fragment?: string) { + + if (typeof schemeOrData === 'object') { + this.scheme = schemeOrData.scheme || _empty; + this.authority = schemeOrData.authority || _empty; + this.path = schemeOrData.path || _empty; + this.query = schemeOrData.query || _empty; + this.fragment = schemeOrData.fragment || _empty; + // no validation because it's this URI + // that creates uri components. + // _validateUri(this); + } else { + this.scheme = schemeOrData || _empty; + this.authority = authority || _empty; + this.path = path || _empty; + this.query = query || _empty; + this.fragment = fragment || _empty; + _validateUri(this); + } } // ---- filesystem path ----------------------- @@ -264,7 +284,7 @@ export default class URI { return _asFormatted(this, skipEncoding); } - public toJSON(): any { + public toJSON(): object { const res = { $mid: 1, fsPath: this.fsPath, @@ -294,21 +314,15 @@ export default class URI { return res; } - static revive(data: any): URI { - let result = new _URI( - (data).scheme, - (data).authority, - (data).path, - (data).query, - (data).fragment - ); + static revive(data: UriComponents | any): URI { + let result = new _URI(data); result._fsPath = (data).fsPath; result._formatted = (data).external; return result; } } -interface UriComponents { +export interface UriComponents { scheme: string; authority: string; path: string; diff --git a/src/vs/base/node/ports.ts b/src/vs/base/node/ports.ts index c515e902d8b..1ab7de9ab2c 100644 --- a/src/vs/base/node/ports.ts +++ b/src/vs/base/node/ports.ts @@ -46,6 +46,10 @@ function doFindFreePort(startPort: number, giveUpAfter: number, clb: (port: numb return doFindFreePort(startPort + 1, giveUpAfter - 1, clb); }); + client.once('data', () => { + // this listener is required since node.js 8.x + }); + client.once('error', (err: Error & { code?: string }) => { dispose(client); diff --git a/src/vs/base/parts/quickopen/common/quickOpenScorer.ts b/src/vs/base/parts/quickopen/common/quickOpenScorer.ts index fe804a29d1f..5636beb5f50 100644 --- a/src/vs/base/parts/quickopen/common/quickOpenScorer.ts +++ b/src/vs/base/parts/quickopen/common/quickOpenScorer.ts @@ -6,189 +6,249 @@ 'use strict'; import { compareAnything } from 'vs/base/common/comparers'; -import { matchesPrefix, IMatch, createMatches, matchesCamelCase, isSeparatorAtPos, isUpper } from 'vs/base/common/filters'; +import { matchesPrefix, IMatch, createMatches, matchesCamelCase, isUpper } from 'vs/base/common/filters'; import { isEqual, nativeSep } from 'vs/base/common/paths'; import { isWindows } from 'vs/base/common/platform'; import { stripWildcards } from 'vs/base/common/strings'; +import { CharCode } from 'vs/base/common/charCode'; export type Score = [number /* score */, number[] /* match positions */]; export type ScorerCache = { [key: string]: IItemScore }; -const NO_SCORE: Score = [0, []]; +const NO_MATCH = 0; +const NO_SCORE: Score = [NO_MATCH, []]; -export function _doScore(target: string, query: string, fuzzy: boolean): Score { +// const DEBUG = false; +// const DEBUG_MATRIX = false; + +export function score(target: string, query: string, queryLower: string, fuzzy: boolean): Score { if (!target || !query) { return NO_SCORE; // return early if target or query are undefined } - if (target.length < query.length) { + const targetLength = target.length; + const queryLength = query.length; + + if (targetLength < queryLength) { return NO_SCORE; // impossible for query to be contained in target } - // console.group(`Target: ${target}, Query: ${query}`); + // if (DEBUG) { + // console.group(`Target: ${target}, Query: ${query}`); + // } - const queryLen = query.length; const targetLower = target.toLowerCase(); - const queryLower = query.toLowerCase(); - - let res = NO_SCORE; // When not searching fuzzy, we require the query to be contained fully - // in the target string. We set the offset to search from to that location. + // in the target string contiguously. if (!fuzzy) { const indexOfQueryInTarget = targetLower.indexOf(queryLower); if (indexOfQueryInTarget === -1) { - // console.log(`Characters not matching consecutively ${queryLower} within ${targetLower}`); + // if (DEBUG) { + // console.log(`Characters not matching consecutively ${queryLower} within ${targetLower}`); + // } return NO_SCORE; } - - res = _doScoreFromOffset(target, query, targetLower, queryLower, queryLen, indexOfQueryInTarget); } - // When searching fuzzy we run the scorer for each location of the first query - // character so that we can produce better results in case the pattern matches - // multiple times on the target (prevent scattering of matching positions). + // When searching fuzzy, we require the query to be contained fully + // in the target string as separate substrings else { - const queryFirstCharacter = queryLower[0]; + let targetOffset = 0; + for (let queryIndex = 0; queryIndex < queryLength; queryIndex++) { + targetOffset = targetLower.indexOf(queryLower[queryIndex], targetOffset); + if (targetOffset === -1) { + return NO_SCORE; + } + } + } - let offset = 0; - while ((offset = targetLower.indexOf(queryFirstCharacter, offset)) !== -1) { - const scoreFromOffset = _doScoreFromOffset(target, query, targetLower, queryLower, queryLen, offset); - if (isBetterScore(res, scoreFromOffset)) { - res = scoreFromOffset; + const res = doScore(query, queryLower, queryLength, target, targetLower, targetLength); + + // if (DEBUG) { + // console.log(`%cFinal Score: ${res[0]}`, 'font-weight: bold'); + // console.groupEnd(); + // } + + return res; +} + +function doScore(query: string, queryLower: string, queryLength: number, target: string, targetLower: string, targetLength: number): [number, number[]] { + const scores = []; + const matches = []; + + // + // Build Scorer Matrix + // The matrix is composed of query q and target t. For each index we score + // q[i] with t[i] and compare that with the previous score. If the score is + // equal or larger, we keep the match. In addition to the score, we also keep + // the length of the consecutive matches to use as boost for the score. + // + // t a r g e t + // q + // u + // e + // r + // y + // + for (let queryIndex = 0; queryIndex < queryLength; queryIndex++) { + for (let targetIndex = 0; targetIndex < targetLength; targetIndex++) { + const currentIndex = queryIndex * targetLength + targetIndex; + const leftIndex = currentIndex - 1; + const diagIndex = (queryIndex - 1) * targetLength + targetIndex - 1; + + const leftScore = targetIndex > 0 ? scores[leftIndex] : 0; + const diagScore = queryIndex > 0 && targetIndex > 0 ? scores[diagIndex] : 0; + + const matchesSequenceLength = queryIndex > 0 && targetIndex > 0 ? matches[diagIndex] : 0; + + const score = computeCharScore(query, queryLower, queryIndex, target, targetLower, targetIndex, matchesSequenceLength); + + // We have a score and its equal or larger than the left score + // Match: sequence continues growing from previous diag value + // Score: increases by diag score value + if (score && diagScore + score >= leftScore) { + matches[currentIndex] = matchesSequenceLength + 1; + scores[currentIndex] = diagScore + score; } - offset++; + // We either have no score or the score is lower than the left score + // Match: reset to 0 + // Score: pick up from left hand side + else { + matches[currentIndex] = NO_MATCH; + scores[currentIndex] = leftScore; + } } } - // console.log(`%cFinal Score: ${score}`, 'font-weight: bold'); - // console.groupEnd(); + // Restore Positions (starting from bottom right of matrix) + const positions = []; + let queryIndex = queryLength - 1; + let targetIndex = targetLength - 1; + while (queryIndex >= 0 && targetIndex >= 0) { + const currentIndex = queryIndex * targetLength + targetIndex; + const match = matches[currentIndex]; + if (match === NO_MATCH) { + targetIndex--; // go left + } else { + positions.push(targetIndex); - return res; + // go up and left + queryIndex--; + targetIndex--; + } + } + + // Print matrix + // if (DEBUG_MATRIX) { + // printMatrix(query, target, matches, scores); + // } + + return [scores[queryLength * targetLength - 1], positions.reverse()]; } -function isBetterScore(score: Score, candidate: Score): boolean { - if (candidate[0] > score[0]) { - return true; // candidate has higher score - } - - if (score[0] > candidate[0]) { - return false; // candidate has lower score - } - - // Score is the same, check by match compactness - const matchStart = score[1][0]; - const matchEnd = score[1][score[1].length - 1]; - const matchLength = matchEnd - matchStart; - - const candidateMatchStart = candidate[1][0]; - const candidateMatchEnd = candidate[1][candidate[1].length - 1]; - const candidateMatchLength = candidateMatchEnd - candidateMatchStart; - - if (candidateMatchLength < matchLength) { - return true; // candidate has more compact matches - } - - return false; -} - -// Based on material from: -/*! -BEGIN THIRD PARTY -*/ -/*! -* string_score.js: String Scoring Algorithm 0.1.22 -* -* http://joshaven.com/string_score -* https://github.com/joshaven/string_score -* -* Copyright (C) 2009-2014 Joshaven Potter -* Special thanks to all of the contributors listed here https://github.com/joshaven/string_score -* MIT License: http://opensource.org/licenses/MIT -* -* Date: Tue Mar 1 2011 -* Updated: Tue Mar 10 2015 -*/ -function _doScoreFromOffset(target: string, query: string, targetLower: string, queryLower: string, queryLen: number, offset: number): Score { - const matchingPositions: number[] = []; - - let targetIndex = offset; - let queryIndex = 0; +function computeCharScore(query: string, queryLower: string, queryIndex: number, target: string, targetLower: string, targetIndex: number, matchesSequenceLength: number): number { let score = 0; - while (queryIndex < queryLen) { - // Check for query character being contained in target - const indexOfQueryInTarget = targetLower.indexOf(queryLower[queryIndex], targetIndex); + if (queryLower[queryIndex] !== targetLower[targetIndex]) { + return score; // no match of characters + } - if (indexOfQueryInTarget < 0) { - // console.log(`Character not part of target ${query[index]}`); + // Character match bonus + score += 1; - score = 0; - break; - } + // if (DEBUG) { + // console.groupCollapsed(`%cCharacter match bonus: +1 (char: ${queryLower[queryIndex]} at index ${targetIndex}, total score: ${score})`, 'font-weight: normal'); + // } - // Fill into positions array - matchingPositions.push(indexOfQueryInTarget); + // Consecutive match bonus + if (matchesSequenceLength > 0) { + score += (matchesSequenceLength * 5); - // Character match bonus + // if (DEBUG) { + // console.log('Consecutive match bonus: ' + (matchesSequenceLength * 5)); + // } + } + + // Same case bonus + if (query[queryIndex] === target[targetIndex]) { score += 1; - // console.groupCollapsed(`%cCharacter match bonus: +1 (char: ${query[index]} at index ${indexOf}, total score: ${score})`, 'font-weight: normal'); - - // Consecutive match bonus - if (targetIndex === indexOfQueryInTarget && queryIndex > 0) { - score += 5; - - // console.log('Consecutive match bonus: +5'); - } - - // Same case bonus - if (target[indexOfQueryInTarget] === query[queryIndex]) { - score += 1; - - // console.log('Same case bonus: +1'); - } - - // Start of word bonus - if (indexOfQueryInTarget === 0) { - score += 8; - - // console.log('Start of word bonus: +8'); - } - - // After separator bonus - else if (isSeparatorAtPos(target, indexOfQueryInTarget - 1)) { - score += 7; - - // console.log('After separtor bonus: +7'); - } - - // Inside word upper case bonus - else if (isUpper(target.charCodeAt(indexOfQueryInTarget))) { - score += 1; - - // console.log('Inside word upper case bonus: +1'); - } - - // console.groupEnd(); - - targetIndex = indexOfQueryInTarget + 1; - queryIndex++; + // if (DEBUG) { + // console.log('Same case bonus: +1'); + // } } - const res: Score = (score > 0) ? [score, matchingPositions] : NO_SCORE; + // Start of word bonus + if (targetIndex === 0) { + score += 8; - // console.log(`%cFinal Score: ${score}`, 'font-weight: bold'); - // console.groupEnd(); + // if (DEBUG) { + // console.log('Start of word bonus: +8'); + // } + } - return res; + else { + + // After separator bonus + const separatorBonus = scoreSeparatorAtPos(target.charCodeAt(targetIndex - 1)); + if (separatorBonus) { + score += separatorBonus; + + // if (DEBUG) { + // console.log('After separtor bonus: +4'); + // } + } + + // Inside word upper case bonus (camel case) + else if (isUpper(target.charCodeAt(targetIndex))) { + score += 1; + + // if (DEBUG) { + // console.log('Inside word upper case bonus: +1'); + // } + } + } + + // if (DEBUG) { + // console.groupEnd(); + // } + + return score; } -/*! -END THIRD PARTY -*/ +function scoreSeparatorAtPos(charCode: number): number { + switch (charCode) { + case CharCode.Slash: + case CharCode.Backslash: + return 5; // prefer path separators... + case CharCode.Underline: + case CharCode.Dash: + case CharCode.Period: + case CharCode.Space: + case CharCode.SingleQuote: + case CharCode.DoubleQuote: + case CharCode.Colon: + return 4; // ...over other separators + default: + return 0; + } +} + +// function printMatrix(query: string, target: string, matches: number[], scores: number[]): void { +// console.log('\t' + target.split('').join('\t')); +// for (let queryIndex = 0; queryIndex < query.length; queryIndex++) { +// let line = query[queryIndex] + '\t'; +// for (let targetIndex = 0; targetIndex < target.length; targetIndex++) { +// const currentIndex = queryIndex * target.length + targetIndex; +// line = line + 'M' + matches[currentIndex] + '/' + 'S' + scores[currentIndex] + '\t'; +// } + +// console.log(line); +// } +// } /** * Scoring on structural items that have a label and optional description. @@ -236,8 +296,34 @@ const LABEL_PREFIX_SCORE = 1 << 17; const LABEL_CAMELCASE_SCORE = 1 << 16; const LABEL_SCORE_THRESHOLD = 1 << 15; -export function scoreItem(item: T, query: string, fuzzy: boolean, accessor: IItemAccessor, cache: ScorerCache): IItemScore { - if (!item || !query) { +export interface IPreparedQuery { + value: string; + lowercase: string; + containsPathSeparator: boolean; +} + +/** + * Helper function to prepare a search value for scoring in quick open by removing unwanted characters. + */ +export function prepareQuery(value: string): IPreparedQuery { + let lowercase: string; + let containsPathSeparator: boolean; + + if (value) { + value = stripWildcards(value).replace(/\s/g, ''); // get rid of all wildcards and whitespace + if (isWindows) { + value = value.replace(/\//g, '\\'); // Help Windows users to search for paths when using slash + } + + lowercase = value.toLowerCase(); + containsPathSeparator = value.indexOf(nativeSep) >= 0; + } + + return { value, lowercase, containsPathSeparator }; +} + +export function scoreItem(item: T, query: IPreparedQuery, fuzzy: boolean, accessor: IItemAccessor, cache: ScorerCache): IItemScore { + if (!item || !query.value) { return NO_ITEM_SCORE; // we need an item and query to score on at least } @@ -250,9 +336,9 @@ export function scoreItem(item: T, query: string, fuzzy: boolean, accessor: I let cacheHash: string; if (description) { - cacheHash = `${label}${description}${query}${fuzzy}`; + cacheHash = `${label}${description}${query.value}${fuzzy}`; } else { - cacheHash = `${label}${query}${fuzzy}`; + cacheHash = `${label}${query.value}${fuzzy}`; } const cached = cache[cacheHash]; @@ -266,31 +352,31 @@ export function scoreItem(item: T, query: string, fuzzy: boolean, accessor: I return itemScore; } -function doScoreItem(label: string, description: string, path: string, query: string, fuzzy: boolean): IItemScore { +function doScoreItem(label: string, description: string, path: string, query: IPreparedQuery, fuzzy: boolean): IItemScore { // 1.) treat identity matches on full path highest - if (path && isEqual(query, path, true)) { + if (path && isEqual(query.value, path, true)) { return { score: PATH_IDENTITY_SCORE, labelMatch: [{ start: 0, end: label.length }], descriptionMatch: description ? [{ start: 0, end: description.length }] : void 0 }; } // We only consider label matches if the query is not including file path separators - const preferLabelMatches = !path || query.indexOf(nativeSep) === -1; + const preferLabelMatches = !path || !query.containsPathSeparator; if (preferLabelMatches) { // 2.) treat prefix matches on the label second highest - const prefixLabelMatch = matchesPrefix(query, label); + const prefixLabelMatch = matchesPrefix(query.value, label); if (prefixLabelMatch) { return { score: LABEL_PREFIX_SCORE, labelMatch: prefixLabelMatch }; } // 3.) treat camelcase matches on the label third highest - const camelcaseLabelMatch = matchesCamelCase(query, label); + const camelcaseLabelMatch = matchesCamelCase(query.value, label); if (camelcaseLabelMatch) { return { score: LABEL_CAMELCASE_SCORE, labelMatch: camelcaseLabelMatch }; } // 4.) prefer scores on the label if any - const [labelScore, labelPositions] = _doScore(label, query, fuzzy); + const [labelScore, labelPositions] = score(label, query.value, query.lowercase, fuzzy); if (labelScore) { return { score: labelScore + LABEL_SCORE_THRESHOLD, labelMatch: createMatches(labelPositions) }; } @@ -306,7 +392,7 @@ function doScoreItem(label: string, description: string, path: string, query: const descriptionPrefixLength = descriptionPrefix.length; const descriptionAndLabel = `${descriptionPrefix}${label}`; - const [labelDescriptionScore, labelDescriptionPositions] = _doScore(descriptionAndLabel, query, fuzzy); + const [labelDescriptionScore, labelDescriptionPositions] = score(descriptionAndLabel, query.value, query.lowercase, fuzzy); if (labelDescriptionScore) { const labelDescriptionMatches = createMatches(labelDescriptionPositions); const labelMatch: IMatch[] = []; @@ -339,7 +425,7 @@ function doScoreItem(label: string, description: string, path: string, query: return NO_ITEM_SCORE; } -export function compareItemsByScore(itemA: T, itemB: T, query: string, fuzzy: boolean, accessor: IItemAccessor, cache: ScorerCache, fallbackComparer = fallbackCompare): number { +export function compareItemsByScore(itemA: T, itemB: T, query: IPreparedQuery, fuzzy: boolean, accessor: IItemAccessor, cache: ScorerCache, fallbackComparer = fallbackCompare): number { const itemScoreA = scoreItem(itemA, query, fuzzy, accessor, cache); const itemScoreB = scoreItem(itemB, query, fuzzy, accessor, cache); @@ -482,7 +568,7 @@ function compareByMatchLength(matchesA?: IMatch[], matchesB?: IMatch[]): number return matchLengthA === matchLengthB ? 0 : matchLengthB < matchLengthA ? 1 : -1; } -export function fallbackCompare(itemA: T, itemB: T, query: string, accessor: IItemAccessor): number { +export function fallbackCompare(itemA: T, itemB: T, query: IPreparedQuery, accessor: IItemAccessor): number { // check for label + description length and prefer shorter const labelA = accessor.getItemLabel(itemA); @@ -510,33 +596,19 @@ export function fallbackCompare(itemA: T, itemB: T, query: string, accessor: // compare by label if (labelA !== labelB) { - return compareAnything(labelA, labelB, query); + return compareAnything(labelA, labelB, query.value); } // compare by description if (descriptionA && descriptionB && descriptionA !== descriptionB) { - return compareAnything(descriptionA, descriptionB, query); + return compareAnything(descriptionA, descriptionB, query.value); } // compare by path if (pathA && pathB && pathA !== pathB) { - return compareAnything(pathA, pathB, query); + return compareAnything(pathA, pathB, query.value); } // equal return 0; -} - -/** - * Helper function to prepare a search value for scoring in quick open by removing unwanted characters. - */ -export function massageSearchForScoring(searchValue: string): string { - if (searchValue) { - searchValue = stripWildcards(searchValue).replace(/\s/g, ''); // get rid of all wildcards and whitespace - if (isWindows) { - searchValue = searchValue.replace(/\//g, '\\'); // Help Windows users to search for paths when using slash - } - } - - return searchValue; } \ No newline at end of file diff --git a/src/vs/base/parts/quickopen/test/common/quickOpenScorer.test.ts b/src/vs/base/parts/quickopen/test/common/quickOpenScorer.test.ts index 8aedf7ce3ea..d9760c58368 100644 --- a/src/vs/base/parts/quickopen/test/common/quickOpenScorer.test.ts +++ b/src/vs/base/parts/quickopen/test/common/quickOpenScorer.test.ts @@ -8,7 +8,7 @@ import * as assert from 'assert'; import * as scorer from 'vs/base/parts/quickopen/common/quickOpenScorer'; import URI from 'vs/base/common/uri'; -import { basename, dirname } from 'vs/base/common/paths'; +import { basename, dirname, nativeSep } from 'vs/base/common/paths'; import { isWindows } from 'vs/base/common/platform'; class ResourceAccessorClass implements scorer.IItemAccessor { @@ -43,28 +43,44 @@ class NullAccessorClass implements scorer.IItemAccessor { } } +function _doScore(target: string, query: string, fuzzy: boolean): scorer.Score { + return scorer.score(target, query, query.toLowerCase(), fuzzy); +} + +function scoreItem(item: T, query: string, fuzzy: boolean, accessor: scorer.IItemAccessor, cache: scorer.ScorerCache): scorer.IItemScore { + return scorer.scoreItem(item, scorer.prepareQuery(query), fuzzy, accessor, cache); +} + +function compareItemsByScore(itemA: T, itemB: T, query: string, fuzzy: boolean, accessor: scorer.IItemAccessor, cache: scorer.ScorerCache, fallbackComparer = scorer.fallbackCompare): number { + return scorer.compareItemsByScore(itemA, itemB, scorer.prepareQuery(query), fuzzy, accessor, cache, fallbackComparer); +} + const NullAccessor = new NullAccessorClass(); -const cache: scorer.ScorerCache = Object.create(null); +let cache: scorer.ScorerCache = Object.create(null); suite('Quick Open Scorer', () => { + setup(() => { + cache = Object.create(null); + }); + test('score (fuzzy)', function () { const target = 'HeLlo-World'; const scores: scorer.Score[] = []; - scores.push(scorer._doScore(target, 'HelLo-World', true)); // direct case match - scores.push(scorer._doScore(target, 'hello-world', true)); // direct mix-case match - scores.push(scorer._doScore(target, 'HW', true)); // direct case prefix (multiple) - scores.push(scorer._doScore(target, 'hw', true)); // direct mix-case prefix (multiple) - scores.push(scorer._doScore(target, 'H', true)); // direct case prefix - scores.push(scorer._doScore(target, 'h', true)); // direct mix-case prefix - scores.push(scorer._doScore(target, 'ld', true)); // in-string mix-case match (consecutive, avoids scattered hit) - scores.push(scorer._doScore(target, 'W', true)); // direct case word prefix - scores.push(scorer._doScore(target, 'w', true)); // direct mix-case word prefix - scores.push(scorer._doScore(target, 'Ld', true)); // in-string case match (multiple) - scores.push(scorer._doScore(target, 'L', true)); // in-string case match - scores.push(scorer._doScore(target, 'l', true)); // in-string mix-case match - scores.push(scorer._doScore(target, '4', true)); // no match + scores.push(_doScore(target, 'HelLo-World', true)); // direct case match + scores.push(_doScore(target, 'hello-world', true)); // direct mix-case match + scores.push(_doScore(target, 'HW', true)); // direct case prefix (multiple) + scores.push(_doScore(target, 'hw', true)); // direct mix-case prefix (multiple) + scores.push(_doScore(target, 'H', true)); // direct case prefix + scores.push(_doScore(target, 'h', true)); // direct mix-case prefix + scores.push(_doScore(target, 'ld', true)); // in-string mix-case match (consecutive, avoids scattered hit) + scores.push(_doScore(target, 'W', true)); // direct case word prefix + scores.push(_doScore(target, 'w', true)); // direct mix-case word prefix + scores.push(_doScore(target, 'Ld', true)); // in-string case match (multiple) + scores.push(_doScore(target, 'L', true)); // in-string case match + scores.push(_doScore(target, 'l', true)); // in-string mix-case match + scores.push(_doScore(target, '4', true)); // no match // Assert scoring order let sortedScores = scores.concat().sort((a, b) => b[0] - a[0]); @@ -83,28 +99,28 @@ suite('Quick Open Scorer', () => { test('score (non fuzzy)', function () { const target = 'HeLlo-World'; - assert.ok(scorer._doScore(target, 'HelLo-World', false)[0] > 0); - assert.equal(scorer._doScore(target, 'HelLo-World', false)[1].length, 'HelLo-World'.length); + assert.ok(_doScore(target, 'HelLo-World', false)[0] > 0); + assert.equal(_doScore(target, 'HelLo-World', false)[1].length, 'HelLo-World'.length); - assert.ok(scorer._doScore(target, 'hello-world', false)[0] > 0); - assert.equal(scorer._doScore(target, 'HW', false)[0], 0); - assert.ok(scorer._doScore(target, 'h', false)[0] > 0); - assert.ok(scorer._doScore(target, 'ello', false)[0] > 0); - assert.ok(scorer._doScore(target, 'ld', false)[0] > 0); - assert.equal(scorer._doScore(target, 'eo', false)[0], 0); + assert.ok(_doScore(target, 'hello-world', false)[0] > 0); + assert.equal(_doScore(target, 'HW', false)[0], 0); + assert.ok(_doScore(target, 'h', false)[0] > 0); + assert.ok(_doScore(target, 'ello', false)[0] > 0); + assert.ok(_doScore(target, 'ld', false)[0] > 0); + assert.equal(_doScore(target, 'eo', false)[0], 0); }); test('scoreItem - matches are proper', function () { - let res = scorer.scoreItem(null, 'something', true, ResourceAccessor, cache); + let res = scoreItem(null, 'something', true, ResourceAccessor, cache); assert.ok(!res.score); const resource = URI.file('/xyz/some/path/someFile123.txt'); - res = scorer.scoreItem(resource, 'something', true, NullAccessor, cache); + res = scoreItem(resource, 'something', true, NullAccessor, cache); assert.ok(!res.score); // Path Identity - const identityRes = scorer.scoreItem(resource, ResourceAccessor.getItemPath(resource), true, ResourceAccessor, cache); + const identityRes = scoreItem(resource, ResourceAccessor.getItemPath(resource), true, ResourceAccessor, cache); assert.ok(identityRes.score); assert.equal(identityRes.descriptionMatch.length, 1); assert.equal(identityRes.labelMatch.length, 1); @@ -114,7 +130,7 @@ suite('Quick Open Scorer', () => { assert.equal(identityRes.labelMatch[0].end, ResourceAccessor.getItemLabel(resource).length); // Basename Prefix - const basenamePrefixRes = scorer.scoreItem(resource, 'som', true, ResourceAccessor, cache); + const basenamePrefixRes = scoreItem(resource, 'som', true, ResourceAccessor, cache); assert.ok(basenamePrefixRes.score); assert.ok(!basenamePrefixRes.descriptionMatch); assert.equal(basenamePrefixRes.labelMatch.length, 1); @@ -122,7 +138,7 @@ suite('Quick Open Scorer', () => { assert.equal(basenamePrefixRes.labelMatch[0].end, 'som'.length); // Basename Camelcase - const basenameCamelcaseRes = scorer.scoreItem(resource, 'sF', true, ResourceAccessor, cache); + const basenameCamelcaseRes = scoreItem(resource, 'sF', true, ResourceAccessor, cache); assert.ok(basenameCamelcaseRes.score); assert.ok(!basenameCamelcaseRes.descriptionMatch); assert.equal(basenameCamelcaseRes.labelMatch.length, 2); @@ -132,7 +148,7 @@ suite('Quick Open Scorer', () => { assert.equal(basenameCamelcaseRes.labelMatch[1].end, 5); // Basename Match - const basenameRes = scorer.scoreItem(resource, 'of', true, ResourceAccessor, cache); + const basenameRes = scoreItem(resource, 'of', true, ResourceAccessor, cache); assert.ok(basenameRes.score); assert.ok(!basenameRes.descriptionMatch); assert.equal(basenameRes.labelMatch.length, 2); @@ -142,7 +158,7 @@ suite('Quick Open Scorer', () => { assert.equal(basenameRes.labelMatch[1].end, 5); // Path Match - const pathRes = scorer.scoreItem(resource, 'xyz123', true, ResourceAccessor, cache); + const pathRes = scoreItem(resource, 'xyz123', true, ResourceAccessor, cache); assert.ok(pathRes.score); assert.ok(pathRes.descriptionMatch); assert.ok(pathRes.labelMatch); @@ -154,7 +170,7 @@ suite('Quick Open Scorer', () => { assert.equal(pathRes.descriptionMatch[0].end, 4); // No Match - const noRes = scorer.scoreItem(resource, '987', true, ResourceAccessor, cache); + const noRes = scoreItem(resource, '987', true, ResourceAccessor, cache); assert.ok(!noRes.score); assert.ok(!noRes.labelMatch); assert.ok(!noRes.descriptionMatch); @@ -168,10 +184,10 @@ suite('Quick Open Scorer', () => { test('scoreItem - invalid input', function () { - let res = scorer.scoreItem(null, null, true, ResourceAccessor, cache); + let res = scoreItem(null, null, true, ResourceAccessor, cache); assert.equal(res.score, 0); - res = scorer.scoreItem(null, 'null', true, ResourceAccessor, cache); + res = scoreItem(null, 'null', true, ResourceAccessor, cache); assert.equal(res.score, 0); }); @@ -181,7 +197,7 @@ suite('Quick Open Scorer', () => { // xsp is more relevant to the end of the file path even though it matches // fuzzy also in the beginning. we verify the more relevant match at the // end gets returned. - const pathRes = scorer.scoreItem(resource, 'xspfile123', true, ResourceAccessor, cache); + const pathRes = scoreItem(resource, 'xspfile123', true, ResourceAccessor, cache); assert.ok(pathRes.score); assert.ok(pathRes.descriptionMatch); assert.ok(pathRes.labelMatch); @@ -193,12 +209,24 @@ suite('Quick Open Scorer', () => { assert.equal(pathRes.descriptionMatch[0].end, 26); }); + test('scoreItem - avoid match scattering (bug #36119)', function () { + const resource = URI.file('projects/ui/cula/ats/target.mk');; + + const pathRes = scoreItem(resource, 'tcltarget.mk', true, ResourceAccessor, cache); + assert.ok(pathRes.score); + assert.ok(pathRes.descriptionMatch); + assert.ok(pathRes.labelMatch); + assert.equal(pathRes.labelMatch.length, 1); + assert.equal(pathRes.labelMatch[0].start, 0); + assert.equal(pathRes.labelMatch[0].end, 9); + }); + test('scoreItem - prefers more compact matches', function () { const resource = URI.file('/1a111d1/11a1d1/something.txt'); // expect "ad" to be matched towards the end of the file because the // match is more compact - const res = scorer.scoreItem(resource, 'ad', true, ResourceAccessor, cache); + const res = scoreItem(resource, 'ad', true, ResourceAccessor, cache); assert.ok(res.score); assert.ok(res.descriptionMatch); assert.ok(!res.labelMatch.length); @@ -217,12 +245,12 @@ suite('Quick Open Scorer', () => { // Full resource A path let query = ResourceAccessor.getItemPath(resourceA); - let res = [resourceA, resourceB, resourceC].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceB); assert.equal(res[2], resourceC); - res = [resourceC, resourceB, resourceA].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceB); assert.equal(res[2], resourceC); @@ -230,12 +258,12 @@ suite('Quick Open Scorer', () => { // Full resource B path query = ResourceAccessor.getItemPath(resourceB); - res = [resourceA, resourceB, resourceC].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); assert.equal(res[2], resourceC); - res = [resourceC, resourceB, resourceA].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); assert.equal(res[2], resourceC); @@ -249,12 +277,12 @@ suite('Quick Open Scorer', () => { // Full resource A basename let query = ResourceAccessor.getItemLabel(resourceA); - let res = [resourceA, resourceB, resourceC].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceB); assert.equal(res[2], resourceC); - res = [resourceC, resourceB, resourceA].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceB); assert.equal(res[2], resourceC); @@ -262,12 +290,12 @@ suite('Quick Open Scorer', () => { // Full resource B basename query = ResourceAccessor.getItemLabel(resourceB); - res = [resourceA, resourceB, resourceC].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); assert.equal(res[2], resourceC); - res = [resourceC, resourceB, resourceA].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); assert.equal(res[2], resourceC); @@ -281,12 +309,12 @@ suite('Quick Open Scorer', () => { // resource A camelcase let query = 'fA'; - let res = [resourceA, resourceB, resourceC].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceB); assert.equal(res[2], resourceC); - res = [resourceC, resourceB, resourceA].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceB); assert.equal(res[2], resourceC); @@ -294,12 +322,12 @@ suite('Quick Open Scorer', () => { // resource B camelcase query = 'fB'; - res = [resourceA, resourceB, resourceC].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); assert.equal(res[2], resourceC); - res = [resourceC, resourceB, resourceA].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); assert.equal(res[2], resourceC); @@ -313,12 +341,12 @@ suite('Quick Open Scorer', () => { // Resource A part of basename let query = 'fileA'; - let res = [resourceA, resourceB, resourceC].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceB); assert.equal(res[2], resourceC); - res = [resourceC, resourceB, resourceA].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceB); assert.equal(res[2], resourceC); @@ -326,12 +354,12 @@ suite('Quick Open Scorer', () => { // Resource B part of basename query = 'fileB'; - res = [resourceA, resourceB, resourceC].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); assert.equal(res[2], resourceC); - res = [resourceC, resourceB, resourceA].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); assert.equal(res[2], resourceC); @@ -345,12 +373,12 @@ suite('Quick Open Scorer', () => { // Resource A part of path let query = 'pathfileA'; - let res = [resourceA, resourceB, resourceC].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceB); assert.equal(res[2], resourceC); - res = [resourceC, resourceB, resourceA].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceB); assert.equal(res[2], resourceC); @@ -358,12 +386,12 @@ suite('Quick Open Scorer', () => { // Resource B part of path query = 'pathfileB'; - res = [resourceA, resourceB, resourceC].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); assert.equal(res[2], resourceC); - res = [resourceC, resourceB, resourceA].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); assert.equal(res[2], resourceC); @@ -377,12 +405,12 @@ suite('Quick Open Scorer', () => { // Resource A part of path let query = 'somepath'; - let res = [resourceA, resourceB, resourceC].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceB); assert.equal(res[2], resourceC); - res = [resourceC, resourceB, resourceA].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceB); assert.equal(res[2], resourceC); @@ -396,12 +424,12 @@ suite('Quick Open Scorer', () => { // Resource A part of path let query = 'file'; - let res = [resourceA, resourceB, resourceC].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceC); assert.equal(res[2], resourceB); - res = [resourceC, resourceB, resourceA].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceC); assert.equal(res[2], resourceB); @@ -415,12 +443,12 @@ suite('Quick Open Scorer', () => { // Resource A part of path let query = 'somepath'; - let res = [resourceA, resourceB, resourceC].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceB); assert.equal(res[2], resourceC); - res = [resourceC, resourceB, resourceA].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceB); assert.equal(res[2], resourceC); @@ -433,7 +461,7 @@ suite('Quick Open Scorer', () => { let query = 'co/te'; - let res = [resourceA, resourceB, resourceC].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); assert.equal(res[2], resourceC); @@ -445,11 +473,11 @@ suite('Quick Open Scorer', () => { let query = 'vscode'; - let res = [resourceA, resourceB].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache, (r1, r2, query, ResourceAccessor) => -1)); + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache, (r1, r2, query, ResourceAccessor) => -1)); assert.equal(res[0], resourceA); assert.equal(res[1], resourceB); - res = [resourceB, resourceA].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache, (r1, r2, query, ResourceAccessor) => -1)); + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache, (r1, r2, query, ResourceAccessor) => -1)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); }); @@ -460,11 +488,11 @@ suite('Quick Open Scorer', () => { let query = 'AH'; - let res = [resourceA, resourceB].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); - res = [resourceB, resourceA].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); }); @@ -475,11 +503,11 @@ suite('Quick Open Scorer', () => { let query = 'xp'; - let res = [resourceA, resourceB].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); - res = [resourceB, resourceA].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); }); @@ -490,11 +518,11 @@ suite('Quick Open Scorer', () => { let query = 'xp'; - let res = [resourceA, resourceB].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); - res = [resourceB, resourceA].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); }); @@ -505,11 +533,11 @@ suite('Quick Open Scorer', () => { let query = 'exfile'; - let res = [resourceA, resourceB].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); - res = [resourceB, resourceA].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); assert.equal(res[0], resourceB); assert.equal(res[1], resourceA); }); @@ -522,24 +550,46 @@ suite('Quick Open Scorer', () => { let query = isWindows ? 'modu1\\index.js' : 'modu1/index.js'; - let res = [resourceA, resourceB, resourceC, resourceD].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB, resourceC, resourceD].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + assert.equal(res[0], resourceC); + + res = [resourceC, resourceB, resourceA, resourceD].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); assert.equal(res[0], resourceC); query = isWindows ? 'un1\\index.js' : 'un1/index.js'; - res = [resourceA, resourceB, resourceC, resourceD].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + res = [resourceA, resourceB, resourceC, resourceD].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + assert.equal(res[0], resourceB); + + res = [resourceC, resourceB, resourceA, resourceD].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); assert.equal(res[0], resourceB); }); - test('compareFilesByScore - avoid match scattering (bug #21019)', function () { + test('compareFilesByScore - avoid match scattering (bug #21019 1.)', function () { const resourceA = URI.file('app/containers/Services/NetworkData/ServiceDetails/ServiceLoad/index.js'); const resourceB = URI.file('app/containers/Services/NetworkData/ServiceDetails/ServiceDistribution/index.js'); const resourceC = URI.file('app/containers/Services/NetworkData/ServiceDetailTabs/ServiceTabs/StatVideo/index.js'); let query = 'StatVideoindex'; - let res = [resourceA, resourceB, resourceC].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); assert.equal(res[0], resourceC); + + res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + assert.equal(res[0], resourceC); + }); + + test('compareFilesByScore - avoid match scattering (bug #21019 2.)', function () { + const resourceA = URI.file('src/build-helper/store/redux.ts'); + const resourceB = URI.file('src/repository/store/redux.ts'); + + let query = 'reproreduxts'; + + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + assert.equal(res[0], resourceB); + + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + assert.equal(res[0], resourceB); }); test('compareFilesByScore - avoid match scattering (bug #26649)', function () { @@ -549,7 +599,10 @@ suite('Quick Open Scorer', () => { let query = 'bookpageIndex'; - let res = [resourceA, resourceB, resourceC].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + assert.equal(res[0], resourceC); + + res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); assert.equal(res[0], resourceC); }); @@ -559,7 +612,10 @@ suite('Quick Open Scorer', () => { let query = isWindows ? 'ui\\icons' : 'ui/icons'; - let res = [resourceA, resourceB].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + assert.equal(res[0], resourceB); + + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); assert.equal(res[0], resourceB); }); @@ -569,7 +625,106 @@ suite('Quick Open Scorer', () => { let query = isWindows ? 'ui\\input\\index' : 'ui/input/index'; - let res = [resourceA, resourceB].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + assert.equal(res[0], resourceB); + + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + assert.equal(res[0], resourceB); + }); + + test('compareFilesByScore - avoid match scattering (bug #36166)', function () { + const resourceA = URI.file('django/contrib/sites/locale/ga/LC_MESSAGES/django.mo'); + const resourceB = URI.file('django/core/signals.py'); + + let query = 'djancosig'; + + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + assert.equal(res[0], resourceB); + + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + assert.equal(res[0], resourceB); + }); + + test('compareFilesByScore - avoid match scattering (bug #32918)', function () { + const resourceA = URI.file('adsys/protected/config.php'); + const resourceB = URI.file('adsys/protected/framework/smarty/sysplugins/smarty_internal_config.php'); + const resourceC = URI.file('duowanVideo/wap/protected/config.php'); + + let query = 'protectedconfig.php'; + + let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + assert.equal(res[0], resourceA); + assert.equal(res[1], resourceC); + assert.equal(res[2], resourceB); + + res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + assert.equal(res[0], resourceA); + assert.equal(res[1], resourceC); + assert.equal(res[2], resourceB); + }); + + test('compareFilesByScore - avoid match scattering (bug #14879)', function () { + const resourceA = URI.file('pkg/search/gradient/testdata/constraint_attrMatchString.yml'); + const resourceB = URI.file('cmd/gradient/main.go'); + + let query = 'gradientmain'; + + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + assert.equal(res[0], resourceB); + + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + assert.equal(res[0], resourceB); + }); + + test('compareFilesByScore - avoid match scattering (bug #14727 1)', function () { + const resourceA = URI.file('alpha-beta-cappa.txt'); + const resourceB = URI.file('abc.txt'); + + let query = 'abc'; + + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + assert.equal(res[0], resourceB); + + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + assert.equal(res[0], resourceB); + }); + + test('compareFilesByScore - avoid match scattering (bug #14727 2)', function () { + const resourceA = URI.file('xerxes-yak-zubba/index.js'); + const resourceB = URI.file('xyz/index.js'); + + let query = 'xyz'; + + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + assert.equal(res[0], resourceB); + + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + assert.equal(res[0], resourceB); + }); + + test('compareFilesByScore - avoid match scattering (bug #18381)', function () { + const resourceA = URI.file('AssymblyInfo.cs'); + const resourceB = URI.file('IAsynchronousTask.java'); + + let query = 'async'; + + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + assert.equal(res[0], resourceB); + + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + assert.equal(res[0], resourceB); + }); + + test('compareFilesByScore - avoid match scattering (bug #35572)', function () { + const resourceA = URI.file('static/app/source/angluar/-admin/-organization/-settings/layout/layout.js'); + const resourceB = URI.file('static/app/source/angular/-admin/-project/-settings/_settings/settings.js'); + + let query = 'partisettings'; + + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + assert.equal(res[0], resourceB); + + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); assert.equal(res[0], resourceB); }); @@ -579,12 +734,32 @@ suite('Quick Open Scorer', () => { let query = 'listview'; - let res = [resourceA, resourceB].sort((r1, r2) => scorer.compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + assert.equal(res[0], resourceB); + + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); assert.equal(res[0], resourceB); }); - test('massageSearchForScoring', function () { - assert.equal(scorer.massageSearchForScoring(' f*a '), 'fa'); - assert.equal(scorer.massageSearchForScoring('model tester.ts'), 'modeltester.ts'); + test('compareFilesByScore - avoid match scattering (bug #12095)', function () { + const resourceA = URI.file('src/vs/workbench/parts/files/common/explorerViewModel.ts'); + const resourceB = URI.file('src/vs/workbench/parts/files/browser/views/explorerView.ts'); + const resourceC = URI.file('src/vs/workbench/parts/files/browser/views/explorerViewer.ts'); + + let query = 'filesexplorerview.ts'; + + let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + assert.equal(res[0], resourceB); + + res = [resourceA, resourceC, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); + assert.equal(res[0], resourceB); + }); + + test('prepareSearchForScoring', function () { + assert.equal(scorer.prepareQuery(' f*a ').value, 'fa'); + assert.equal(scorer.prepareQuery('model Tester.ts').value, 'modelTester.ts'); + assert.equal(scorer.prepareQuery('Model Tester.ts').lowercase, 'modeltester.ts'); + assert.equal(scorer.prepareQuery('ModelTester.ts').containsPathSeparator, false); + assert.equal(scorer.prepareQuery('Model' + nativeSep + 'Tester.ts').containsPathSeparator, true); }); }); \ No newline at end of file diff --git a/src/vs/base/test/common/map.test.ts b/src/vs/base/test/common/map.test.ts index e53c60a028b..9eff83860da 100644 --- a/src/vs/base/test/common/map.test.ts +++ b/src/vs/base/test/common/map.test.ts @@ -319,8 +319,7 @@ suite('Map', () => { map.forEach((value, key) => { assert.equal(trie.get(key), value); }); - trie.forEach(entry => { - const [key, element] = entry; + trie.forEach((element, key) => { assert.equal(element, map.get(key)); map.delete(key); }); diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 7b1efdfba4f..57788640abe 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -5,7 +5,7 @@ 'use strict'; -import { app, ipcMain as ipc, BrowserWindow } from 'electron'; +import { app, ipcMain as ipc, BrowserWindow, dialog } from 'electron'; import * as platform from 'vs/base/common/platform'; import { WindowsManager } from 'vs/code/electron-main/windows'; import { IWindowsService, OpenContext } from 'vs/platform/windows/common/windows'; @@ -76,7 +76,7 @@ export class CodeApplication { @ILogService private logService: ILogService, @IEnvironmentService private environmentService: IEnvironmentService, @ILifecycleService private lifecycleService: ILifecycleService, - @IConfigurationService private configurationService: ConfigurationService, + @IConfigurationService private configurationService: ConfigurationService, @IStorageService private storageService: IStorageService, @IHistoryMainService private historyService: IHistoryMainService ) { @@ -379,7 +379,23 @@ export class CodeApplication { windowsMutex = new Mutex(product.win32MutexName); this.toDispose.push({ dispose: () => windowsMutex.release() }); } catch (e) { - // noop + if (!this.environmentService.isBuilt) { + dialog.showMessageBox({ + message: 'Failed to load windows-mutex', + detail: e.toString() + }); + } + } + + try { + require.__$__nodeRequire('windows-foreground-love'); + } catch (e) { + if (!this.environmentService.isBuilt) { + dialog.showMessageBox({ + message: 'Failed to load windows-foreground-love', + detail: e.toString() + }); + } } } diff --git a/src/vs/code/electron-main/menus.ts b/src/vs/code/electron-main/menus.ts index 327dcba23b3..69d49d836fd 100644 --- a/src/vs/code/electron-main/menus.ts +++ b/src/vs/code/electron-main/menus.ts @@ -11,8 +11,8 @@ 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, 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 { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; +import { AutoSaveConfiguration } from 'vs/platform/files/common/files'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IUpdateService, State as UpdateState } from 'vs/platform/update/common/update'; import product from 'vs/platform/node/product'; @@ -29,27 +29,6 @@ interface IExtensionViewlet { label: string; } -interface IConfiguration extends IFilesConfiguration { - window: { - enableMenuBarMnemonics: boolean; - nativeTabs: boolean; - }; - workbench: { - sideBar: { - location: 'left' | 'right'; - }, - statusBar: { - visible: boolean; - }, - activityBar: { - visible: boolean; - } - }; - editor: { - multiCursorModifier: 'ctrlCmd' | 'alt' - }; -} - interface IMenuItemClickHandler { inDevTools: (contents: Electron.WebContents) => void; inNoWindow: () => void; @@ -61,13 +40,15 @@ export class CodeMenu { private static MAX_MENU_RECENT_ENTRIES = 10; - private currentAutoSaveSetting: string; - private currentMultiCursorModifierSetting: string; - private currentSidebarLocation: 'left' | 'right'; - private currentStatusbarVisible: boolean; - private currentActivityBarVisible: boolean; - private currentEnableMenuBarMnemonics: boolean; - private currentEnableNativeTabs: boolean; + private keys = [ + 'files.autoSave', + 'editor.multiCursorModifier', + 'workbench.sideBar.location', + 'workbench.statusBar.visible', + 'workbench.activityBar.visible', + 'window.enableMenuBarMnemonics', + 'window.nativeTabs' + ]; private isQuitting: boolean; private appMenuInstalled: boolean; @@ -99,8 +80,6 @@ export class CodeMenu { this.menuUpdater = new RunOnceScheduler(() => this.doUpdateMenu(), 0); this.keybindingsResolver = instantiationService.createInstance(KeybindingsResolver); - this.onConfigurationUpdated(this.configurationService.getConfiguration()); - this.install(); this.registerListeners(); @@ -136,7 +115,7 @@ export class CodeMenu { }); // Update when auto save config changes - this.configurationService.onDidUpdateConfiguration(e => this.onConfigurationUpdated(this.configurationService.getConfiguration(), true /* update menu if changed */)); + this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated(e)); // Listen to update service this.updateService.onStateChange(() => this.updateMenu()); @@ -145,67 +124,56 @@ export class CodeMenu { this.keybindingsResolver.onKeybindingsChanged(() => this.updateMenu()); } - private onConfigurationUpdated(config: IConfiguration, handleMenu?: boolean): void { - let updateMenu = false; - const newAutoSaveSetting = config && config.files && config.files.autoSave; - if (newAutoSaveSetting !== this.currentAutoSaveSetting) { - this.currentAutoSaveSetting = newAutoSaveSetting; - updateMenu = true; - } - - const newMultiCursorModifierSetting = config && config.editor && config.editor.multiCursorModifier; - if (newMultiCursorModifierSetting !== this.currentMultiCursorModifierSetting) { - this.currentMultiCursorModifierSetting = newMultiCursorModifierSetting; - updateMenu = true; - } - - const newSidebarLocation = config && config.workbench && config.workbench.sideBar && config.workbench.sideBar.location || 'left'; - if (newSidebarLocation !== this.currentSidebarLocation) { - this.currentSidebarLocation = newSidebarLocation; - updateMenu = true; - } - - let newStatusbarVisible = config && config.workbench && config.workbench.statusBar && config.workbench.statusBar.visible; - if (typeof newStatusbarVisible !== 'boolean') { - newStatusbarVisible = true; - } - if (newStatusbarVisible !== this.currentStatusbarVisible) { - this.currentStatusbarVisible = newStatusbarVisible; - updateMenu = true; - } - - let newActivityBarVisible = config && config.workbench && config.workbench.activityBar && config.workbench.activityBar.visible; - if (typeof newActivityBarVisible !== 'boolean') { - newActivityBarVisible = true; - } - if (newActivityBarVisible !== this.currentActivityBarVisible) { - this.currentActivityBarVisible = newActivityBarVisible; - updateMenu = true; - } - - let newEnableMenuBarMnemonics = config && config.window && config.window.enableMenuBarMnemonics; - if (typeof newEnableMenuBarMnemonics !== 'boolean') { - newEnableMenuBarMnemonics = true; - } - if (newEnableMenuBarMnemonics !== this.currentEnableMenuBarMnemonics) { - this.currentEnableMenuBarMnemonics = newEnableMenuBarMnemonics; - updateMenu = true; - } - - let newEnableNativeTabs = config && config.window && config.window.nativeTabs; - if (typeof newEnableNativeTabs !== 'boolean') { - newEnableNativeTabs = false; - } - if (newEnableNativeTabs !== this.currentEnableNativeTabs) { - this.currentEnableNativeTabs = newEnableNativeTabs; - updateMenu = true; - } - - if (handleMenu && updateMenu) { + private onConfigurationUpdated(event: IConfigurationChangeEvent): void { + if (this.keys.some(key => event.affectsConfiguration(key))) { this.updateMenu(); } } + private get currentAutoSaveSetting(): string { + return this.configurationService.getValue('files.autoSave'); + } + + private get currentMultiCursorModifierSetting(): string { + return this.configurationService.getValue('editor.multiCursorModifier'); + } + + private get currentSidebarLocation(): string { + return this.configurationService.getValue('workbench.sideBar.location') || 'left'; + } + + private get currentStatusbarVisible(): boolean { + let statusbarVisible = this.configurationService.getValue('workbench.statusBar.visible'); + if (typeof statusbarVisible !== 'boolean') { + statusbarVisible = true; + } + return statusbarVisible; + } + + private get currentActivityBarVisible(): boolean { + let activityBarVisible = this.configurationService.getValue('workbench.activityBar.visible'); + if (typeof activityBarVisible !== 'boolean') { + activityBarVisible = true; + } + return activityBarVisible; + } + + private get currentEnableMenuBarMnemonics(): boolean { + let enableMenuBarMnemonics = this.configurationService.getValue('window.enableMenuBarMnemonics'); + if (typeof enableMenuBarMnemonics !== 'boolean') { + enableMenuBarMnemonics = true; + } + return enableMenuBarMnemonics; + } + + private get currentEnableNativeTabs(): boolean { + let enableNativeTabs = this.configurationService.getValue('window.nativeTabs'); + if (typeof enableNativeTabs !== 'boolean') { + enableNativeTabs = false; + } + return enableNativeTabs; + } + private updateMenu(): void { this.menuUpdater.schedule(); // buffer multiple attempts to update the menu } @@ -253,7 +221,7 @@ export class CodeMenu { this.closeWorkspace.visible = isInWorkspaceContext; this.closeFolder.visible = !isInWorkspaceContext; - this.closeFolder.enabled = isInFolderContext; + this.closeFolder.enabled = isInFolderContext || isLinux /* https://github.com/Microsoft/vscode/issues/36431 */; } private install(): void { @@ -389,7 +357,7 @@ export class CodeMenu { } const open = new MenuItem(this.likeAction('workbench.action.files.openFileFolder', { label: this.mnemonicLabel(nls.localize({ key: 'miOpen', comment: ['&& denotes a mnemonic'] }, "&&Open...")), click: (menuItem, win, event) => this.windowsService.pickFileFolderAndOpen({ forceNewWindow: this.isOptionClick(event), telemetryExtraData: { from: telemetryFrom } }) })); - const openWorkspace = new MenuItem(this.likeAction('workbench.action.openWorkspace', { label: this.mnemonicLabel(nls.localize({ key: 'miOpenWorkspace', comment: ['&& denotes a mnemonic'] }, "&&Open Workspace...")), click: () => this.windowsService.openWorkspace() })); + const openWorkspace = new MenuItem(this.likeAction('workbench.action.openWorkspace', { label: this.mnemonicLabel(nls.localize({ key: 'miOpenWorkspace', comment: ['&& denotes a mnemonic'] }, "&&Open Workspace...")), click: (menuItem, win, event) => this.windowsService.openWorkspace(void 0, { forceNewWindow: this.isOptionClick(event) }) })); const openFolder = new MenuItem(this.likeAction('workbench.action.files.openFolder', { label: this.mnemonicLabel(nls.localize({ key: 'miOpenFolder', comment: ['&& denotes a mnemonic'] }, "Open &&Folder...")), click: (menuItem, win, event) => this.windowsService.pickFolderAndOpen({ forceNewWindow: this.isOptionClick(event), telemetryExtraData: { from: telemetryFrom } }) })); let openFile: Electron.MenuItem; diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index 50570fd57b9..e1999859678 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -409,7 +409,7 @@ export class CodeWindow implements ICodeWindow { } // Handle configuration changes - this.toDispose.push(this.configurationService.onDidUpdateConfiguration(e => this.onConfigurationUpdated())); + this.toDispose.push(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated())); // Handle Workspace events this.toDispose.push(this.workspaceService.onUntitledWorkspaceDeleted(e => this.onUntitledWorkspaceDeleted(e))); diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 0698760579e..436856c01d5 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -1339,8 +1339,8 @@ export class WindowsManager implements IWindowsMainService { return result; } - public openWorkspace(win?: CodeWindow): void { - this.workspacesManager.openWorkspace(win); + public openWorkspace(win?: CodeWindow, options?: { forceNewWindow?: boolean }): void { + this.workspacesManager.openWorkspace(win, options); } private onBeforeWindowUnload(e: IWindowUnloadEvent): void { @@ -1618,7 +1618,7 @@ class FileDialog { }); } - public getFileOrFolderPaths(options: IInternalNativeOpenDialogOptions, clb: (paths: string[]) => void): void { + private getFileOrFolderPaths(options: IInternalNativeOpenDialogOptions, clb: (paths: string[]) => void): void { // Ensure dialog options if (!options.dialogOptions) { @@ -1755,7 +1755,7 @@ class WorkspacesManager { }); } - public openWorkspace(window = this.windowsMainService.getLastActiveWindow()): void { + public openWorkspace(window = this.windowsMainService.getLastActiveWindow(), options?: { forceNewWindow?: boolean }): void { let defaultPath: string; if (window && window.openedWorkspace && !this.workspacesService.isUntitledWorkspace(window.openedWorkspace)) { defaultPath = dirname(window.openedWorkspace.configPath); @@ -1771,7 +1771,8 @@ class WorkspacesManager { filters: WORKSPACE_FILTER, properties: ['openFile'], defaultPath - } + }, + forceNewWindow: options && options.forceNewWindow }); } diff --git a/src/vs/editor/browser/controller/mouseTarget.ts b/src/vs/editor/browser/controller/mouseTarget.ts index 4290a200487..bc8982232d3 100644 --- a/src/vs/editor/browser/controller/mouseTarget.ts +++ b/src/vs/editor/browser/controller/mouseTarget.ts @@ -25,6 +25,13 @@ export interface IViewZoneData { afterLineNumber: number; } +export interface IMarginData { + isAfterLines: boolean; + glyphMarginWidth: number; + lineNumbersWidth: number; + offsetX: number; +} + interface IETextRange { boundingHeight: number; boundingLeft: number; @@ -565,22 +572,28 @@ export class MouseTargetFactory { if (request.isInMarginArea) { let res = ctx.getFullLineRangeAtCoord(request.mouseVerticalOffset); let pos = res.range.getStartPosition(); - let offset = Math.abs(request.pos.x - request.editorPos.x); + const detail: IMarginData = { + isAfterLines: res.isAfterLines, + glyphMarginWidth: ctx.layoutInfo.glyphMarginWidth, + lineNumbersWidth: ctx.layoutInfo.lineNumbersWidth, + offsetX: offset + }; + if (offset <= ctx.layoutInfo.glyphMarginWidth) { // On the glyph margin - return request.fulfill(MouseTargetType.GUTTER_GLYPH_MARGIN, pos, res.range, res.isAfterLines); + return request.fulfill(MouseTargetType.GUTTER_GLYPH_MARGIN, pos, res.range, detail); } offset -= ctx.layoutInfo.glyphMarginWidth; if (offset <= ctx.layoutInfo.lineNumbersWidth) { // On the line numbers - return request.fulfill(MouseTargetType.GUTTER_LINE_NUMBERS, pos, res.range, res.isAfterLines); + return request.fulfill(MouseTargetType.GUTTER_LINE_NUMBERS, pos, res.range, detail); } offset -= ctx.layoutInfo.lineNumbersWidth; // On the line decorations - return request.fulfill(MouseTargetType.GUTTER_LINE_DECORATIONS, pos, res.range, res.isAfterLines); + return request.fulfill(MouseTargetType.GUTTER_LINE_DECORATIONS, pos, res.range, detail); } return null; } diff --git a/src/vs/editor/browser/viewParts/decorations/decorations.ts b/src/vs/editor/browser/viewParts/decorations/decorations.ts index 2cc107d4037..dc63ec06edc 100644 --- a/src/vs/editor/browser/viewParts/decorations/decorations.ts +++ b/src/vs/editor/browser/viewParts/decorations/decorations.ts @@ -78,15 +78,15 @@ export class DecorationsOverlay extends DynamicViewOverlay { let decorations: ViewModelDecoration[] = [], decorationsLen = 0; for (let i = 0, len = _decorations.length; i < len; i++) { let d = _decorations[i]; - if (d.source.options.className) { + if (d.options.className) { decorations[decorationsLen++] = d; } } // Sort decorations for consistent render output decorations = decorations.sort((a, b) => { - let aClassName = a.source.options.className; - let bClassName = b.source.options.className; + let aClassName = a.options.className; + let bClassName = b.options.className; if (aClassName < bClassName) { return -1; @@ -120,13 +120,13 @@ export class DecorationsOverlay extends DynamicViewOverlay { for (let i = 0, lenI = decorations.length; i < lenI; i++) { let d = decorations[i]; - if (!d.source.options.isWholeLine) { + if (!d.options.isWholeLine) { continue; } let decorationOutput = ( '
' @@ -148,12 +148,12 @@ export class DecorationsOverlay extends DynamicViewOverlay { for (let i = 0, lenI = decorations.length; i < lenI; i++) { const d = decorations[i]; - if (d.source.options.isWholeLine) { + if (d.options.isWholeLine) { continue; } - const className = d.source.options.className; - const showIfCollapsed = d.source.options.showIfCollapsed; + const className = d.options.className; + const showIfCollapsed = d.options.showIfCollapsed; let range = d.range; if (showIfCollapsed && range.endColumn === 1 && range.endLineNumber !== range.startLineNumber) { diff --git a/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.ts b/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.ts index 33874c27b51..757305ebc08 100644 --- a/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.ts +++ b/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.ts @@ -145,7 +145,7 @@ export class GlyphMarginOverlay extends DedupOverlay { let r: DecorationToRender[] = [], rLen = 0; for (let i = 0, len = decorations.length; i < len; i++) { let d = decorations[i]; - let glyphMarginClassName = d.source.options.glyphMarginClassName; + let glyphMarginClassName = d.options.glyphMarginClassName; if (glyphMarginClassName) { r[rLen++] = new DecorationToRender(d.range.startLineNumber, d.range.endLineNumber, glyphMarginClassName); } diff --git a/src/vs/editor/browser/viewParts/linesDecorations/linesDecorations.ts b/src/vs/editor/browser/viewParts/linesDecorations/linesDecorations.ts index 3a143124040..43767788096 100644 --- a/src/vs/editor/browser/viewParts/linesDecorations/linesDecorations.ts +++ b/src/vs/editor/browser/viewParts/linesDecorations/linesDecorations.ts @@ -73,7 +73,7 @@ export class LinesDecorationsOverlay extends DedupOverlay { let r: DecorationToRender[] = [], rLen = 0; for (let i = 0, len = decorations.length; i < len; i++) { let d = decorations[i]; - let linesDecorationsClassName = d.source.options.linesDecorationsClassName; + let linesDecorationsClassName = d.options.linesDecorationsClassName; if (linesDecorationsClassName) { r[rLen++] = new DecorationToRender(d.range.startLineNumber, d.range.endLineNumber, linesDecorationsClassName); } diff --git a/src/vs/editor/browser/viewParts/marginDecorations/marginDecorations.ts b/src/vs/editor/browser/viewParts/marginDecorations/marginDecorations.ts index b83c9faa123..75ff0a0e3ed 100644 --- a/src/vs/editor/browser/viewParts/marginDecorations/marginDecorations.ts +++ b/src/vs/editor/browser/viewParts/marginDecorations/marginDecorations.ts @@ -63,7 +63,7 @@ export class MarginViewLineDecorationsOverlay extends DedupOverlay { let r: DecorationToRender[] = [], rLen = 0; for (let i = 0, len = decorations.length; i < len; i++) { let d = decorations[i]; - let marginClassName = d.source.options.marginClassName; + let marginClassName = d.options.marginClassName; if (marginClassName) { r[rLen++] = new DecorationToRender(d.range.startLineNumber, d.range.endLineNumber, marginClassName); } diff --git a/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts b/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts index 91ebeaf3945..79e46980347 100644 --- a/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts +++ b/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts @@ -6,262 +6,416 @@ import * as editorCommon from 'vs/editor/common/editorCommon'; import { ViewPart } from 'vs/editor/browser/view/viewPart'; -import { OverviewRulerImpl } from 'vs/editor/browser/viewParts/overviewRuler/overviewRulerImpl'; import { ViewContext } from 'vs/editor/common/view/viewContext'; import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext'; import { Position } from 'vs/editor/common/core/position'; import { TokenizationRegistry } from 'vs/editor/common/modes'; import { IDisposable } from 'vs/base/common/lifecycle'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; -import { OverviewRulerZone } from 'vs/editor/common/view/overviewZoneManager'; import { editorOverviewRulerBorder, editorCursorForeground } from 'vs/editor/common/view/editorColorRegistry'; import { Color } from 'vs/base/common/color'; -import { ThemeColor } from 'vs/platform/theme/common/themeService'; +import { ITheme } from 'vs/platform/theme/common/themeService'; +import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; + +class Settings { + + public readonly lineHeight: number; + public readonly pixelRatio: number; + public readonly overviewRulerLanes: number; + + public readonly renderBorder: boolean; + public readonly borderColor: string; + + public readonly hideCursor: boolean; + public readonly cursorColor: string; + + public readonly themeType: 'light' | 'dark' | 'hc'; + public readonly backgroundColor: string; + + public readonly top: number; + public readonly right: number; + public readonly domWidth: number; + public readonly domHeight: number; + public readonly canvasWidth: number; + public readonly canvasHeight: number; + + public readonly x: number[]; + public readonly w: number[]; + + constructor(config: editorCommon.IConfiguration, theme: ITheme) { + this.lineHeight = config.editor.lineHeight; + this.pixelRatio = config.editor.pixelRatio; + this.overviewRulerLanes = config.editor.viewInfo.overviewRulerLanes; + + this.renderBorder = config.editor.viewInfo.overviewRulerBorder; + const borderColor = theme.getColor(editorOverviewRulerBorder); + this.borderColor = borderColor ? borderColor.toString() : null; + + this.hideCursor = config.editor.viewInfo.hideCursorInOverviewRuler; + const cursorColor = theme.getColor(editorCursorForeground); + this.cursorColor = cursorColor ? cursorColor.transparent(0.7).toString() : null; + + this.themeType = theme.type; + + const minimapEnabled = config.editor.viewInfo.minimap.enabled; + const backgroundColor = (minimapEnabled ? TokenizationRegistry.getDefaultBackground() : null); + this.backgroundColor = (backgroundColor ? Color.Format.CSS.formatHex(backgroundColor) : null); + + const position = config.editor.layoutInfo.overviewRuler; + this.top = position.top; + this.right = position.right; + this.domWidth = position.width; + this.domHeight = position.height; + this.canvasWidth = (this.domWidth * this.pixelRatio) | 0; + this.canvasHeight = (this.domHeight * this.pixelRatio) | 0; + + const [x, w] = this._initLanes(1, this.canvasWidth, this.overviewRulerLanes); + this.x = x; + this.w = w; + } + + private _initLanes(canvasLeftOffset: number, canvasWidth: number, laneCount: number): [number[], number[]] { + const remainingWidth = canvasWidth - canvasLeftOffset; + + if (laneCount >= 3) { + const leftWidth = Math.floor(remainingWidth / 3); + const rightWidth = Math.floor(remainingWidth / 3); + const centerWidth = remainingWidth - leftWidth - rightWidth; + const leftOffset = canvasLeftOffset; + const centerOffset = leftOffset + leftWidth; + const rightOffset = leftOffset + leftWidth + centerWidth; + + return [ + [ + 0, + leftOffset, // Left + centerOffset, // Center + leftOffset, // Left | Center + rightOffset, // Right + leftOffset, // Left | Right + centerOffset, // Center | Right + leftOffset, // Left | Center | Right + ], [ + 0, + leftWidth, // Left + centerWidth, // Center + leftWidth + centerWidth, // Left | Center + rightWidth, // Right + leftWidth + centerWidth + rightWidth, // Left | Right + centerWidth + rightWidth, // Center | Right + leftWidth + centerWidth + rightWidth, // Left | Center | Right + ] + ]; + } else if (laneCount === 2) { + const leftWidth = Math.floor(remainingWidth / 2); + const rightWidth = remainingWidth - leftWidth; + const leftOffset = canvasLeftOffset; + const rightOffset = leftOffset + leftWidth; + + return [ + [ + 0, + leftOffset, // Left + leftOffset, // Center + leftOffset, // Left | Center + rightOffset, // Right + leftOffset, // Left | Right + leftOffset, // Center | Right + leftOffset, // Left | Center | Right + ], [ + 0, + leftWidth, // Left + leftWidth, // Center + leftWidth, // Left | Center + rightWidth, // Right + leftWidth + rightWidth, // Left | Right + leftWidth + rightWidth, // Center | Right + leftWidth + rightWidth, // Left | Center | Right + ] + ]; + } else { + const offset = canvasLeftOffset; + const width = remainingWidth; + + return [ + [ + 0, + offset, // Left + offset, // Center + offset, // Left | Center + offset, // Right + offset, // Left | Right + offset, // Center | Right + offset, // Left | Center | Right + ], [ + 0, + width, // Left + width, // Center + width, // Left | Center + width, // Right + width, // Left | Right + width, // Center | Right + width, // Left | Center | Right + ] + ]; + } + } + + public equals(other: Settings): boolean { + return ( + this.lineHeight === other.lineHeight + && this.pixelRatio === other.pixelRatio + && this.overviewRulerLanes === other.overviewRulerLanes + && this.renderBorder === other.renderBorder + && this.borderColor === other.borderColor + && this.hideCursor === other.hideCursor + && this.cursorColor === other.cursorColor + && this.themeType === other.themeType + && this.backgroundColor === other.backgroundColor + && this.top === other.top + && this.right === other.right + && this.domWidth === other.domWidth + && this.domHeight === other.domHeight + && this.canvasWidth === other.canvasWidth + && this.canvasHeight === other.canvasHeight + ); + } +} + +const enum Constants { + MIN_DECORATION_HEIGHT = 6 +} + +const enum OverviewRulerLane { + Left = 1, + Center = 2, + Right = 4, + Full = 7 +} export class DecorationsOverviewRuler extends ViewPart { - static MIN_DECORATION_HEIGHT = 6; - static MAX_DECORATION_HEIGHT = 60; - private readonly _tokensColorTrackerListener: IDisposable; - - private _overviewRuler: OverviewRulerImpl; - - private _renderBorder: boolean; - private _borderColor: string; - private _cursorColor: string; - - private _shouldUpdateDecorations: boolean; - private _shouldUpdateCursorPosition: boolean; - - private _hideCursor: boolean; + private readonly _domNode: FastDomNode; + private _settings: Settings; private _cursorPositions: Position[]; - private _zonesFromDecorations: OverviewRulerZone[]; - private _zonesFromCursors: OverviewRulerZone[]; - constructor(context: ViewContext) { super(context); - this._overviewRuler = new OverviewRulerImpl( - 1, - 'decorationsOverviewRuler', - this._context.viewLayout.getScrollHeight(), - this._context.configuration.editor.lineHeight, - this._context.configuration.editor.pixelRatio, - DecorationsOverviewRuler.MIN_DECORATION_HEIGHT, - DecorationsOverviewRuler.MAX_DECORATION_HEIGHT, - (lineNumber: number) => this._context.viewLayout.getVerticalOffsetForLineNumber(lineNumber) - ); - this._overviewRuler.setLanesCount(this._context.configuration.editor.viewInfo.overviewRulerLanes, false); - this._overviewRuler.setLayout(this._context.configuration.editor.layoutInfo.overviewRuler, false); - this._renderBorder = this._context.configuration.editor.viewInfo.overviewRulerBorder; + this._domNode = createFastDomNode(document.createElement('canvas')); + this._domNode.setClassName('decorationsOverviewRuler'); + this._domNode.setPosition('absolute'); + this._domNode.setLayerHinting(true); - this._updateColors(); + this._settings = null; + this._updateSettings(false); - this._updateBackground(false); this._tokensColorTrackerListener = TokenizationRegistry.onDidChange((e) => { if (e.changedColorMap) { - this._updateBackground(true); + this._updateSettings(true); } }); - this._shouldUpdateDecorations = true; - this._zonesFromDecorations = []; - - this._shouldUpdateCursorPosition = true; - this._hideCursor = this._context.configuration.editor.viewInfo.hideCursorInOverviewRuler; - - this._zonesFromCursors = []; this._cursorPositions = []; } public dispose(): void { super.dispose(); - this._overviewRuler.dispose(); this._tokensColorTrackerListener.dispose(); } - private _updateBackground(render: boolean): void { - const minimapEnabled = this._context.configuration.editor.viewInfo.minimap.enabled; - this._overviewRuler.setUseBackground((minimapEnabled ? TokenizationRegistry.getDefaultBackground() : null), render); - } - - // ---- begin view event handlers - - public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { - let prevLanesCount = this._overviewRuler.getLanesCount(); - let newLanesCount = this._context.configuration.editor.viewInfo.overviewRulerLanes; - - if (prevLanesCount !== newLanesCount) { - this._overviewRuler.setLanesCount(newLanesCount, false); + private _updateSettings(renderNow: boolean): boolean { + const newSettings = new Settings(this._context.configuration, this._context.theme); + if (this._settings !== null && this._settings.equals(newSettings)) { + // nothing to do + return false; } - if (e.lineHeight) { - this._overviewRuler.setLineHeight(this._context.configuration.editor.lineHeight, false); - } + this._settings = newSettings; - if (e.pixelRatio) { - this._overviewRuler.setPixelRatio(this._context.configuration.editor.pixelRatio, false); - } + this._domNode.setTop(this._settings.top); + this._domNode.setRight(this._settings.right); + this._domNode.setWidth(this._settings.domWidth); + this._domNode.setHeight(this._settings.domHeight); + this._domNode.domNode.width = this._settings.canvasWidth; + this._domNode.domNode.height = this._settings.canvasHeight; - if (e.viewInfo) { - this._renderBorder = this._context.configuration.editor.viewInfo.overviewRulerBorder; - this._hideCursor = this._context.configuration.editor.viewInfo.hideCursorInOverviewRuler; - this._shouldUpdateCursorPosition = true; - this._updateBackground(false); - } - - if (e.layoutInfo) { - this._overviewRuler.setLayout(this._context.configuration.editor.layoutInfo.overviewRuler, false); + if (renderNow) { + this._render(); } return true; } + // ---- begin view event handlers + + public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { + return this._updateSettings(false); + } public onCursorStateChanged(e: viewEvents.ViewCursorStateChangedEvent): boolean { - this._shouldUpdateCursorPosition = true; this._cursorPositions = []; for (let i = 0, len = e.selections.length; i < len; i++) { this._cursorPositions[i] = e.selections[i].getPosition(); } return true; } - public onDecorationsChanged(e: viewEvents.ViewDecorationsChangedEvent): boolean { - this._shouldUpdateDecorations = true; return true; } - public onFlushed(e: viewEvents.ViewFlushedEvent): boolean { - this._shouldUpdateCursorPosition = true; - this._shouldUpdateDecorations = true; return true; } - public onScrollChanged(e: viewEvents.ViewScrollChangedEvent): boolean { - this._overviewRuler.setScrollHeight(e.scrollHeight, false); - return super.onScrollChanged(e) || e.scrollHeightChanged; + return e.scrollHeightChanged; } - public onZonesChanged(e: viewEvents.ViewZonesChangedEvent): boolean { return true; } - public onThemeChanged(e: viewEvents.ViewThemeChangedEvent): boolean { - this._updateColors(); - this._shouldUpdateDecorations = true; - this._shouldUpdateCursorPosition = true; - return true; + // invalidate color cache + this._context.model.invalidateOverviewRulerColorCache(); + return this._updateSettings(false); } // ---- end view event handlers public getDomNode(): HTMLElement { - return this._overviewRuler.getDomNode(); - } - - private _updateColors() { - let borderColor = this._context.theme.getColor(editorOverviewRulerBorder); - this._borderColor = borderColor ? borderColor.toString() : null; - - let cursorColor = this._context.theme.getColor(editorCursorForeground); - this._cursorColor = cursorColor ? cursorColor.transparent(0.7).toString() : null; - - this._overviewRuler.setThemeType(this._context.theme.type, false); - } - - private _createZonesFromDecorations(): OverviewRulerZone[] { - let decorations = this._context.model.getAllOverviewRulerDecorations(); - let zones: OverviewRulerZone[] = []; - - for (let i = 0, len = decorations.length; i < len; i++) { - let dec = decorations[i]; - let overviewRuler = dec.source.options.overviewRuler; - zones[i] = new OverviewRulerZone( - dec.range.startLineNumber, - dec.range.endLineNumber, - overviewRuler.position, - 0, - this.resolveRulerColor(overviewRuler.color), - this.resolveRulerColor(overviewRuler.darkColor), - this.resolveRulerColor(overviewRuler.hcColor) - ); - } - - return zones; - } - - private resolveRulerColor(color: string | ThemeColor): string { - if (editorCommon.isThemeColor(color)) { - let c = this._context.theme.getColor(color.id) || Color.transparent; - return c.toString(); - } - return color; - } - - private _createZonesFromCursors(): OverviewRulerZone[] { - let zones: OverviewRulerZone[] = []; - - for (let i = 0, len = this._cursorPositions.length; i < len; i++) { - let cursor = this._cursorPositions[i]; - - zones[i] = new OverviewRulerZone( - cursor.lineNumber, - cursor.lineNumber, - editorCommon.OverviewRulerLane.Full, - 2, - this._cursorColor, - this._cursorColor, - this._cursorColor - ); - } - - return zones; + return this._domNode.domNode; } public prepareRender(ctx: RenderingContext): void { // Nothing to read } - public render(ctx: RestrictedRenderingContext): void { - if (this._shouldUpdateDecorations || this._shouldUpdateCursorPosition) { + public render(editorCtx: RestrictedRenderingContext): void { + this._render(); + } - if (this._shouldUpdateDecorations) { - this._shouldUpdateDecorations = false; - this._zonesFromDecorations = this._createZonesFromDecorations(); - } + private _render(): void { + const canvasWidth = this._settings.canvasWidth; + const canvasHeight = this._settings.canvasHeight; + const lineHeight = this._settings.lineHeight; + const viewLayout = this._context.viewLayout; + const outerHeight = this._context.viewLayout.getScrollHeight(); + const heightRatio = canvasHeight / outerHeight; + const decorations = this._context.model.getAllOverviewRulerDecorations(this._context.theme); - if (this._shouldUpdateCursorPosition) { - this._shouldUpdateCursorPosition = false; - if (this._hideCursor) { - this._zonesFromCursors = []; - } else { - this._zonesFromCursors = this._createZonesFromCursors(); - } - } + const minDecorationHeight = (Constants.MIN_DECORATION_HEIGHT * this._settings.pixelRatio) | 0; + const halfMinDecorationHeight = (minDecorationHeight / 2) | 0; - let allZones: OverviewRulerZone[] = []; - allZones = allZones.concat(this._zonesFromCursors); - allZones = allZones.concat(this._zonesFromDecorations); - - this._overviewRuler.setZones(allZones, false); + const canvasCtx = this._domNode.domNode.getContext('2d'); + if (this._settings.backgroundColor === null) { + canvasCtx.clearRect(0, 0, canvasWidth, canvasHeight); + } else { + canvasCtx.fillStyle = this._settings.backgroundColor; + canvasCtx.fillRect(0, 0, canvasWidth, canvasHeight); } - let hasRendered = this._overviewRuler.render(false); + const x = this._settings.x; + const w = this._settings.w; + // Avoid flickering by always rendering the colors in the same order + // colors that don't use transparency will be sorted last (they start with #) + const colors = Object.keys(decorations); + colors.sort(); + for (let cIndex = 0, cLen = colors.length; cIndex < cLen; cIndex++) { + const color = colors[cIndex]; - if (hasRendered && this._renderBorder && this._borderColor && this._overviewRuler.getLanesCount() > 0 && (this._zonesFromDecorations.length > 0 || this._zonesFromCursors.length > 0)) { - let ctx2 = this._overviewRuler.getDomNode().getContext('2d'); - ctx2.beginPath(); - ctx2.lineWidth = 1; - ctx2.strokeStyle = this._borderColor; - ctx2.moveTo(0, 0); - ctx2.lineTo(0, this._overviewRuler.getPixelHeight()); - ctx2.stroke(); + const colorDecorations = decorations[color]; - ctx2.moveTo(0, 0); - ctx2.lineTo(this._overviewRuler.getPixelWidth(), 0); - ctx2.stroke(); + canvasCtx.fillStyle = color; + + let prevLane = 0; + let prevY1 = 0; + let prevY2 = 0; + for (let i = 0, len = colorDecorations.length; i < len; i++) { + const lane = colorDecorations[3 * i]; + const startLineNumber = colorDecorations[3 * i + 1]; + const endLineNumber = colorDecorations[3 * i + 2]; + + let y1 = (viewLayout.getVerticalOffsetForLineNumber(startLineNumber) * heightRatio) | 0; + let y2 = ((viewLayout.getVerticalOffsetForLineNumber(endLineNumber) + lineHeight) * heightRatio) | 0; + let height = y2 - y1; + if (height < minDecorationHeight) { + let yCenter = ((y1 + y2) / 2) | 0; + if (yCenter < halfMinDecorationHeight) { + yCenter = halfMinDecorationHeight; + } else if (yCenter + halfMinDecorationHeight > canvasHeight) { + yCenter = canvasHeight - halfMinDecorationHeight; + } + y1 = yCenter - halfMinDecorationHeight; + y2 = yCenter + halfMinDecorationHeight; + } + + if (y1 > prevY2 + 1 || lane !== prevLane) { + // flush prev + if (i !== 0) { + canvasCtx.fillRect(x[prevLane], prevY1, w[prevLane], prevY2 - prevY1); + } + prevLane = lane; + prevY1 = y1; + prevY2 = y2; + } else { + // merge into prev + if (y2 > prevY2) { + prevY2 = y2; + } + } + } + canvasCtx.fillRect(x[prevLane], prevY1, w[prevLane], prevY2 - prevY1); + } + + // Draw cursors + if (!this._settings.hideCursor) { + const cursorHeight = (2 * this._settings.pixelRatio) | 0; + const halfCursorHeight = (cursorHeight / 2) | 0; + const cursorX = this._settings.x[OverviewRulerLane.Full]; + const cursorW = this._settings.w[OverviewRulerLane.Full]; + canvasCtx.fillStyle = this._settings.cursorColor; + + let prevY1 = -100; + let prevY2 = -100; + for (let i = 0, len = this._cursorPositions.length; i < len; i++) { + const cursor = this._cursorPositions[i]; + + let yCenter = (viewLayout.getVerticalOffsetForLineNumber(cursor.lineNumber) * heightRatio) | 0; + if (yCenter < halfCursorHeight) { + yCenter = halfCursorHeight; + } else if (yCenter + halfCursorHeight > canvasHeight) { + yCenter = canvasHeight - halfCursorHeight; + } + const y1 = yCenter - halfCursorHeight; + const y2 = y1 + cursorHeight; + + if (y1 > prevY2 + 1) { + // flush prev + if (i !== 0) { + canvasCtx.fillRect(cursorX, prevY1, cursorW, prevY2 - prevY1); + } + prevY1 = y1; + prevY2 = y2; + } else { + // merge into prev + if (y2 > prevY2) { + prevY2 = y2; + } + } + } + canvasCtx.fillRect(cursorX, prevY1, cursorW, prevY2 - prevY1); + } + + if (this._settings.renderBorder && this._settings.borderColor && this._settings.overviewRulerLanes > 0) { + canvasCtx.beginPath(); + canvasCtx.lineWidth = 1; + canvasCtx.strokeStyle = this._settings.borderColor; + canvasCtx.moveTo(0, 0); + canvasCtx.lineTo(0, canvasHeight); + canvasCtx.stroke(); + + canvasCtx.moveTo(0, 0); + canvasCtx.lineTo(canvasWidth, 0); + canvasCtx.stroke(); } } } + diff --git a/src/vs/editor/common/commonCodeEditor.ts b/src/vs/editor/common/commonCodeEditor.ts index 2c38ec938d2..4881d847e08 100644 --- a/src/vs/editor/common/commonCodeEditor.ts +++ b/src/vs/editor/common/commonCodeEditor.ts @@ -30,6 +30,7 @@ import { ICursorPositionChangedEvent, ICursorSelectionChangedEvent } from 'vs/ed import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { CommonEditorRegistry } from 'vs/editor/common/editorCommonExtensions'; import { VerticalRevealType } from 'vs/editor/common/view/viewEvents'; +import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations'; let EDITOR_ID = 0; @@ -856,6 +857,26 @@ export abstract class CommonCodeEditor extends Disposable implements editorCommo this._decorationTypeKeysToIds[decorationTypeKey] = this.deltaDecorations(oldDecorationsIds, newModelDecorations); } + public setDecorationsFast(decorationTypeKey: string, ranges: IRange[]): void { + + // remove decoration sub types that are no longer used, deregister decoration type if necessary + let oldDecorationsSubTypes = this._decorationTypeSubtypes[decorationTypeKey] || {}; + for (let subType in oldDecorationsSubTypes) { + this._removeDecorationType(decorationTypeKey + '-' + subType); + } + this._decorationTypeSubtypes[decorationTypeKey] = {}; + + const opts = ModelDecorationOptions.createDynamic(this._resolveDecorationOptions(decorationTypeKey, false)); + let newModelDecorations: editorCommon.IModelDeltaDecoration[] = new Array(ranges.length); + for (let i = 0, len = ranges.length; i < len; i++) { + newModelDecorations[i] = { range: ranges[i], options: opts }; + } + + // update all decorations + let oldDecorationsIds = this._decorationTypeKeysToIds[decorationTypeKey] || []; + this._decorationTypeKeysToIds[decorationTypeKey] = this.deltaDecorations(oldDecorationsIds, newModelDecorations); + } + public removeDecorations(decorationTypeKey: string): void { // remove decorations for type and sub type let oldDecorationsIds = this._decorationTypeKeysToIds[decorationTypeKey]; diff --git a/src/vs/editor/common/controller/coreCommands.ts b/src/vs/editor/common/controller/coreCommands.ts index cefe9d0d216..33661420850 100644 --- a/src/vs/editor/common/controller/coreCommands.ts +++ b/src/vs/editor/common/controller/coreCommands.ts @@ -8,7 +8,7 @@ import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import * as editorCommon from 'vs/editor/common/editorCommon'; -import { CursorState, ICursors, RevealTarget, IColumnSelectData, CursorContext } from 'vs/editor/common/controller/cursorCommon'; +import { CursorState, ICursors, RevealTarget, IColumnSelectData, CursorContext, EditOperationType } from 'vs/editor/common/controller/cursorCommon'; import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents'; import { CursorMoveCommands, CursorMove as CursorMove_ } from 'vs/editor/common/controller/cursorMoveCommands'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; @@ -1614,11 +1614,13 @@ export namespace CoreEditingCommands { } public runEditorCommand(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor, args: any): void { - const [shouldPushStackElementBefore, commands] = DeleteOperations.deleteLeft(editor._getCursorConfiguration(), editor.getModel(), editor.getSelections()); + const cursors = editor._getCursors(); + const [shouldPushStackElementBefore, commands] = DeleteOperations.deleteLeft(cursors.getPrevEditOperationType(), editor._getCursorConfiguration(), editor.getModel(), editor.getSelections()); if (shouldPushStackElementBefore) { editor.pushUndoStop(); } editor.executeCommands(this.id, commands); + cursors.setPrevEditOperationType(EditOperationType.DeletingLeft); } }); @@ -1637,11 +1639,13 @@ export namespace CoreEditingCommands { } public runEditorCommand(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor, args: any): void { - const [shouldPushStackElementBefore, commands] = DeleteOperations.deleteRight(editor._getCursorConfiguration(), editor.getModel(), editor.getSelections()); + const cursors = editor._getCursors(); + const [shouldPushStackElementBefore, commands] = DeleteOperations.deleteRight(cursors.getPrevEditOperationType(), editor._getCursorConfiguration(), editor.getModel(), editor.getSelections()); if (shouldPushStackElementBefore) { editor.pushUndoStop(); } editor.executeCommands(this.id, commands); + cursors.setPrevEditOperationType(EditOperationType.DeletingRight); } }); diff --git a/src/vs/editor/common/controller/cursor.ts b/src/vs/editor/common/controller/cursor.ts index 65bc4059e5c..9c247c0ac58 100644 --- a/src/vs/editor/common/controller/cursor.ts +++ b/src/vs/editor/common/controller/cursor.ts @@ -12,7 +12,7 @@ import { Position } from 'vs/editor/common/core/position'; 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 { CursorColumns, CursorConfiguration, EditOperationResult, CursorContext, CursorState, RevealTarget, IColumnSelectData, ICursors, EditOperationType } from 'vs/editor/common/controller/cursorCommon'; 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'; @@ -98,6 +98,7 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors { private _isHandling: boolean; private _isDoingComposition: boolean; private _columnSelectData: IColumnSelectData; + private _prevEditOperationType: EditOperationType; constructor(configuration: editorCommon.IConfiguration, model: editorCommon.IModel, viewModel: IViewModel) { super(); @@ -110,6 +111,7 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors { this._isHandling = false; this._isDoingComposition = false; this._columnSelectData = null; + this._prevEditOperationType = EditOperationType.Other; this._register(this._model.addBulkListener((events) => { if (this._isHandling) { @@ -278,6 +280,9 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors { } private _onModelContentChanged(hadFlushEvent: boolean): void { + + this._prevEditOperationType = EditOperationType.Other; + if (hadFlushEvent) { // a model.setValue() was called this._cursors.dispose(); @@ -322,6 +327,14 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors { this.setStates(source, CursorChangeReason.NotSet, CursorState.fromModelSelections(selections)); } + public getPrevEditOperationType(): EditOperationType { + return this._prevEditOperationType; + } + + public setPrevEditOperationType(type: EditOperationType): void { + this._prevEditOperationType = type; + } + // ------ auxiliary handling logic private _executeEditOperation(opResult: EditOperationResult): void { @@ -344,6 +357,8 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors { if (result) { // The commands were applied correctly this._interpretCommandResult(result); + + this._prevEditOperationType = opResult.type; } if (opResult.shouldPushStackElementAfter) { @@ -515,16 +530,16 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors { } // Here we must interpret each typed character individually, that's why we create a new context - this._executeEditOperation(TypeOperations.typeWithInterceptors(this.context.config, this.context.model, this.getSelections(), chr)); + this._executeEditOperation(TypeOperations.typeWithInterceptors(this._prevEditOperationType, this.context.config, this.context.model, this.getSelections(), chr)); } } else { - this._executeEditOperation(TypeOperations.typeWithoutInterceptors(this.context.config, this.context.model, this.getSelections(), text)); + this._executeEditOperation(TypeOperations.typeWithoutInterceptors(this._prevEditOperationType, this.context.config, this.context.model, this.getSelections(), text)); } } private _replacePreviousChar(text: string, replaceCharCnt: number): void { - this._executeEditOperation(TypeOperations.replacePreviousChar(this.context.config, this.context.model, this.getSelections(), text, replaceCharCnt)); + this._executeEditOperation(TypeOperations.replacePreviousChar(this._prevEditOperationType, this.context.config, this.context.model, this.getSelections(), text, replaceCharCnt)); } private _paste(text: string, pasteOnNewLine: boolean): void { @@ -538,14 +553,14 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors { private _externalExecuteCommand(command: editorCommon.ICommand): void { this._cursors.killSecondaryCursors(); - this._executeEditOperation(new EditOperationResult([command], { + this._executeEditOperation(new EditOperationResult(EditOperationType.Other, [command], { shouldPushStackElementBefore: false, shouldPushStackElementAfter: false })); } private _externalExecuteCommands(commands: editorCommon.ICommand[]): void { - this._executeEditOperation(new EditOperationResult(commands, { + this._executeEditOperation(new EditOperationResult(EditOperationType.Other, commands, { shouldPushStackElementBefore: false, shouldPushStackElementAfter: false })); @@ -555,8 +570,8 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors { interface IExecContext { readonly model: editorCommon.IModel; readonly selectionsBefore: Selection[]; - readonly selectionStartMarkers: string[]; - readonly positionMarkers: string[]; + readonly trackedRanges: string[]; + readonly trackedRangesDirection: SelectionDirection[]; } interface ICommandData { @@ -576,15 +591,14 @@ class CommandExecutor { const ctx: IExecContext = { model: model, selectionsBefore: selectionsBefore, - selectionStartMarkers: [], - positionMarkers: [] + trackedRanges: [], + trackedRangesDirection: [] }; const result = this._innerExecuteCommands(ctx, commands); - for (let i = 0; i < ctx.selectionStartMarkers.length; i++) { - ctx.model._removeMarker(ctx.selectionStartMarkers[i]); - ctx.model._removeMarker(ctx.positionMarkers[i]); + for (let i = 0, len = ctx.trackedRanges.length; i < len; i++) { + ctx.model._setTrackedRange(ctx.trackedRanges[i], null, editorCommon.TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges); } return result; @@ -661,9 +675,11 @@ class CommandExecutor { getTrackedSelection: (id: string) => { const idx = parseInt(id, 10); - const selectionStartMarker = ctx.model._getMarker(ctx.selectionStartMarkers[idx]); - const positionMarker = ctx.model._getMarker(ctx.positionMarkers[idx]); - return new Selection(selectionStartMarker.lineNumber, selectionStartMarker.column, positionMarker.lineNumber, positionMarker.column); + const range = ctx.model._getTrackedRange(ctx.trackedRanges[idx]); + if (ctx.trackedRangesDirection[idx] === SelectionDirection.LTR) { + return new Selection(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn); + } + return new Selection(range.endLineNumber, range.endColumn, range.startLineNumber, range.startColumn); } }); } else { @@ -750,37 +766,31 @@ class CommandExecutor { }; const trackSelection = (selection: Selection, trackPreviousOnEmpty?: boolean) => { - let selectionMarkerStickToPreviousCharacter: boolean; - let positionMarkerStickToPreviousCharacter: boolean; - + let stickiness: editorCommon.TrackedRangeStickiness; if (selection.isEmpty()) { - // Try to lock it with surrounding text if (typeof trackPreviousOnEmpty === 'boolean') { - selectionMarkerStickToPreviousCharacter = trackPreviousOnEmpty; - positionMarkerStickToPreviousCharacter = trackPreviousOnEmpty; + if (trackPreviousOnEmpty) { + stickiness = editorCommon.TrackedRangeStickiness.GrowsOnlyWhenTypingBefore; + } else { + stickiness = editorCommon.TrackedRangeStickiness.GrowsOnlyWhenTypingAfter; + } } else { + // Try to lock it with surrounding text const maxLineColumn = ctx.model.getLineMaxColumn(selection.startLineNumber); if (selection.startColumn === maxLineColumn) { - selectionMarkerStickToPreviousCharacter = true; - positionMarkerStickToPreviousCharacter = true; + stickiness = editorCommon.TrackedRangeStickiness.GrowsOnlyWhenTypingBefore; } else { - selectionMarkerStickToPreviousCharacter = false; - positionMarkerStickToPreviousCharacter = false; + stickiness = editorCommon.TrackedRangeStickiness.GrowsOnlyWhenTypingAfter; } } } else { - if (selection.getDirection() === SelectionDirection.LTR) { - selectionMarkerStickToPreviousCharacter = false; - positionMarkerStickToPreviousCharacter = true; - } else { - selectionMarkerStickToPreviousCharacter = true; - positionMarkerStickToPreviousCharacter = false; - } + stickiness = editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges; } - const l = ctx.selectionStartMarkers.length; - ctx.selectionStartMarkers[l] = ctx.model._addMarker(0, selection.selectionStartLineNumber, selection.selectionStartColumn, selectionMarkerStickToPreviousCharacter); - ctx.positionMarkers[l] = ctx.model._addMarker(0, selection.positionLineNumber, selection.positionColumn, positionMarkerStickToPreviousCharacter); + const l = ctx.trackedRanges.length; + const id = ctx.model._setTrackedRange(null, selection, stickiness); + ctx.trackedRanges[l] = id; + ctx.trackedRangesDirection[l] = selection.getDirection(); return l.toString(); }; diff --git a/src/vs/editor/common/controller/cursorCommon.ts b/src/vs/editor/common/controller/cursorCommon.ts index 615643272da..b3b40cca6b3 100644 --- a/src/vs/editor/common/controller/cursorCommon.ts +++ b/src/vs/editor/common/controller/cursorCommon.ts @@ -31,6 +31,17 @@ export const enum RevealTarget { BottomMost = 2 } +/** + * This is an operation type that will be recorded for undo/redo purposes. + * The goal is to introduce an undo stop when the controller switches between different operation types. + */ +export const enum EditOperationType { + Other = 0, + Typing = 1, + DeletingLeft = 2, + DeletingRight = 3 +} + export interface ICursors { readonly context: CursorContext; getPrimaryCursor(): CursorState; @@ -45,6 +56,9 @@ export interface ICursors { revealRange(revealHorizontal: boolean, viewRange: Range, verticalType: VerticalRevealType, scrollType: ScrollType): void; scrollTo(desiredScrollTop: number): void; + + getPrevEditOperationType(): EditOperationType; + setPrevEditOperationType(type: EditOperationType): void; } export interface CharacterMap { @@ -422,17 +436,20 @@ export class CursorState { export class EditOperationResult { _editOperationResultBrand: void; + readonly type: EditOperationType; readonly commands: ICommand[]; readonly shouldPushStackElementBefore: boolean; readonly shouldPushStackElementAfter: boolean; constructor( + type: EditOperationType, commands: ICommand[], opts: { shouldPushStackElementBefore: boolean; shouldPushStackElementAfter: boolean; } ) { + this.type = type; this.commands = commands; this.shouldPushStackElementBefore = opts.shouldPushStackElementBefore; this.shouldPushStackElementAfter = opts.shouldPushStackElementAfter; diff --git a/src/vs/editor/common/controller/cursorDeleteOperations.ts b/src/vs/editor/common/controller/cursorDeleteOperations.ts index 7951ab91b83..e5b498b40e9 100644 --- a/src/vs/editor/common/controller/cursorDeleteOperations.ts +++ b/src/vs/editor/common/controller/cursorDeleteOperations.ts @@ -5,7 +5,7 @@ 'use strict'; import { ReplaceCommand } from 'vs/editor/common/commands/replaceCommand'; -import { CursorColumns, CursorConfiguration, ICursorSimpleModel, EditOperationResult } from 'vs/editor/common/controller/cursorCommon'; +import { CursorColumns, CursorConfiguration, ICursorSimpleModel, EditOperationResult, EditOperationType } from 'vs/editor/common/controller/cursorCommon'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import { MoveOperations } from 'vs/editor/common/controller/cursorMoveOperations'; @@ -14,9 +14,9 @@ import { ICommand } from 'vs/editor/common/editorCommon'; export class DeleteOperations { - public static deleteRight(config: CursorConfiguration, model: ICursorSimpleModel, selections: Selection[]): [boolean, ICommand[]] { + public static deleteRight(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ICursorSimpleModel, selections: Selection[]): [boolean, ICommand[]] { let commands: ICommand[] = []; - let shouldPushStackElementBefore = false; + let shouldPushStackElementBefore = (prevEditOperationType !== EditOperationType.DeletingRight); for (let i = 0, len = selections.length; i < len; i++) { const selection = selections[i]; @@ -94,14 +94,14 @@ export class DeleteOperations { return [true, commands]; } - public static deleteLeft(config: CursorConfiguration, model: ICursorSimpleModel, selections: Selection[]): [boolean, ICommand[]] { + public static deleteLeft(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ICursorSimpleModel, selections: Selection[]): [boolean, ICommand[]] { if (this._isAutoClosingPairDelete(config, model, selections)) { return this._runAutoClosingPairDelete(config, model, selections); } let commands: ICommand[] = []; - let shouldPushStackElementBefore = false; + let shouldPushStackElementBefore = (prevEditOperationType !== EditOperationType.DeletingLeft); for (let i = 0, len = selections.length; i < len; i++) { const selection = selections[i]; @@ -210,7 +210,7 @@ export class DeleteOperations { commands[i] = new ReplaceCommand(selection, ''); } } - return new EditOperationResult(commands, { + return new EditOperationResult(EditOperationType.Other, commands, { shouldPushStackElementBefore: true, shouldPushStackElementAfter: true }); diff --git a/src/vs/editor/common/controller/cursorTypeOperations.ts b/src/vs/editor/common/controller/cursorTypeOperations.ts index 129d02b48a5..0ffb52bd5af 100644 --- a/src/vs/editor/common/controller/cursorTypeOperations.ts +++ b/src/vs/editor/common/controller/cursorTypeOperations.ts @@ -6,7 +6,7 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { ReplaceCommand, ReplaceCommandWithoutChangingPosition, ReplaceCommandWithOffsetCursorState } from 'vs/editor/common/commands/replaceCommand'; -import { CursorColumns, CursorConfiguration, ICursorSimpleModel, EditOperationResult } from 'vs/editor/common/controller/cursorCommon'; +import { CursorColumns, CursorConfiguration, ICursorSimpleModel, EditOperationResult, EditOperationType } from 'vs/editor/common/controller/cursorCommon'; import { Range } from 'vs/editor/common/core/range'; import { ICommand, ITokenizedModel } from 'vs/editor/common/editorCommon'; import * as strings from 'vs/base/common/strings'; @@ -73,7 +73,7 @@ export class TypeOperations { for (let i = 0, len = selections.length; i < len; i++) { commands[i] = new ReplaceCommand(selections[i], text[i]); } - return new EditOperationResult(commands, { + return new EditOperationResult(EditOperationType.Other, commands, { shouldPushStackElementBefore: true, shouldPushStackElementAfter: true }); @@ -103,7 +103,7 @@ export class TypeOperations { commands[i] = new ReplaceCommand(selection, text); } } - return new EditOperationResult(commands, { + return new EditOperationResult(EditOperationType.Other, commands, { shouldPushStackElementBefore: true, shouldPushStackElementAfter: true }); @@ -255,7 +255,7 @@ export class TypeOperations { return commands; } - public static replacePreviousChar(config: CursorConfiguration, model: ITokenizedModel, selections: Selection[], txt: string, replaceCharCnt: number): EditOperationResult { + public static replacePreviousChar(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ITokenizedModel, selections: Selection[], txt: string, replaceCharCnt: number): EditOperationResult { let commands: ICommand[] = []; for (let i = 0, len = selections.length; i < len; i++) { const selection = selections[i]; @@ -271,8 +271,8 @@ export class TypeOperations { let range = new Range(pos.lineNumber, startColumn, pos.lineNumber, pos.column); commands[i] = new ReplaceCommand(range, txt); } - return new EditOperationResult(commands, { - shouldPushStackElementBefore: false, + return new EditOperationResult(EditOperationType.Typing, commands, { + shouldPushStackElementBefore: (prevEditOperationType !== EditOperationType.Typing), shouldPushStackElementAfter: false }); } @@ -324,6 +324,13 @@ export class TypeOperations { } // no enter rules applied, we should check indentation rules then. + if (!config.autoIndent) { + // Nothing special + let lineText = model.getLineContent(range.startLineNumber); + let indentation = strings.getLeadingWhitespace(lineText).substring(0, range.startColumn - 1); + return TypeOperations._typeCommand(range, '\n' + config.normalizeIndentation(indentation), keepPosition); + } + let ir = LanguageConfigurationRegistry.getIndentForEnter(model, range, { unshiftIndent: (indent) => { return TypeOperations.unshiftIndent(config, indent); @@ -468,7 +475,7 @@ export class TypeOperations { return cnt; } - private static _runAutoClosingCloseCharType(config: CursorConfiguration, model: ITokenizedModel, selections: Selection[], ch: string): EditOperationResult { + private static _runAutoClosingCloseCharType(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ITokenizedModel, selections: Selection[], ch: string): EditOperationResult { let commands: ICommand[] = []; for (let i = 0, len = selections.length; i < len; i++) { const selection = selections[i]; @@ -476,8 +483,8 @@ export class TypeOperations { const typeSelection = new Range(position.lineNumber, position.column, position.lineNumber, position.column + 1); commands[i] = new ReplaceCommand(typeSelection, ch); } - return new EditOperationResult(commands, { - shouldPushStackElementBefore: false, + return new EditOperationResult(EditOperationType.Typing, commands, { + shouldPushStackElementBefore: (prevEditOperationType !== EditOperationType.Typing), shouldPushStackElementAfter: false }); } @@ -550,14 +557,14 @@ export class TypeOperations { return true; } - private static _runAutoClosingOpenCharType(config: CursorConfiguration, model: ITokenizedModel, selections: Selection[], ch: string): EditOperationResult { + private static _runAutoClosingOpenCharType(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ITokenizedModel, selections: Selection[], ch: string): EditOperationResult { let commands: ICommand[] = []; for (let i = 0, len = selections.length; i < len; i++) { const selection = selections[i]; const closeCharacter = config.autoClosingPairsOpen[ch]; commands[i] = new ReplaceCommandWithOffsetCursorState(selection, ch + closeCharacter, 0, -closeCharacter.length); } - return new EditOperationResult(commands, { + return new EditOperationResult(EditOperationType.Typing, commands, { shouldPushStackElementBefore: true, shouldPushStackElementAfter: false }); @@ -597,14 +604,14 @@ export class TypeOperations { return true; } - private static _runSurroundSelectionType(config: CursorConfiguration, model: ITokenizedModel, selections: Selection[], ch: string): EditOperationResult { + private static _runSurroundSelectionType(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ITokenizedModel, selections: Selection[], ch: string): EditOperationResult { let commands: ICommand[] = []; for (let i = 0, len = selections.length; i < len; i++) { const selection = selections[i]; const closeCharacter = config.surroundingPairs[ch]; commands[i] = new SurroundSelectionCommand(selection, ch, closeCharacter); } - return new EditOperationResult(commands, { + return new EditOperationResult(EditOperationType.Other, commands, { shouldPushStackElementBefore: true, shouldPushStackElementAfter: true }); @@ -617,7 +624,7 @@ export class TypeOperations { return false; } - private static _typeInterceptorElectricChar(config: CursorConfiguration, model: ITokenizedModel, selection: Selection, ch: string): EditOperationResult { + private static _typeInterceptorElectricChar(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ITokenizedModel, selection: Selection, ch: string): EditOperationResult { if (!config.electricChars.hasOwnProperty(ch) || !selection.isEmpty()) { return null; } @@ -639,7 +646,7 @@ export class TypeOperations { if (electricAction.appendText) { const command = new ReplaceCommandWithOffsetCursorState(selection, ch + electricAction.appendText, 0, -electricAction.appendText.length); - return new EditOperationResult([command], { + return new EditOperationResult(EditOperationType.Typing, [command], { shouldPushStackElementBefore: false, shouldPushStackElementAfter: true }); @@ -670,7 +677,7 @@ export class TypeOperations { let typeSelection = new Range(position.lineNumber, 1, position.lineNumber, position.column); const command = new ReplaceCommand(typeSelection, typeText); - return new EditOperationResult([command], { + return new EditOperationResult(EditOperationType.Typing, [command], { shouldPushStackElementBefore: false, shouldPushStackElementAfter: true }); @@ -680,14 +687,14 @@ export class TypeOperations { return null; } - public static typeWithInterceptors(config: CursorConfiguration, model: ITokenizedModel, selections: Selection[], ch: string): EditOperationResult { + public static typeWithInterceptors(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ITokenizedModel, selections: Selection[], ch: string): EditOperationResult { if (ch === '\n') { let commands: ICommand[] = []; for (let i = 0, len = selections.length; i < len; i++) { commands[i] = TypeOperations._enter(config, model, false, selections[i]); } - return new EditOperationResult(commands, { + return new EditOperationResult(EditOperationType.Typing, commands, { shouldPushStackElementBefore: true, shouldPushStackElementAfter: false, }); @@ -704,7 +711,7 @@ export class TypeOperations { } } if (!autoIndentFails) { - return new EditOperationResult(commands, { + return new EditOperationResult(EditOperationType.Typing, commands, { shouldPushStackElementBefore: true, shouldPushStackElementAfter: false, }); @@ -712,36 +719,48 @@ export class TypeOperations { } if (this._isAutoClosingCloseCharType(config, model, selections, ch)) { - return this._runAutoClosingCloseCharType(config, model, selections, ch); + return this._runAutoClosingCloseCharType(prevEditOperationType, config, model, selections, ch); } if (this._isAutoClosingOpenCharType(config, model, selections, ch)) { - return this._runAutoClosingOpenCharType(config, model, selections, ch); + return this._runAutoClosingOpenCharType(prevEditOperationType, config, model, selections, ch); } if (this._isSurroundSelectionType(config, model, selections, ch)) { - return this._runSurroundSelectionType(config, model, selections, ch); + return this._runSurroundSelectionType(prevEditOperationType, config, model, selections, ch); } // Electric characters make sense only when dealing with a single cursor, // as multiple cursors typing brackets for example would interfer with bracket matching if (this._isTypeInterceptorElectricChar(config, model, selections)) { - const r = this._typeInterceptorElectricChar(config, model, selections[0], ch); + const r = this._typeInterceptorElectricChar(prevEditOperationType, config, model, selections[0], ch); if (r) { return r; } } - return this.typeWithoutInterceptors(config, model, selections, ch); + // A simple character type + let commands: ICommand[] = []; + for (let i = 0, len = selections.length; i < len; i++) { + commands[i] = new ReplaceCommand(selections[i], ch); + } + let shouldPushStackElementBefore = (prevEditOperationType !== EditOperationType.Typing); + if (ch === ' ') { + shouldPushStackElementBefore = true; + } + return new EditOperationResult(EditOperationType.Typing, commands, { + shouldPushStackElementBefore: shouldPushStackElementBefore, + shouldPushStackElementAfter: false + }); } - public static typeWithoutInterceptors(config: CursorConfiguration, model: ITokenizedModel, selections: Selection[], str: string): EditOperationResult { + public static typeWithoutInterceptors(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ITokenizedModel, selections: Selection[], str: string): EditOperationResult { let commands: ICommand[] = []; for (let i = 0, len = selections.length; i < len; i++) { commands[i] = new ReplaceCommand(selections[i], str); } - return new EditOperationResult(commands, { - shouldPushStackElementBefore: false, + return new EditOperationResult(EditOperationType.Typing, commands, { + shouldPushStackElementBefore: (prevEditOperationType !== EditOperationType.Typing), shouldPushStackElementAfter: false }); } diff --git a/src/vs/editor/common/controller/cursorWordOperations.ts b/src/vs/editor/common/controller/cursorWordOperations.ts index 7d8b52b17a3..8f83c1f8621 100644 --- a/src/vs/editor/common/controller/cursorWordOperations.ts +++ b/src/vs/editor/common/controller/cursorWordOperations.ts @@ -425,7 +425,9 @@ export class WordOperations { let lineNumber = position.lineNumber; let column: number; - if (position.isBeforeOrEqual(cursor.selectionStart.getStartPosition())) { + if (cursor.selectionStart.containsPosition(position)) { + column = cursor.selectionStart.endColumn; + } else if (position.isBeforeOrEqual(cursor.selectionStart.getStartPosition())) { column = startColumn; let possiblePosition = new Position(lineNumber, column); if (cursor.selectionStart.containsPosition(possiblePosition)) { diff --git a/src/vs/editor/common/controller/oneCursor.ts b/src/vs/editor/common/controller/oneCursor.ts index ae9c990ec4a..fc2690244fb 100644 --- a/src/vs/editor/common/controller/oneCursor.ts +++ b/src/vs/editor/common/controller/oneCursor.ts @@ -8,16 +8,21 @@ import { SingleCursorState, CursorContext, CursorState } from 'vs/editor/common/ import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { Selection, SelectionDirection } from 'vs/editor/common/core/selection'; +import { TrackedRangeStickiness } from 'vs/editor/common/editorCommon'; export class OneCursor { public modelState: SingleCursorState; public viewState: SingleCursorState; - private _selStartMarker: string; - private _selEndMarker: string; + private _selTrackedRange: string; constructor(context: CursorContext) { + this.modelState = null; + this.viewState = null; + + this._selTrackedRange = null; + this._setState( context, new SingleCursorState(new Range(1, 1, 1, 1), 0, new Position(1, 1), 0), @@ -26,8 +31,7 @@ export class OneCursor { } public dispose(context: CursorContext): void { - context.model._removeMarker(this._selStartMarker); - context.model._removeMarker(this._selEndMarker); + this._selTrackedRange = context.model._setTrackedRange(this._selTrackedRange, null, TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges); } public asCursorState(): CursorState { @@ -35,14 +39,11 @@ export class OneCursor { } public readSelectionFromMarkers(context: CursorContext): Selection { - const start = context.model._getMarker(this._selStartMarker); - const end = context.model._getMarker(this._selEndMarker); - + const range = context.model._getTrackedRange(this._selTrackedRange); if (this.modelState.selection.getDirection() === SelectionDirection.LTR) { - return new Selection(start.lineNumber, start.column, end.lineNumber, end.column); + return new Selection(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn); } - - return new Selection(end.lineNumber, end.column, start.lineNumber, start.column); + return new Selection(range.endLineNumber, range.endColumn, range.startLineNumber, range.startColumn); } public ensureValidState(context: CursorContext): void { @@ -100,17 +101,6 @@ export class OneCursor { this.modelState = modelState; this.viewState = viewState; - this._selStartMarker = this._ensureMarker(context, this._selStartMarker, this.modelState.selection.startLineNumber, this.modelState.selection.startColumn, true); - this._selEndMarker = this._ensureMarker(context, this._selEndMarker, this.modelState.selection.endLineNumber, this.modelState.selection.endColumn, false); - } - - private _ensureMarker(context: CursorContext, markerId: string, lineNumber: number, column: number, stickToPreviousCharacter: boolean): string { - if (!markerId) { - return context.model._addMarker(0, lineNumber, column, stickToPreviousCharacter); - } else { - context.model._changeMarker(markerId, lineNumber, column); - context.model._changeMarkerStickiness(markerId, stickToPreviousCharacter); - return markerId; - } + this._selTrackedRange = context.model._setTrackedRange(this._selTrackedRange, this.modelState.selection, TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges); } } diff --git a/src/vs/editor/common/editorCommon.ts b/src/vs/editor/common/editorCommon.ts index ab9db53d0ce..a346ca6ea9d 100644 --- a/src/vs/editor/common/editorCommon.ts +++ b/src/vs/editor/common/editorCommon.ts @@ -157,10 +157,6 @@ export interface IModelDecoration { * Options associated with this decoration. */ readonly options: IModelDecorationOptions; - /** - * A flag describing if this is a problem decoration (e.g. warning/error). - */ - readonly isForValidation: boolean; } /** @@ -915,32 +911,6 @@ export interface ITokenizedModel extends ITextModel { getLineIndentGuide(lineNumber: number): number; } -/** - * A model that can track markers. - */ -export interface ITextModelWithMarkers extends ITextModel { - /** - * @internal - */ - _addMarker(internalDecorationId: number, lineNumber: number, column: number, stickToPreviousCharacter: boolean): string; - /** - * @internal - */ - _changeMarker(id: string, newLineNumber: number, newColumn: number): void; - /** - * @internal - */ - _changeMarkerStickiness(id: string, newStickToPreviousCharacter: boolean): void; - /** - * @internal - */ - _getMarker(id: string): Position; - /** - * @internal - */ - _removeMarker(id: string): void; -} - /** * Describes the behavior of decorations when typing/editing near their edges. * Note: Please do not edit the values, as they very carefully match `DecorationRangeBehavior` @@ -1034,12 +1004,29 @@ export interface ITextModelWithDecorations { * @param filterOutValidation If set, it will ignore decorations specific to validation (i.e. warnings, errors). */ getAllDecorations(ownerId?: number, filterOutValidation?: boolean): IModelDecoration[]; + + /** + * Gets all the decorations that should be rendered in the overview ruler as an array. + * @param ownerId If set, it will ignore decorations belonging to other owners. + * @param filterOutValidation If set, it will ignore decorations specific to validation (i.e. warnings, errors). + */ + getOverviewRulerDecorations(ownerId?: number, filterOutValidation?: boolean): IModelDecoration[]; + + /** + * @internal + */ + _getTrackedRange(id: string): Range; + + /** + * @internal + */ + _setTrackedRange(id: string, newRange: Range, newStickiness: TrackedRangeStickiness): string; } /** * An editable text model. */ -export interface IEditableTextModel extends ITextModelWithMarkers { +export interface IEditableTextModel extends ITextModel { /** * Normalize a string containing whitespace according to indentation rules (converts to spaces or to tabs). @@ -1122,7 +1109,7 @@ export interface IEditableTextModel extends ITextModelWithMarkers { /** * A model. */ -export interface IModel extends IReadOnlyModel, IEditableTextModel, ITextModelWithMarkers, ITokenizedModel, ITextModelWithDecorations { +export interface IModel extends IReadOnlyModel, IEditableTextModel, ITokenizedModel, ITextModelWithDecorations { /** * @deprecated Please use `onDidChangeContent` instead. * An event emitted when the contents of the model have changed. @@ -1977,6 +1964,11 @@ export interface ICommonCodeEditor extends IEditor { */ setDecorations(decorationTypeKey: string, ranges: IDecorationOptions[]): void; + /** + * @internal + */ + setDecorationsFast(decorationTypeKey: string, ranges: IRange[]): void; + /** * @internal */ diff --git a/src/vs/editor/common/editorCommonExtensions.ts b/src/vs/editor/common/editorCommonExtensions.ts index fee9cdfbd3a..aaea4371983 100644 --- a/src/vs/editor/common/editorCommonExtensions.ts +++ b/src/vs/editor/common/editorCommonExtensions.ts @@ -153,6 +153,7 @@ export abstract class EditorCommand extends Command { export interface IEditorCommandMenuOptions { group?: string; order?: number; + when?: ContextKeyExpr; } export interface IActionOptions extends ICommandOptions { label: string; @@ -182,7 +183,7 @@ export abstract class EditorAction extends EditorCommand { id: this.id, title: this.label }, - when: this.precondition, + when: ContextKeyExpr.and(this.precondition, this.menuOpts.when), group: this.menuOpts.group, order: this.menuOpts.order }; diff --git a/src/vs/editor/common/model/editableTextModel.ts b/src/vs/editor/common/model/editableTextModel.ts index 66566d1092b..77c0bd9f8a8 100644 --- a/src/vs/editor/common/model/editableTextModel.ts +++ b/src/vs/editor/common/model/editableTextModel.ts @@ -7,12 +7,11 @@ import { Range, IRange } from 'vs/editor/common/core/range'; import * as editorCommon from 'vs/editor/common/editorCommon'; import { EditStack } from 'vs/editor/common/model/editStack'; -import { ILineEdit, LineMarker, MarkersTracker, IModelLine } from 'vs/editor/common/model/modelLine'; +import { ILineEdit, IModelLine } from 'vs/editor/common/model/modelLine'; import { TextModelWithDecorations, ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations'; import * as strings from 'vs/base/common/strings'; import * as arrays from 'vs/base/common/arrays'; import { Selection } from 'vs/editor/common/core/selection'; -import { Position } from 'vs/editor/common/core/position'; import { IDisposable } from 'vs/base/common/lifecycle'; import { LanguageIdentifier } from 'vs/editor/common/modes'; import { ITextSource, IRawTextSource, RawTextSource } from 'vs/editor/common/model/textSource'; @@ -23,6 +22,7 @@ export interface IValidatedEditOperation { sortIndex: number; identifier: editorCommon.ISingleEditOperationIdentifier; range: Range; + rangeOffset: number; rangeLength: number; lines: string[]; forceMoveMarkers: boolean; @@ -249,6 +249,7 @@ export class EditableTextModel extends TextModelWithDecorations implements edito sortIndex: 0, identifier: operations[0].identifier, range: entireEditRange, + rangeOffset: this.getOffsetAt(entireEditRange.getStartPosition()), rangeLength: this.getValueLengthInRange(entireEditRange), lines: result.join('').split('\n'), forceMoveMarkers: forceMoveMarkers, @@ -275,15 +276,15 @@ export class EditableTextModel extends TextModelWithDecorations implements edito public applyEdits(rawOperations: editorCommon.IIdentifiedSingleEditOperation[]): editorCommon.IIdentifiedSingleEditOperation[] { try { this._eventEmitter.beginDeferredEmit(); - let markersTracker = this._acquireMarkersTracker(); - return this._applyEdits(markersTracker, rawOperations); + this._acquireDecorationsTracker(); + return this._applyEdits(rawOperations); } finally { - this._releaseMarkersTracker(); + this._releaseDecorationsTracker(); this._eventEmitter.endDeferredEmit(); } } - private _applyEdits(markersTracker: MarkersTracker, rawOperations: editorCommon.IIdentifiedSingleEditOperation[]): editorCommon.IIdentifiedSingleEditOperation[] { + private _applyEdits(rawOperations: editorCommon.IIdentifiedSingleEditOperation[]): editorCommon.IIdentifiedSingleEditOperation[] { if (rawOperations.length === 0) { return []; } @@ -310,6 +311,7 @@ export class EditableTextModel extends TextModelWithDecorations implements edito sortIndex: i, identifier: op.identifier, range: validatedRange, + rangeOffset: this.getOffsetAt(validatedRange.getStartPosition()), rangeLength: this.getValueLengthInRange(validatedRange), lines: op.text ? op.text.split(/\r\n|\r|\n/) : null, forceMoveMarkers: op.forceMoveMarkers, @@ -378,7 +380,7 @@ export class EditableTextModel extends TextModelWithDecorations implements edito this._mightContainRTL = mightContainRTL; this._mightContainNonBasicASCII = mightContainNonBasicASCII; - this._doApplyEdits(markersTracker, operations); + this._doApplyEdits(operations); this._trimAutoWhitespaceLines = null; if (this._options.trimAutoWhitespace && newTrimAutoWhitespaceCandidates.length > 0) { @@ -465,7 +467,7 @@ export class EditableTextModel extends TextModelWithDecorations implements edito return result; } - private _doApplyEdits(markersTracker: MarkersTracker, operations: IValidatedEditOperation[]): void { + private _doApplyEdits(operations: IValidatedEditOperation[]): void { const tabSize = this._options.tabSize; @@ -503,11 +505,8 @@ export class EditableTextModel extends TextModelWithDecorations implements edito } this._invalidateLine(currentLineNumber - 1); - this._lines[currentLineNumber - 1].applyEdits(markersTracker, lineEditsQueue.slice(currentLineNumberStart, i), tabSize); - if (this._lineStarts) { - // update prefix sum - this._lineStarts.changeValue(currentLineNumber - 1, this._lines[currentLineNumber - 1].text.length + this._EOL.length); - } + this._lines[currentLineNumber - 1].applyEdits(lineEditsQueue.slice(currentLineNumberStart, i), tabSize); + this._lineStarts.changeValue(currentLineNumber - 1, this._lines[currentLineNumber - 1].text.length + this._EOL.length); rawContentChanges.push( new textModelEvents.ModelRawLineChanged(currentLineNumber, this._lines[currentLineNumber - 1].text) ); @@ -517,11 +516,8 @@ export class EditableTextModel extends TextModelWithDecorations implements edito } this._invalidateLine(currentLineNumber - 1); - this._lines[currentLineNumber - 1].applyEdits(markersTracker, lineEditsQueue.slice(currentLineNumberStart, lineEditsQueue.length), tabSize); - if (this._lineStarts) { - // update prefix sum - this._lineStarts.changeValue(currentLineNumber - 1, this._lines[currentLineNumber - 1].text.length + this._EOL.length); - } + this._lines[currentLineNumber - 1].applyEdits(lineEditsQueue.slice(currentLineNumberStart, lineEditsQueue.length), tabSize); + this._lineStarts.changeValue(currentLineNumber - 1, this._lines[currentLineNumber - 1].text.length + this._EOL.length); rawContentChanges.push( new textModelEvents.ModelRawLineChanged(currentLineNumber, this._lines[currentLineNumber - 1].text) ); @@ -529,10 +525,6 @@ export class EditableTextModel extends TextModelWithDecorations implements edito lineEditsQueue = []; }; - let minTouchedLineNumber = operations[operations.length - 1].range.startLineNumber; - let maxTouchedLineNumber = operations[0].range.endLineNumber + 1; - let totalLinesCountDelta = 0; - for (let i = 0, len = operations.length; i < len; i++) { const op = operations[i]; @@ -556,8 +548,6 @@ export class EditableTextModel extends TextModelWithDecorations implements edito const insertingLinesCnt = (op.lines ? op.lines.length - 1 : 0); const editingLinesCnt = Math.min(deletingLinesCnt, insertingLinesCnt); - totalLinesCountDelta += (insertingLinesCnt - deletingLinesCnt); - // Iterating descending to overlap with previous op // in case there are common lines being edited in both for (let j = editingLinesCnt; j >= 0; j--) { @@ -567,8 +557,7 @@ export class EditableTextModel extends TextModelWithDecorations implements edito lineNumber: editLineNumber, startColumn: (editLineNumber === startLineNumber ? startColumn : 1), endColumn: (editLineNumber === endLineNumber ? endColumn : this.getLineMaxColumn(editLineNumber)), - text: (op.lines ? op.lines[j] : ''), - forceMoveMarkers: op.forceMoveMarkers + text: (op.lines ? op.lines[j] : '') }); } @@ -579,43 +568,19 @@ export class EditableTextModel extends TextModelWithDecorations implements edito flushLineEdits(); const spliceStartLineNumber = startLineNumber + editingLinesCnt; - const spliceStartColumn = this.getLineMaxColumn(spliceStartLineNumber); - const endLineRemains = this._lines[endLineNumber - 1].split(markersTracker, endColumn, false, tabSize); + const endLineRemains = this._lines[endLineNumber - 1].split(endColumn, tabSize); this._invalidateLine(spliceStartLineNumber - 1); const spliceCnt = endLineNumber - spliceStartLineNumber; - // Collect all these markers - let markersOnDeletedLines: LineMarker[] = []; - for (let j = 0; j < spliceCnt; j++) { - const deleteLineIndex = spliceStartLineNumber + j; - const deleteLineMarkers = this._lines[deleteLineIndex].getMarkers(); - if (deleteLineMarkers) { - markersOnDeletedLines = markersOnDeletedLines.concat(deleteLineMarkers); - } - } - this._lines.splice(spliceStartLineNumber, spliceCnt); - if (this._lineStarts) { - // update prefix sum - this._lineStarts.removeValues(spliceStartLineNumber, spliceCnt); - } + this._lineStarts.removeValues(spliceStartLineNumber, spliceCnt); // Reconstruct first line - this._lines[spliceStartLineNumber - 1].append(markersTracker, spliceStartLineNumber, endLineRemains, tabSize); - if (this._lineStarts) { - // update prefix sum - this._lineStarts.changeValue(spliceStartLineNumber - 1, this._lines[spliceStartLineNumber - 1].text.length + this._EOL.length); - } + this._lines[spliceStartLineNumber - 1].append(endLineRemains, tabSize); + this._lineStarts.changeValue(spliceStartLineNumber - 1, this._lines[spliceStartLineNumber - 1].text.length + this._EOL.length); - // Update deleted markers - const deletedMarkersPosition = new Position(spliceStartLineNumber, spliceStartColumn); - for (let j = 0, lenJ = markersOnDeletedLines.length; j < lenJ; j++) { - markersOnDeletedLines[j].updatePosition(markersTracker, deletedMarkersPosition); - } - - this._lines[spliceStartLineNumber - 1].addMarkers(markersOnDeletedLines); rawContentChanges.push( new textModelEvents.ModelRawLineChanged(spliceStartLineNumber, this._lines[spliceStartLineNumber - 1].text) ); @@ -638,11 +603,8 @@ export class EditableTextModel extends TextModelWithDecorations implements edito } // Split last line - let leftoverLine = this._lines[spliceLineNumber - 1].split(markersTracker, spliceColumn, op.forceMoveMarkers, tabSize); - if (this._lineStarts) { - // update prefix sum - this._lineStarts.changeValue(spliceLineNumber - 1, this._lines[spliceLineNumber - 1].text.length + this._EOL.length); - } + let leftoverLine = this._lines[spliceLineNumber - 1].split(spliceColumn, tabSize); + this._lineStarts.changeValue(spliceLineNumber - 1, this._lines[spliceLineNumber - 1].text.length + this._EOL.length); rawContentChanges.push( new textModelEvents.ModelRawLineChanged(spliceLineNumber, this._lines[spliceLineNumber - 1].text) ); @@ -659,44 +621,31 @@ export class EditableTextModel extends TextModelWithDecorations implements edito } this._lines = arrays.arrayInsert(this._lines, startLineNumber + editingLinesCnt, newLines); newLinesContent[newLinesContent.length - 1] += leftoverLine.text; - if (this._lineStarts) { - // update prefix sum - this._lineStarts.insertValues(startLineNumber + editingLinesCnt, newLinesLengths); - } + this._lineStarts.insertValues(startLineNumber + editingLinesCnt, newLinesLengths); // Last line - this._lines[startLineNumber + insertingLinesCnt - 1].append(markersTracker, startLineNumber + insertingLinesCnt, leftoverLine, tabSize); - if (this._lineStarts) { - // update prefix sum - this._lineStarts.changeValue(startLineNumber + insertingLinesCnt - 1, this._lines[startLineNumber + insertingLinesCnt - 1].text.length + this._EOL.length); - } + this._lines[startLineNumber + insertingLinesCnt - 1].append(leftoverLine, tabSize); + this._lineStarts.changeValue(startLineNumber + insertingLinesCnt - 1, this._lines[startLineNumber + insertingLinesCnt - 1].text.length + this._EOL.length); rawContentChanges.push( new textModelEvents.ModelRawLinesInserted(spliceLineNumber + 1, startLineNumber + insertingLinesCnt, newLinesContent.join('\n')) ); } + const text = (op.lines ? op.lines.join(this.getEOL()) : ''); contentChanges.push({ range: new Range(startLineNumber, startColumn, endLineNumber, endColumn), rangeLength: op.rangeLength, - text: op.lines ? op.lines.join(this.getEOL()) : '' + text: text }); + this._adjustDecorationsForEdit(op.rangeOffset, op.rangeLength, text.length, op.forceMoveMarkers); + // console.log('AFTER:'); // console.log('<<<\n' + this._lines.map(l => l.text).join('\n') + '\n>>>'); } flushLineEdits(); - maxTouchedLineNumber = Math.max(1, Math.min(this.getLineCount(), maxTouchedLineNumber + totalLinesCountDelta)); - if (totalLinesCountDelta !== 0) { - // must update line numbers all the way to the bottom - maxTouchedLineNumber = this.getLineCount(); - } - - for (let lineNumber = minTouchedLineNumber; lineNumber <= maxTouchedLineNumber; lineNumber++) { - this._lines[lineNumber - 1].updateLineNumber(markersTracker, lineNumber); - } - if (rawContentChanges.length !== 0 || contentChanges.length !== 0) { this._increaseVersionId(); @@ -718,35 +667,9 @@ export class EditableTextModel extends TextModelWithDecorations implements edito this._eventEmitter.emit(textModelEvents.TextModelEventType.ModelContentChanged, e); } - // this._assertLineNumbersOK(); this._resetIndentRanges(); } - public _assertLineNumbersOK(): void { - let foundMarkersCnt = 0; - for (let i = 0, len = this._lines.length; i < len; i++) { - let line = this._lines[i]; - let lineNumber = i + 1; - - let markers = line.getMarkers(); - if (markers !== null) { - for (let j = 0, lenJ = markers.length; j < lenJ; j++) { - foundMarkersCnt++; - let markerId = markers[j].id; - let marker = this._markerIdToMarker[markerId]; - if (marker.position.lineNumber !== lineNumber) { - throw new Error('Misplaced marker with id ' + markerId); - } - } - } - } - - let totalMarkersCnt = Object.keys(this._markerIdToMarker).length; - if (totalMarkersCnt !== foundMarkersCnt) { - throw new Error('There are misplaced markers!'); - } - } - private _undo(): Selection[] { this._isUndoing = true; let r = this._commandManager.undo(); @@ -764,10 +687,10 @@ export class EditableTextModel extends TextModelWithDecorations implements edito public undo(): Selection[] { try { this._eventEmitter.beginDeferredEmit(); - this._acquireMarkersTracker(); + this._acquireDecorationsTracker(); return this._undo(); } finally { - this._releaseMarkersTracker(); + this._releaseDecorationsTracker(); this._eventEmitter.endDeferredEmit(); } } @@ -789,10 +712,10 @@ export class EditableTextModel extends TextModelWithDecorations implements edito public redo(): Selection[] { try { this._eventEmitter.beginDeferredEmit(); - this._acquireMarkersTracker(); + this._acquireDecorationsTracker(); return this._redo(); } finally { - this._releaseMarkersTracker(); + this._releaseDecorationsTracker(); this._eventEmitter.endDeferredEmit(); } } diff --git a/src/vs/editor/common/model/intervalTree.ts b/src/vs/editor/common/model/intervalTree.ts new file mode 100644 index 00000000000..c07fdd33ba2 --- /dev/null +++ b/src/vs/editor/common/model/intervalTree.ts @@ -0,0 +1,1369 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations'; +import { Range } from 'vs/editor/common/core/range'; +import { IModelDecoration } from 'vs/editor/common/editorCommon'; + +// +// The red-black tree is based on the "Introduction to Algorithms" by Cormen, Leiserson and Rivest. +// + +export const ClassName = { + EditorInfoDecoration: 'infosquiggly', + EditorWarningDecoration: 'warningsquiggly', + EditorErrorDecoration: 'errorsquiggly' +}; + +/** + * Describes the behavior of decorations when typing/editing near their edges. + * Note: Please do not edit the values, as they very carefully match `DecorationRangeBehavior` + */ +export const enum TrackedRangeStickiness { + AlwaysGrowsWhenTypingAtEdges = 0, + NeverGrowsWhenTypingAtEdges = 1, + GrowsOnlyWhenTypingBefore = 2, + GrowsOnlyWhenTypingAfter = 3, +} + +const enum NodeColor { + Black = 0, + Red = 1, +} + +const enum Constants { + ColorMask = 0b00000001, + ColorMaskInverse = 0b11111110, + ColorOffset = 0, + + IsVisitedMask = 0b00000010, + IsVisitedMaskInverse = 0b11111101, + IsVisitedOffset = 1, + + IsForValidationMask = 0b00000100, + IsForValidationMaskInverse = 0b11111011, + IsForValidationOffset = 2, + + IsInOverviewRulerMask = 0b00001000, + IsInOverviewRulerMaskInverse = 0b11110111, + IsInOverviewRulerOffset = 3, + + StickinessMask = 0b00110000, + StickinessMaskInverse = 0b11001111, + StickinessOffset = 4, + + /** + * Due to how deletion works (in order to avoid always walking the right subtree of the deleted node), + * the deltas for nodes can grow and shrink dramatically. It has been observed, in practice, that unless + * the deltas are corrected, integer overflow will occur. + * + * The integer overflow occurs when 53 bits are used in the numbers, but we will try to avoid it as + * a node's delta gets below a negative 30 bits number. + * + * MIN SMI (SMall Integer) as defined in v8. + * one bit is lost for boxing/unboxing flag. + * one bit is lost for sign flag. + * See https://thibaultlaurens.github.io/javascript/2013/04/29/how-the-v8-engine-works/#tagged-values + */ + MIN_SAFE_DELTA = -(1 << 30), + /** + * MAX SMI (SMall Integer) as defined in v8. + * one bit is lost for boxing/unboxing flag. + * one bit is lost for sign flag. + * See https://thibaultlaurens.github.io/javascript/2013/04/29/how-the-v8-engine-works/#tagged-values + */ + MAX_SAFE_DELTA = 1 << 30, +} + +function getNodeColor(node: IntervalNode): NodeColor { + return ((node.metadata & Constants.ColorMask) >>> Constants.ColorOffset); +} +function setNodeColor(node: IntervalNode, color: NodeColor): void { + node.metadata = ( + (node.metadata & Constants.ColorMaskInverse) | (color << Constants.ColorOffset) + ); +} +function getNodeIsVisited(node: IntervalNode): boolean { + return ((node.metadata & Constants.IsVisitedMask) >>> Constants.IsVisitedOffset) === 1; +} +function setNodeIsVisited(node: IntervalNode, value: boolean): void { + node.metadata = ( + (node.metadata & Constants.IsVisitedMaskInverse) | ((value ? 1 : 0) << Constants.IsVisitedOffset) + ); +} +function getNodeIsForValidation(node: IntervalNode): boolean { + return ((node.metadata & Constants.IsForValidationMask) >>> Constants.IsForValidationOffset) === 1; +} +function setNodeIsForValidation(node: IntervalNode, value: boolean): void { + node.metadata = ( + (node.metadata & Constants.IsForValidationMaskInverse) | ((value ? 1 : 0) << Constants.IsForValidationOffset) + ); +} +export function getNodeIsInOverviewRuler(node: IntervalNode): boolean { + return ((node.metadata & Constants.IsInOverviewRulerMask) >>> Constants.IsInOverviewRulerOffset) === 1; +} +function setNodeIsInOverviewRuler(node: IntervalNode, value: boolean): void { + node.metadata = ( + (node.metadata & Constants.IsInOverviewRulerMaskInverse) | ((value ? 1 : 0) << Constants.IsInOverviewRulerOffset) + ); +} +function getNodeStickiness(node: IntervalNode): TrackedRangeStickiness { + return ((node.metadata & Constants.StickinessMask) >>> Constants.StickinessOffset); +} +function setNodeStickiness(node: IntervalNode, stickiness: TrackedRangeStickiness): void { + node.metadata = ( + (node.metadata & Constants.StickinessMaskInverse) | (stickiness << Constants.StickinessOffset) + ); +} + +export class IntervalNode implements IModelDecoration { + + /** + * contains binary encoded information for color, visited, isForValidation and stickiness. + */ + public metadata: number; + + public parent: IntervalNode; + public left: IntervalNode; + public right: IntervalNode; + + public start: number; + public end: number; + public delta: number; + public maxEnd: number; + + public id: string; + public ownerId: number; + public options: ModelDecorationOptions; + + public cachedVersionId: number; + public cachedAbsoluteStart: number; + public cachedAbsoluteEnd: number; + public range: Range; + + constructor(id: string, start: number, end: number) { + this.metadata = 0; + + this.parent = null; + this.left = null; + this.right = null; + setNodeColor(this, NodeColor.Red); + + this.start = start; + this.end = end; + // FORCE_OVERFLOWING_TEST: this.delta = start; + this.delta = 0; + this.maxEnd = end; + + this.id = id; + this.ownerId = 0; + this.options = null; + setNodeIsForValidation(this, false); + setNodeStickiness(this, TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges); + setNodeIsInOverviewRuler(this, false); + + this.cachedVersionId = 0; + this.cachedAbsoluteStart = start; + this.cachedAbsoluteEnd = end; + this.range = null; + + setNodeIsVisited(this, false); + } + + public reset(versionId: number, start: number, end: number, range: Range): void { + this.start = start; + this.end = end; + this.maxEnd = end; + this.cachedVersionId = versionId; + this.cachedAbsoluteStart = start; + this.cachedAbsoluteEnd = end; + this.range = range; + } + + public setOptions(options: ModelDecorationOptions) { + this.options = options; + setNodeIsForValidation(this, ( + this.options.className === ClassName.EditorErrorDecoration + || this.options.className === ClassName.EditorWarningDecoration + )); + setNodeStickiness(this, this.options.stickiness); + setNodeIsInOverviewRuler(this, this.options.overviewRuler.color ? true : false); + } + + public setCachedOffsets(absoluteStart: number, absoluteEnd: number, cachedVersionId: number): void { + if (this.cachedVersionId !== cachedVersionId) { + this.range = null; + } + this.cachedVersionId = cachedVersionId; + this.cachedAbsoluteStart = absoluteStart; + this.cachedAbsoluteEnd = absoluteEnd; + } + + public detach(): void { + this.parent = null; + this.left = null; + this.right = null; + } +} + +const SENTINEL: IntervalNode = new IntervalNode(null, 0, 0); +SENTINEL.parent = SENTINEL; +SENTINEL.left = SENTINEL; +SENTINEL.right = SENTINEL; +setNodeColor(SENTINEL, NodeColor.Black); + +export class IntervalTree { + + public root: IntervalNode; + public requestNormalizeDelta: boolean; + + constructor() { + this.root = SENTINEL; + this.requestNormalizeDelta = false; + } + + public intervalSearch(start: number, end: number, filterOwnerId: number, filterOutValidation: boolean, cachedVersionId: number): IntervalNode[] { + if (this.root === SENTINEL) { + return []; + } + return intervalSearch(this, start, end, filterOwnerId, filterOutValidation, cachedVersionId); + } + + public search(filterOwnerId: number, filterOutValidation: boolean, cachedVersionId: number): IntervalNode[] { + if (this.root === SENTINEL) { + return []; + } + return search(this, filterOwnerId, filterOutValidation, cachedVersionId); + } + + public count(): number { + if (this.root === SENTINEL) { + return 0; + } + return nodeCount(this); + } + + /** + * Will not set `cachedAbsoluteStart` nor `cachedAbsoluteEnd` on the returned nodes! + */ + public collectNodesFromOwner(ownerId: number): IntervalNode[] { + return collectNodesFromOwner(this, ownerId); + } + + /** + * Will not set `cachedAbsoluteStart` nor `cachedAbsoluteEnd` on the returned nodes! + */ + public collectNodesPostOrder(): IntervalNode[] { + return collectNodesPostOrder(this); + } + + public insert(node: IntervalNode): void { + rbTreeInsert(this, node); + this._normalizeDeltaIfNecessary(); + } + + public delete(node: IntervalNode): void { + rbTreeDelete(this, node); + this._normalizeDeltaIfNecessary(); + } + + public resolveNode(node: IntervalNode, cachedVersionId: number): void { + const initialNode = node; + let delta = 0; + while (node !== this.root) { + if (node === node.parent.right) { + delta += node.parent.delta; + } + node = node.parent; + } + + const nodeStart = initialNode.start + delta; + const nodeEnd = initialNode.end + delta; + initialNode.setCachedOffsets(nodeStart, nodeEnd, cachedVersionId); + } + + public acceptReplace(offset: number, length: number, textLength: number, forceMoveMarkers: boolean): void { + // Our strategy is to remove all directly impacted nodes, and then add them back to the tree. + + // (1) collect all nodes that are intersecting this edit as nodes of interest + const nodesOfInterest = searchForEditing(this, offset, offset + length); + + // (2) remove all nodes that are intersecting this edit + for (let i = 0, len = nodesOfInterest.length; i < len; i++) { + const node = nodesOfInterest[i]; + rbTreeDelete(this, node); + } + this._normalizeDeltaIfNecessary(); + + // (3) edit all tree nodes except the nodes of interest + noOverlapReplace(this, offset, offset + length, textLength); + this._normalizeDeltaIfNecessary(); + + // (4) edit the nodes of interest and insert them back in the tree + for (let i = 0, len = nodesOfInterest.length; i < len; i++) { + const node = nodesOfInterest[i]; + node.start = node.cachedAbsoluteStart; + node.end = node.cachedAbsoluteEnd; + nodeAcceptEdit(node, offset, (offset + length), textLength, forceMoveMarkers); + node.maxEnd = node.end; + rbTreeInsert(this, node); + } + this._normalizeDeltaIfNecessary(); + } + + public assertInvariants(): void { + assert(getNodeColor(SENTINEL) === NodeColor.Black); + assert(SENTINEL.parent === SENTINEL); + assert(SENTINEL.left === SENTINEL); + assert(SENTINEL.right === SENTINEL); + assert(SENTINEL.start === 0); + assert(SENTINEL.end === 0); + assert(SENTINEL.delta === 0); + assert(this.root.parent === SENTINEL); + assertValidTree(this); + } + + public getAllInOrder(): IntervalNode[] { + return search(this, 0, false, 0); + } + + public print(): void { + if (this.root === SENTINEL) { + console.log(`~~ empty`); + return; + } + let out: string[] = []; + this._print(this.root, '', 0, out); + console.log(out.join('')); + } + + private _print(n: IntervalNode, indent: string, delta: number, out: string[]): void { + out.push(`${indent}[${getNodeColor(n) === NodeColor.Red ? 'R' : 'B'},${n.delta}, ${n.start}->${n.end}, ${n.maxEnd}] : {${delta + n.start}->${delta + n.end}}, maxEnd: ${n.maxEnd + delta}\n`); + if (n.left !== SENTINEL) { + this._print(n.left, indent + ' ', delta, out); + } else { + out.push(`${indent} NIL\n`); + } + if (n.right !== SENTINEL) { + this._print(n.right, indent + ' ', delta + n.delta, out); + } else { + out.push(`${indent} NIL\n`); + } + } + + private _normalizeDeltaIfNecessary(): void { + if (!this.requestNormalizeDelta) { + return; + } + this.requestNormalizeDelta = false; + normalizeDelta(this); + } +} + +//#region Delta Normalization +function normalizeDelta(T: IntervalTree): void { + let node = T.root; + let delta = 0; + while (node !== SENTINEL) { + + if (node.left !== SENTINEL && !getNodeIsVisited(node.left)) { + // go left + node = node.left; + continue; + } + + if (node.right !== SENTINEL && !getNodeIsVisited(node.right)) { + // go right + delta += node.delta; + node = node.right; + continue; + } + + // handle current node + node.start = delta + node.start; + node.end = delta + node.end; + node.delta = 0; + recomputeMaxEnd(node); + + setNodeIsVisited(node, true); + + // going up from this node + setNodeIsVisited(node.left, false); + setNodeIsVisited(node.right, false); + if (node === node.parent.right) { + delta -= node.parent.delta; + } + node = node.parent; + } + + setNodeIsVisited(T.root, false); +} +//#endregion + +//#region Editing + +const enum MarkerMoveSemantics { + MarkerDefined = 0, + ForceMove = 1, + ForceStay = 2 +} + +function adjustMarkerBeforeColumn(markerOffset: number, markerStickToPreviousCharacter: boolean, checkOffset: number, moveSemantics: MarkerMoveSemantics): boolean { + if (markerOffset < checkOffset) { + return true; + } + if (markerOffset > checkOffset) { + return false; + } + if (moveSemantics === MarkerMoveSemantics.ForceMove) { + return false; + } + if (moveSemantics === MarkerMoveSemantics.ForceStay) { + return true; + } + return markerStickToPreviousCharacter; +}; + +/** + * This is a lot more complicated than strictly necessary to maintain the same behaviour + * as when decorations were implemented using two markers. + */ +function nodeAcceptEdit(node: IntervalNode, start: number, end: number, textLength: number, forceMoveMarkers: boolean): void { + const nodeStickiness = getNodeStickiness(node); + const startStickToPreviousCharacter = ( + nodeStickiness === TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges + || nodeStickiness === TrackedRangeStickiness.GrowsOnlyWhenTypingBefore + ); + const endStickToPreviousCharacter = ( + nodeStickiness === TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges + || nodeStickiness === TrackedRangeStickiness.GrowsOnlyWhenTypingBefore + ); + + const deletingCnt = (end - start); + const insertingCnt = textLength; + const commonLength = Math.min(deletingCnt, insertingCnt); + + const nodeStart = node.start; + let startDone = false; + + const nodeEnd = node.end; + let endDone = false; + + { + const moveSemantics = forceMoveMarkers ? MarkerMoveSemantics.ForceMove : (deletingCnt > 0 ? MarkerMoveSemantics.ForceStay : MarkerMoveSemantics.MarkerDefined); + if (!startDone && adjustMarkerBeforeColumn(nodeStart, startStickToPreviousCharacter, start, moveSemantics)) { + startDone = true; + } + if (!endDone && adjustMarkerBeforeColumn(nodeEnd, endStickToPreviousCharacter, start, moveSemantics)) { + endDone = true; + } + } + + if (commonLength > 0 && !forceMoveMarkers) { + const moveSemantics = (deletingCnt > insertingCnt ? MarkerMoveSemantics.ForceStay : MarkerMoveSemantics.MarkerDefined); + if (!startDone && adjustMarkerBeforeColumn(nodeStart, startStickToPreviousCharacter, start + commonLength, moveSemantics)) { + startDone = true; + } + if (!endDone && adjustMarkerBeforeColumn(nodeEnd, endStickToPreviousCharacter, start + commonLength, moveSemantics)) { + endDone = true; + } + } + + { + const moveSemantics = forceMoveMarkers ? MarkerMoveSemantics.ForceMove : MarkerMoveSemantics.MarkerDefined; + if (!startDone && adjustMarkerBeforeColumn(nodeStart, startStickToPreviousCharacter, end, moveSemantics)) { + node.start = start + insertingCnt; + startDone = true; + } + if (!endDone && adjustMarkerBeforeColumn(nodeEnd, endStickToPreviousCharacter, end, moveSemantics)) { + node.end = start + insertingCnt; + endDone = true; + } + } + + // Finish + const deltaColumn = (insertingCnt - deletingCnt); + if (!startDone) { + node.start = Math.max(0, nodeStart + deltaColumn); + startDone = true; + } + if (!endDone) { + node.end = Math.max(0, nodeEnd + deltaColumn); + endDone = true; + } + + if (node.start > node.end) { + node.end = node.start; + } +} + +function searchForEditing(T: IntervalTree, start: number, end: number): IntervalNode[] { + // https://en.wikipedia.org/wiki/Interval_tree#Augmented_tree + // Now, it is known that two intervals A and B overlap only when both + // A.low <= B.high and A.high >= B.low. When searching the trees for + // nodes overlapping with a given interval, you can immediately skip: + // a) all nodes to the right of nodes whose low value is past the end of the given interval. + // b) all nodes that have their maximum 'high' value below the start of the given interval. + let node = T.root; + let delta = 0; + let nodeMaxEnd = 0; + let nodeStart = 0; + let nodeEnd = 0; + let result: IntervalNode[] = []; + let resultLen = 0; + while (node !== SENTINEL) { + if (getNodeIsVisited(node)) { + // going up from this node + setNodeIsVisited(node.left, false); + setNodeIsVisited(node.right, false); + if (node === node.parent.right) { + delta -= node.parent.delta; + } + node = node.parent; + continue; + } + + if (!getNodeIsVisited(node.left)) { + // first time seeing this node + nodeMaxEnd = delta + node.maxEnd; + if (nodeMaxEnd < start) { + // cover case b) from above + // there is no need to search this node or its children + setNodeIsVisited(node, true); + continue; + } + + if (node.left !== SENTINEL) { + // go left + node = node.left; + continue; + } + } + + // handle current node + nodeStart = delta + node.start; + if (nodeStart > end) { + // cover case a) from above + // there is no need to search this node or its right subtree + setNodeIsVisited(node, true); + continue; + } + + nodeEnd = delta + node.end; + if (nodeEnd >= start) { + node.setCachedOffsets(nodeStart, nodeEnd, 0); + result[resultLen++] = node; + } + setNodeIsVisited(node, true); + + if (node.right !== SENTINEL && !getNodeIsVisited(node.right)) { + // go right + delta += node.delta; + node = node.right; + continue; + } + } + + setNodeIsVisited(T.root, false); + + return result; +} + +function noOverlapReplace(T: IntervalTree, start: number, end: number, textLength: number): void { + // https://en.wikipedia.org/wiki/Interval_tree#Augmented_tree + // Now, it is known that two intervals A and B overlap only when both + // A.low <= B.high and A.high >= B.low. When searching the trees for + // nodes overlapping with a given interval, you can immediately skip: + // a) all nodes to the right of nodes whose low value is past the end of the given interval. + // b) all nodes that have their maximum 'high' value below the start of the given interval. + let node = T.root; + let delta = 0; + let nodeMaxEnd = 0; + let nodeStart = 0; + const editDelta = (textLength - (end - start)); + while (node !== SENTINEL) { + if (getNodeIsVisited(node)) { + // going up from this node + setNodeIsVisited(node.left, false); + setNodeIsVisited(node.right, false); + if (node === node.parent.right) { + delta -= node.parent.delta; + } + recomputeMaxEnd(node); + node = node.parent; + continue; + } + + if (!getNodeIsVisited(node.left)) { + // first time seeing this node + nodeMaxEnd = delta + node.maxEnd; + if (nodeMaxEnd < start) { + // cover case b) from above + // there is no need to search this node or its children + setNodeIsVisited(node, true); + continue; + } + + if (node.left !== SENTINEL) { + // go left + node = node.left; + continue; + } + } + + // handle current node + nodeStart = delta + node.start; + if (nodeStart > end) { + node.start += editDelta; + node.end += editDelta; + node.delta += editDelta; + if (node.delta < Constants.MIN_SAFE_DELTA || node.delta > Constants.MAX_SAFE_DELTA) { + T.requestNormalizeDelta = true; + } + // cover case a) from above + // there is no need to search this node or its right subtree + setNodeIsVisited(node, true); + continue; + } + + setNodeIsVisited(node, true); + + if (node.right !== SENTINEL && !getNodeIsVisited(node.right)) { + // go right + delta += node.delta; + node = node.right; + continue; + } + } + + setNodeIsVisited(T.root, false); +} + +//#endregion + +//#region Searching + +function nodeCount(T: IntervalTree): number { + let node = T.root; + let count = 0; + while (node !== SENTINEL) { + if (getNodeIsVisited(node)) { + // going up from this node + setNodeIsVisited(node.left, false); + setNodeIsVisited(node.right, false); + node = node.parent; + continue; + } + + if (node.left !== SENTINEL && !getNodeIsVisited(node.left)) { + // go left + node = node.left; + continue; + } + + // handle current node + count++; + setNodeIsVisited(node, true); + + if (node.right !== SENTINEL && !getNodeIsVisited(node.right)) { + // go right + node = node.right; + continue; + } + } + + setNodeIsVisited(T.root, false); + + return count; +} + +function collectNodesFromOwner(T: IntervalTree, ownerId: number): IntervalNode[] { + let node = T.root; + let result: IntervalNode[] = []; + let resultLen = 0; + while (node !== SENTINEL) { + if (getNodeIsVisited(node)) { + // going up from this node + setNodeIsVisited(node.left, false); + setNodeIsVisited(node.right, false); + node = node.parent; + continue; + } + + if (node.left !== SENTINEL && !getNodeIsVisited(node.left)) { + // go left + node = node.left; + continue; + } + + // handle current node + if (node.ownerId === ownerId) { + result[resultLen++] = node; + } + + setNodeIsVisited(node, true); + + if (node.right !== SENTINEL && !getNodeIsVisited(node.right)) { + // go right + node = node.right; + continue; + } + } + + setNodeIsVisited(T.root, false); + + return result; +} + +function collectNodesPostOrder(T: IntervalTree): IntervalNode[] { + let node = T.root; + let result: IntervalNode[] = []; + let resultLen = 0; + while (node !== SENTINEL) { + if (getNodeIsVisited(node)) { + // going up from this node + setNodeIsVisited(node.left, false); + setNodeIsVisited(node.right, false); + node = node.parent; + continue; + } + + if (node.left !== SENTINEL && !getNodeIsVisited(node.left)) { + // go left + node = node.left; + continue; + } + + if (node.right !== SENTINEL && !getNodeIsVisited(node.right)) { + // go right + node = node.right; + continue; + } + + // handle current node + result[resultLen++] = node; + setNodeIsVisited(node, true); + } + + setNodeIsVisited(T.root, false); + + return result; +} + +function search(T: IntervalTree, filterOwnerId: number, filterOutValidation: boolean, cachedVersionId: number): IntervalNode[] { + let node = T.root; + let delta = 0; + let nodeStart = 0; + let nodeEnd = 0; + let result: IntervalNode[] = []; + let resultLen = 0; + while (node !== SENTINEL) { + if (getNodeIsVisited(node)) { + // going up from this node + setNodeIsVisited(node.left, false); + setNodeIsVisited(node.right, false); + if (node === node.parent.right) { + delta -= node.parent.delta; + } + node = node.parent; + continue; + } + + if (node.left !== SENTINEL && !getNodeIsVisited(node.left)) { + // go left + node = node.left; + continue; + } + + // handle current node + nodeStart = delta + node.start; + nodeEnd = delta + node.end; + + node.setCachedOffsets(nodeStart, nodeEnd, cachedVersionId); + + let include = true; + if (filterOwnerId && node.ownerId && node.ownerId !== filterOwnerId) { + include = false; + } + if (filterOutValidation && getNodeIsForValidation(node)) { + include = false; + } + if (include) { + result[resultLen++] = node; + } + + setNodeIsVisited(node, true); + + if (node.right !== SENTINEL && !getNodeIsVisited(node.right)) { + // go right + delta += node.delta; + node = node.right; + continue; + } + } + + setNodeIsVisited(T.root, false); + + return result; +} + +function intervalSearch(T: IntervalTree, intervalStart: number, intervalEnd: number, filterOwnerId: number, filterOutValidation: boolean, cachedVersionId: number): IntervalNode[] { + // https://en.wikipedia.org/wiki/Interval_tree#Augmented_tree + // Now, it is known that two intervals A and B overlap only when both + // A.low <= B.high and A.high >= B.low. When searching the trees for + // nodes overlapping with a given interval, you can immediately skip: + // a) all nodes to the right of nodes whose low value is past the end of the given interval. + // b) all nodes that have their maximum 'high' value below the start of the given interval. + + let node = T.root; + let delta = 0; + let nodeMaxEnd = 0; + let nodeStart = 0; + let nodeEnd = 0; + let result: IntervalNode[] = []; + let resultLen = 0; + while (node !== SENTINEL) { + if (getNodeIsVisited(node)) { + // going up from this node + setNodeIsVisited(node.left, false); + setNodeIsVisited(node.right, false); + if (node === node.parent.right) { + delta -= node.parent.delta; + } + node = node.parent; + continue; + } + + if (!getNodeIsVisited(node.left)) { + // first time seeing this node + nodeMaxEnd = delta + node.maxEnd; + if (nodeMaxEnd < intervalStart) { + // cover case b) from above + // there is no need to search this node or its children + setNodeIsVisited(node, true); + continue; + } + + if (node.left !== SENTINEL) { + // go left + node = node.left; + continue; + } + } + + // handle current node + nodeStart = delta + node.start; + if (nodeStart > intervalEnd) { + // cover case a) from above + // there is no need to search this node or its right subtree + setNodeIsVisited(node, true); + continue; + } + + nodeEnd = delta + node.end; + + if (nodeEnd >= intervalStart) { + // There is overlap + node.setCachedOffsets(nodeStart, nodeEnd, cachedVersionId); + + let include = true; + if (filterOwnerId && node.ownerId && node.ownerId !== filterOwnerId) { + include = false; + } + if (filterOutValidation && getNodeIsForValidation(node)) { + include = false; + } + + if (include) { + result[resultLen++] = node; + } + } + + setNodeIsVisited(node, true); + + if (node.right !== SENTINEL && !getNodeIsVisited(node.right)) { + // go right + delta += node.delta; + node = node.right; + continue; + } + } + + setNodeIsVisited(T.root, false); + + return result; +} + +//#endregion + +//#region Insertion +function rbTreeInsert(T: IntervalTree, newNode: IntervalNode): IntervalNode { + if (T.root === SENTINEL) { + newNode.parent = SENTINEL; + newNode.left = SENTINEL; + newNode.right = SENTINEL; + setNodeColor(newNode, NodeColor.Black); + T.root = newNode; + return T.root; + } + + treeInsert(T, newNode); + + recomputeMaxEndWalkToRoot(newNode.parent); + + // repair tree + let x = newNode; + while (x !== T.root && getNodeColor(x.parent) === NodeColor.Red) { + if (x.parent === x.parent.parent.left) { + const y = x.parent.parent.right; + + if (getNodeColor(y) === NodeColor.Red) { + setNodeColor(x.parent, NodeColor.Black); + setNodeColor(y, NodeColor.Black); + setNodeColor(x.parent.parent, NodeColor.Red); + x = x.parent.parent; + } else { + if (x === x.parent.right) { + x = x.parent; + leftRotate(T, x); + } + setNodeColor(x.parent, NodeColor.Black); + setNodeColor(x.parent.parent, NodeColor.Red); + rightRotate(T, x.parent.parent); + } + } else { + const y = x.parent.parent.left; + + if (getNodeColor(y) === NodeColor.Red) { + setNodeColor(x.parent, NodeColor.Black); + setNodeColor(y, NodeColor.Black); + setNodeColor(x.parent.parent, NodeColor.Red); + x = x.parent.parent; + } else { + if (x === x.parent.left) { + x = x.parent; + rightRotate(T, x); + } + setNodeColor(x.parent, NodeColor.Black); + setNodeColor(x.parent.parent, NodeColor.Red); + leftRotate(T, x.parent.parent); + } + } + } + + setNodeColor(T.root, NodeColor.Black); + + return newNode; +} + +function treeInsert(T: IntervalTree, z: IntervalNode): void { + let delta: number = 0; + let x = T.root; + const zAbsoluteStart = z.start; + const zAbsoluteEnd = z.end; + while (true) { + const cmp = intervalCompare(zAbsoluteStart, zAbsoluteEnd, x.start + delta, x.end + delta); + if (cmp < 0) { + // this node should be inserted to the left + // => it is not affected by the node's delta + if (x.left === SENTINEL) { + z.start -= delta; + z.end -= delta; + z.maxEnd -= delta; + x.left = z; + break; + } else { + x = x.left; + } + } else { + // this node should be inserted to the right + // => it is not affected by the node's delta + if (x.right === SENTINEL) { + z.start -= (delta + x.delta); + z.end -= (delta + x.delta); + z.maxEnd -= (delta + x.delta); + x.right = z; + break; + } else { + delta += x.delta; + x = x.right; + } + } + } + + z.parent = x; + z.left = SENTINEL; + z.right = SENTINEL; + setNodeColor(z, NodeColor.Red); +} +//#endregion + +//#region Deletion +function rbTreeDelete(T: IntervalTree, z: IntervalNode): void { + + let x: IntervalNode; + let y: IntervalNode; + + // RB-DELETE except we don't swap z and y in case c) + // i.e. we always delete what's pointed at by z. + + if (z.left === SENTINEL) { + x = z.right; + y = z; + + // x's delta is no longer influenced by z's delta + x.delta += z.delta; + if (x.delta < Constants.MIN_SAFE_DELTA || x.delta > Constants.MAX_SAFE_DELTA) { + T.requestNormalizeDelta = true; + } + x.start += z.delta; + x.end += z.delta; + + } else if (z.right === SENTINEL) { + x = z.left; + y = z; + + } else { + y = leftest(z.right); + x = y.right; + + // y's delta is no longer influenced by z's delta, + // but we don't want to walk the entire right-hand-side subtree of x. + // we therefore maintain z's delta in y, and adjust only x + x.start += y.delta; + x.end += y.delta; + x.delta += y.delta; + if (x.delta < Constants.MIN_SAFE_DELTA || x.delta > Constants.MAX_SAFE_DELTA) { + T.requestNormalizeDelta = true; + } + + y.start += z.delta; + y.end += z.delta; + y.delta = z.delta; + if (y.delta < Constants.MIN_SAFE_DELTA || y.delta > Constants.MAX_SAFE_DELTA) { + T.requestNormalizeDelta = true; + } + } + + if (y === T.root) { + T.root = x; + setNodeColor(x, NodeColor.Black); + + z.detach(); + resetSentinel(); + recomputeMaxEnd(x); + T.root.parent = SENTINEL; + return; + } + + let yWasRed = (getNodeColor(y) === NodeColor.Red); + + if (y === y.parent.left) { + y.parent.left = x; + } else { + y.parent.right = x; + } + + if (y === z) { + x.parent = y.parent; + } else { + + if (y.parent === z) { + x.parent = y; + } else { + x.parent = y.parent; + } + + y.left = z.left; + y.right = z.right; + y.parent = z.parent; + setNodeColor(y, getNodeColor(z)); + + if (z === T.root) { + T.root = y; + } else { + if (z === z.parent.left) { + z.parent.left = y; + } else { + z.parent.right = y; + } + } + + if (y.left !== SENTINEL) { + y.left.parent = y; + } + if (y.right !== SENTINEL) { + y.right.parent = y; + } + } + + z.detach(); + + if (yWasRed) { + recomputeMaxEndWalkToRoot(x.parent); + if (y !== z) { + recomputeMaxEndWalkToRoot(y); + recomputeMaxEndWalkToRoot(y.parent); + } + resetSentinel(); + return; + } + + recomputeMaxEndWalkToRoot(x); + recomputeMaxEndWalkToRoot(x.parent); + if (y !== z) { + recomputeMaxEndWalkToRoot(y); + recomputeMaxEndWalkToRoot(y.parent); + } + + // RB-DELETE-FIXUP + let w: IntervalNode; + while (x !== T.root && getNodeColor(x) === NodeColor.Black) { + + if (x === x.parent.left) { + w = x.parent.right; + + if (getNodeColor(w) === NodeColor.Red) { + setNodeColor(w, NodeColor.Black); + setNodeColor(x.parent, NodeColor.Red); + leftRotate(T, x.parent); + w = x.parent.right; + } + + if (getNodeColor(w.left) === NodeColor.Black && getNodeColor(w.right) === NodeColor.Black) { + setNodeColor(w, NodeColor.Red); + x = x.parent; + } else { + if (getNodeColor(w.right) === NodeColor.Black) { + setNodeColor(w.left, NodeColor.Black); + setNodeColor(w, NodeColor.Red); + rightRotate(T, w); + w = x.parent.right; + } + + setNodeColor(w, getNodeColor(x.parent)); + setNodeColor(x.parent, NodeColor.Black); + setNodeColor(w.right, NodeColor.Black); + leftRotate(T, x.parent); + x = T.root; + } + + } else { + w = x.parent.left; + + if (getNodeColor(w) === NodeColor.Red) { + setNodeColor(w, NodeColor.Black); + setNodeColor(x.parent, NodeColor.Red); + rightRotate(T, x.parent); + w = x.parent.left; + } + + if (getNodeColor(w.left) === NodeColor.Black && getNodeColor(w.right) === NodeColor.Black) { + setNodeColor(w, NodeColor.Red); + x = x.parent; + + } else { + if (getNodeColor(w.left) === NodeColor.Black) { + setNodeColor(w.right, NodeColor.Black); + setNodeColor(w, NodeColor.Red); + leftRotate(T, w); + w = x.parent.left; + } + + setNodeColor(w, getNodeColor(x.parent)); + setNodeColor(x.parent, NodeColor.Black); + setNodeColor(w.left, NodeColor.Black); + rightRotate(T, x.parent); + x = T.root; + } + } + } + + setNodeColor(x, NodeColor.Black); + resetSentinel(); +} + +function leftest(node: IntervalNode): IntervalNode { + while (node.left !== SENTINEL) { + node = node.left; + } + return node; +} + +function resetSentinel(): void { + SENTINEL.parent = SENTINEL; + SENTINEL.delta = 0; // optional + SENTINEL.start = 0; // optional + SENTINEL.end = 0; // optional +} +//#endregion + +//#region Rotations +function leftRotate(T: IntervalTree, x: IntervalNode): void { + const y = x.right; // set y. + + y.delta += x.delta; // y's delta is no longer influenced by x's delta + if (y.delta < Constants.MIN_SAFE_DELTA || y.delta > Constants.MAX_SAFE_DELTA) { + T.requestNormalizeDelta = true; + } + y.start += x.delta; + y.end += x.delta; + + x.right = y.left; // turn y's left subtree into x's right subtree. + if (y.left !== SENTINEL) { + y.left.parent = x; + } + y.parent = x.parent; // link x's parent to y. + if (x.parent === SENTINEL) { + T.root = y; + } else if (x === x.parent.left) { + x.parent.left = y; + } else { + x.parent.right = y; + } + + y.left = x; // put x on y's left. + x.parent = y; + + recomputeMaxEnd(x); + recomputeMaxEnd(y); +} + +function rightRotate(T: IntervalTree, y: IntervalNode): void { + const x = y.left; + + y.delta -= x.delta; + if (y.delta < Constants.MIN_SAFE_DELTA || y.delta > Constants.MAX_SAFE_DELTA) { + T.requestNormalizeDelta = true; + } + y.start -= x.delta; + y.end -= x.delta; + + y.left = x.right; + if (x.right !== SENTINEL) { + x.right.parent = y; + } + x.parent = y.parent; + if (y.parent === SENTINEL) { + T.root = x; + } else if (y === y.parent.right) { + y.parent.right = x; + } else { + y.parent.left = x; + } + + x.right = y; + y.parent = x; + + recomputeMaxEnd(y); + recomputeMaxEnd(x); +} +//#endregion + +//#region max end computation + +function computeMaxEnd(node: IntervalNode): number { + let maxEnd = node.end; + if (node.left !== SENTINEL) { + const leftMaxEnd = node.left.maxEnd; + if (leftMaxEnd > maxEnd) { + maxEnd = leftMaxEnd; + } + } + if (node.right !== SENTINEL) { + const rightMaxEnd = node.right.maxEnd + node.delta; + if (rightMaxEnd > maxEnd) { + maxEnd = rightMaxEnd; + } + } + return maxEnd; +} + +export function recomputeMaxEnd(node: IntervalNode): void { + node.maxEnd = computeMaxEnd(node); +} + +function recomputeMaxEndWalkToRoot(node: IntervalNode): void { + while (node !== SENTINEL) { + + const maxEnd = computeMaxEnd(node); + + if (node.maxEnd === maxEnd) { + // no need to go further + return; + } + + node.maxEnd = maxEnd; + node = node.parent; + } +} + +//#endregion + +//#region utils +function intervalCompare(aStart: number, aEnd: number, bStart: number, bEnd: number): number { + if (aStart === bStart) { + return aEnd - bEnd; + } + return aStart - bStart; +} +//#endregion + +//#region Assertion + +function depth(n: IntervalNode): number { + if (n === SENTINEL) { + // The leafs are black + return 1; + } + assert(depth(n.left) === depth(n.right)); + return (getNodeColor(n) === NodeColor.Black ? 1 : 0) + depth(n.left); +} + +function assertValidNode(n: IntervalNode, delta): void { + if (n === SENTINEL) { + return; + } + + let l = n.left; + let r = n.right; + + if (getNodeColor(n) === NodeColor.Red) { + assert(getNodeColor(l) === NodeColor.Black); + assert(getNodeColor(r) === NodeColor.Black); + } + + let expectedMaxEnd = n.end; + if (l !== SENTINEL) { + assert(intervalCompare(l.start + delta, l.end + delta, n.start + delta, n.end + delta) <= 0); + expectedMaxEnd = Math.max(expectedMaxEnd, l.maxEnd); + } + if (r !== SENTINEL) { + assert(intervalCompare(n.start + delta, n.end + delta, r.start + delta + n.delta, r.end + delta + n.delta) <= 0); + expectedMaxEnd = Math.max(expectedMaxEnd, r.maxEnd + n.delta); + } + assert(n.maxEnd === expectedMaxEnd); + + assertValidNode(l, delta); + assertValidNode(r, delta + n.delta); +} + +function assertValidTree(tree: IntervalTree): void { + if (tree.root === SENTINEL) { + return; + } + assert(getNodeColor(tree.root) === NodeColor.Black); + assert(depth(tree.root.left) === depth(tree.root.right)); + assertValidNode(tree.root, 0); +} + +function assert(condition: boolean): void { + if (!condition) { + throw new Error('Assertion violation'); + } +} + +//#endregion diff --git a/src/vs/editor/common/model/model.ts b/src/vs/editor/common/model/model.ts index 1228592fe0f..2e1410fc830 100644 --- a/src/vs/editor/common/model/model.ts +++ b/src/vs/editor/common/model/model.ts @@ -16,7 +16,7 @@ import { IRawTextSource, RawTextSource } from 'vs/editor/common/model/textSource import * as textModelEvents from 'vs/editor/common/model/textModelEvents'; // The hierarchy is: -// Model -> EditableTextModel -> TextModelWithDecorations -> TextModelWithTrackedRanges -> TextModelWithMarkers -> TextModelWithTokens -> TextModel +// Model -> EditableTextModel -> TextModelWithDecorations -> TextModelWithTokens -> TextModel var MODEL_ID = 0; diff --git a/src/vs/editor/common/model/modelLine.ts b/src/vs/editor/common/model/modelLine.ts index e71219bd689..b811f40a73f 100644 --- a/src/vs/editor/common/model/modelLine.ts +++ b/src/vs/editor/common/model/modelLine.ts @@ -7,94 +7,12 @@ import { IState, FontStyle, StandardTokenType, MetadataConsts, ColorId, LanguageId } from 'vs/editor/common/modes'; import { CharCode } from 'vs/base/common/charCode'; import { LineTokens } from 'vs/editor/common/core/lineTokens'; -import { Position } from 'vs/editor/common/core/position'; import { Constants } from 'vs/editor/common/core/uint'; export interface ILineEdit { startColumn: number; endColumn: number; text: string; - forceMoveMarkers: boolean; -} - -export class LineMarker { - _lineMarkerBrand: void; - - public readonly id: string; - public readonly internalDecorationId: number; - - public stickToPreviousCharacter: boolean; - public position: Position; - - constructor(id: string, internalDecorationId: number, position: Position, stickToPreviousCharacter: boolean) { - this.id = id; - this.internalDecorationId = internalDecorationId; - this.position = position; - this.stickToPreviousCharacter = stickToPreviousCharacter; - } - - public toString(): string { - return '{\'' + this.id + '\';' + this.position.toString() + ',' + this.stickToPreviousCharacter + '}'; - } - - public updateLineNumber(markersTracker: MarkersTracker, lineNumber: number): void { - if (this.position.lineNumber === lineNumber) { - return; - } - markersTracker.addChangedMarker(this); - this.position = new Position(lineNumber, this.position.column); - } - - public updateColumn(markersTracker: MarkersTracker, column: number): void { - if (this.position.column === column) { - return; - } - markersTracker.addChangedMarker(this); - this.position = new Position(this.position.lineNumber, column); - } - - public updatePosition(markersTracker: MarkersTracker, position: Position): void { - if (this.position.lineNumber === position.lineNumber && this.position.column === position.column) { - return; - } - markersTracker.addChangedMarker(this); - this.position = position; - } - - public setPosition(position: Position) { - this.position = position; - } - - - public static compareMarkers(a: LineMarker, b: LineMarker): number { - if (a.position.column === b.position.column) { - return (a.stickToPreviousCharacter ? 0 : 1) - (b.stickToPreviousCharacter ? 0 : 1); - } - return a.position.column - b.position.column; - } -} - -export class MarkersTracker { - _changedDecorationsBrand: void; - - private _changedDecorations: number[]; - private _changedDecorationsLen: number; - - constructor() { - this._changedDecorations = []; - this._changedDecorationsLen = 0; - } - - public addChangedMarker(marker: LineMarker): void { - let internalDecorationId = marker.internalDecorationId; - if (internalDecorationId !== 0) { - this._changedDecorations[this._changedDecorationsLen++] = internalDecorationId; - } - } - - public getDecorationIds(): number[] { - return this._changedDecorations; - } } export interface ITokensAdjuster { @@ -102,27 +20,10 @@ export interface ITokensAdjuster { finish(delta: number, lineTextLength: number): void; } -interface IMarkersAdjuster { - adjustDelta(toColumn: number, delta: number, minimumAllowedColumn: number, moveSemantics: MarkerMoveSemantics): void; - adjustSet(toColumn: number, newColumn: number, moveSemantics: MarkerMoveSemantics): void; - finish(delta: number, lineTextLength: number): void; -} - var NO_OP_TOKENS_ADJUSTER: ITokensAdjuster = { adjust: () => { }, finish: () => { } }; -var NO_OP_MARKERS_ADJUSTER: IMarkersAdjuster = { - adjustDelta: () => { }, - adjustSet: () => { }, - finish: () => { } -}; - -const enum MarkerMoveSemantics { - MarkerDefined = 0, - ForceMove = 1, - ForceStay = 2 -} /** * Returns: @@ -156,13 +57,6 @@ function computePlusOneIndentLevel(line: string, tabSize: number): number { export interface IModelLine { readonly text: string; - // --- markers - addMarker(marker: LineMarker): void; - addMarkers(markers: LineMarker[]): void; - removeMarker(marker: LineMarker): void; - removeMarkers(deleteMarkers: { [markerId: string]: boolean; }): void; - getMarkers(): LineMarker[]; - // --- tokenization resetTokenizationState(): void; isInvalid(): boolean; @@ -177,20 +71,14 @@ export interface IModelLine { getIndentLevel(): number; // --- editing - updateLineNumber(markersTracker: MarkersTracker, newLineNumber: number): void; - applyEdits(markersTracker: MarkersTracker, edits: ILineEdit[], tabSize: number): number; - append(markersTracker: MarkersTracker, myLineNumber: number, other: IModelLine, tabSize: number): void; - split(markersTracker: MarkersTracker, splitColumn: number, forceMoveMarkers: boolean, tabSize: number): IModelLine; + applyEdits(edits: ILineEdit[], tabSize: number): number; + append(other: IModelLine, tabSize: number): void; + split(splitColumn: number, tabSize: number): IModelLine; } export abstract class AbstractModelLine { - private _markers: LineMarker[]; - - constructor(initializeMarkers: boolean) { - if (initializeMarkers) { - this._markers = null; - } + constructor() { } /// @@ -202,121 +90,18 @@ export abstract class AbstractModelLine { /// - // private _printMarkers(): string { - // if (!this._markers) { - // return '[]'; - // } - // if (this._markers.length === 0) { - // return '[]'; - // } - - // var markers = this._markers; - - // var printMarker = (m:LineMarker) => { - // if (m.stickToPreviousCharacter) { - // return '|' + m.position.column; - // } - // return m.position.column + '|'; - // }; - // return '[' + markers.map(printMarker).join(', ') + ']'; - // } - - private _createMarkersAdjuster(markersTracker: MarkersTracker): IMarkersAdjuster { - if (!this._markers) { - return NO_OP_MARKERS_ADJUSTER; - } - if (this._markers.length === 0) { - return NO_OP_MARKERS_ADJUSTER; - } - - this._markers.sort(LineMarker.compareMarkers); - - var markers = this._markers; - var markersLength = markers.length; - var markersIndex = 0; - var marker = markers[markersIndex]; - - // console.log('------------- INITIAL MARKERS: ' + this._printMarkers()); - - let adjustMarkerBeforeColumn = (toColumn: number, moveSemantics: MarkerMoveSemantics) => { - if (marker.position.column < toColumn) { - return true; - } - if (marker.position.column > toColumn) { - return false; - } - if (moveSemantics === MarkerMoveSemantics.ForceMove) { - return false; - } - if (moveSemantics === MarkerMoveSemantics.ForceStay) { - return true; - } - return marker.stickToPreviousCharacter; - }; - - let adjustDelta = (toColumn: number, delta: number, minimumAllowedColumn: number, moveSemantics: MarkerMoveSemantics) => { - // console.log('------------------------------'); - // console.log('adjustDelta called: toColumn: ' + toColumn + ', delta: ' + delta + ', minimumAllowedColumn: ' + minimumAllowedColumn + ', moveSemantics: ' + MarkerMoveSemantics[moveSemantics]); - // console.log('BEFORE::: markersIndex: ' + markersIndex + ' : ' + this._printMarkers()); - - while (markersIndex < markersLength && adjustMarkerBeforeColumn(toColumn, moveSemantics)) { - if (delta !== 0) { - let newColumn = Math.max(minimumAllowedColumn, marker.position.column + delta); - marker.updateColumn(markersTracker, newColumn); - } - - markersIndex++; - if (markersIndex < markersLength) { - marker = markers[markersIndex]; - } - } - - // console.log('AFTER::: markersIndex: ' + markersIndex + ' : ' + this._printMarkers()); - }; - - let adjustSet = (toColumn: number, newColumn: number, moveSemantics: MarkerMoveSemantics) => { - // console.log('------------------------------'); - // console.log('adjustSet called: toColumn: ' + toColumn + ', newColumn: ' + newColumn + ', moveSemantics: ' + MarkerMoveSemantics[moveSemantics]); - // console.log('BEFORE::: markersIndex: ' + markersIndex + ' : ' + this._printMarkers()); - - while (markersIndex < markersLength && adjustMarkerBeforeColumn(toColumn, moveSemantics)) { - marker.updateColumn(markersTracker, newColumn); - - markersIndex++; - if (markersIndex < markersLength) { - marker = markers[markersIndex]; - } - } - - // console.log('AFTER::: markersIndex: ' + markersIndex + ' : ' + this._printMarkers()); - }; - - let finish = (delta: number, lineTextLength: number) => { - adjustDelta(Constants.MAX_SAFE_SMALL_INTEGER, delta, 1, MarkerMoveSemantics.MarkerDefined); - - // console.log('------------- FINAL MARKERS: ' + this._printMarkers()); - }; - - return { - adjustDelta: adjustDelta, - adjustSet: adjustSet, - finish: finish - }; - } - - public applyEdits(markersTracker: MarkersTracker, edits: ILineEdit[], tabSize: number): number { + public applyEdits(edits: ILineEdit[], tabSize: number): number { let deltaColumn = 0; let resultText = this.text; let tokensAdjuster = this._createTokensAdjuster(); - let markersAdjuster = this._createMarkersAdjuster(markersTracker); for (let i = 0, len = edits.length; i < len; i++) { let edit = edits[i]; // console.log(); // console.log('============================='); - // console.log('EDIT #' + i + ' [ ' + edit.startColumn + ' -> ' + edit.endColumn + ' ] : <<<' + edit.text + '>>>, forceMoveMarkers: ' + edit.forceMoveMarkers); + // console.log('EDIT #' + i + ' [ ' + edit.startColumn + ' -> ' + edit.endColumn + ' ] : <<<' + edit.text + '>>>'); // console.log('deltaColumn: ' + deltaColumn); let startColumn = deltaColumn + edit.startColumn; @@ -324,35 +109,28 @@ export abstract class AbstractModelLine { let deletingCnt = endColumn - startColumn; let insertingCnt = edit.text.length; - // Adjust tokens & markers before this edit - // console.log('Adjust tokens & markers before this edit'); + // Adjust tokens before this edit + // console.log('Adjust tokens before this edit'); tokensAdjuster.adjust(edit.startColumn - 1, deltaColumn, 1); - markersAdjuster.adjustDelta(edit.startColumn, deltaColumn, 1, edit.forceMoveMarkers ? MarkerMoveSemantics.ForceMove : (deletingCnt > 0 ? MarkerMoveSemantics.ForceStay : MarkerMoveSemantics.MarkerDefined)); - // Adjust tokens & markers for the common part of this edit + // Adjust tokens for the common part of this edit let commonLength = Math.min(deletingCnt, insertingCnt); if (commonLength > 0) { - // console.log('Adjust tokens & markers for the common part of this edit'); + // console.log('Adjust tokens for the common part of this edit'); tokensAdjuster.adjust(edit.startColumn - 1 + commonLength, deltaColumn, startColumn); - - if (!edit.forceMoveMarkers) { - markersAdjuster.adjustDelta(edit.startColumn + commonLength, deltaColumn, startColumn, edit.forceMoveMarkers ? MarkerMoveSemantics.ForceMove : (deletingCnt > insertingCnt ? MarkerMoveSemantics.ForceStay : MarkerMoveSemantics.MarkerDefined)); - } } // Perform the edit & update `deltaColumn` resultText = resultText.substring(0, startColumn - 1) + edit.text + resultText.substring(endColumn - 1); deltaColumn += insertingCnt - deletingCnt; - // Adjust tokens & markers inside this edit - // console.log('Adjust tokens & markers inside this edit'); + // Adjust tokens inside this edit + // console.log('Adjust tokens inside this edit'); tokensAdjuster.adjust(edit.endColumn, deltaColumn, startColumn); - markersAdjuster.adjustSet(edit.endColumn, startColumn + insertingCnt, edit.forceMoveMarkers ? MarkerMoveSemantics.ForceMove : MarkerMoveSemantics.MarkerDefined); } - // Wrap up tokens & markers; adjust remaining if needed + // Wrap up tokens; adjust remaining if needed tokensAdjuster.finish(deltaColumn, resultText.length); - markersAdjuster.finish(deltaColumn, resultText.length); // Save the resulting text this._setText(resultText, tabSize); @@ -360,157 +138,16 @@ export abstract class AbstractModelLine { return deltaColumn; } - public split(markersTracker: MarkersTracker, splitColumn: number, forceMoveMarkers: boolean, tabSize: number): IModelLine { - // console.log('--> split @ ' + splitColumn + '::: ' + this._printMarkers()); - var myText = this.text.substring(0, splitColumn - 1); - var otherText = this.text.substring(splitColumn - 1); - - var otherMarkers: LineMarker[] = null; - - if (this._markers) { - this._markers.sort(LineMarker.compareMarkers); - for (let i = 0, len = this._markers.length; i < len; i++) { - let marker = this._markers[i]; - - if ( - marker.position.column > splitColumn - || ( - marker.position.column === splitColumn - && ( - forceMoveMarkers - || !marker.stickToPreviousCharacter - ) - ) - ) { - let myMarkers = this._markers.slice(0, i); - otherMarkers = this._markers.slice(i); - this._markers = myMarkers; - break; - } - } - - if (otherMarkers) { - for (let i = 0, len = otherMarkers.length; i < len; i++) { - let marker = otherMarkers[i]; - - marker.updateColumn(markersTracker, marker.position.column - (splitColumn - 1)); - } - } - } + public split(splitColumn: number, tabSize: number): IModelLine { + const myText = this.text.substring(0, splitColumn - 1); + const otherText = this.text.substring(splitColumn - 1); this._setText(myText, tabSize); - - var otherLine = this._createModelLine(otherText, tabSize); - if (otherMarkers) { - otherLine.addMarkers(otherMarkers); - } - return otherLine; + return this._createModelLine(otherText, tabSize); } - public append(markersTracker: MarkersTracker, myLineNumber: number, other: IModelLine, tabSize: number): void { - // console.log('--> append: THIS :: ' + this._printMarkers()); - // console.log('--> append: OTHER :: ' + this._printMarkers()); - let thisTextLength = this.text.length; + public append(other: IModelLine, tabSize: number): void { this._setText(this.text + other.text, tabSize); - - if (other instanceof AbstractModelLine) { - if (other._markers) { - // Other has markers - let otherMarkers = other._markers; - - // Adjust other markers - for (let i = 0, len = otherMarkers.length; i < len; i++) { - let marker = otherMarkers[i]; - - marker.updatePosition(markersTracker, new Position(myLineNumber, marker.position.column + thisTextLength)); - } - - this.addMarkers(otherMarkers); - } - } - } - - public addMarker(marker: LineMarker): void { - if (!this._markers) { - this._markers = [marker]; - } else { - this._markers.push(marker); - } - } - - public addMarkers(markers: LineMarker[]): void { - if (markers.length === 0) { - return; - } - - if (!this._markers) { - this._markers = markers.slice(0); - } else { - this._markers = this._markers.concat(markers); - } - } - - public removeMarker(marker: LineMarker): void { - if (!this._markers) { - return; - } - - let index = this._indexOfMarkerId(marker.id); - if (index < 0) { - return; - } - - if (this._markers.length === 1) { - // was last marker on line - this._markers = null; - } else { - this._markers.splice(index, 1); - } - } - - public removeMarkers(deleteMarkers: { [markerId: string]: boolean; }): void { - if (!this._markers) { - return; - } - for (let i = 0, len = this._markers.length; i < len; i++) { - let marker = this._markers[i]; - - if (deleteMarkers[marker.id]) { - this._markers.splice(i, 1); - len--; - i--; - } - } - if (this._markers.length === 0) { - this._markers = null; - } - } - - public getMarkers(): LineMarker[] { - if (!this._markers) { - return null; - } - return this._markers; - } - - public updateLineNumber(markersTracker: MarkersTracker, newLineNumber: number): void { - if (this._markers) { - let markers = this._markers; - for (let i = 0, len = markers.length; i < len; i++) { - let marker = markers[i]; - marker.updateLineNumber(markersTracker, newLineNumber); - } - } - } - - private _indexOfMarkerId(markerId: string): number { - let markers = this._markers; - for (let i = 0, len = markers.length; i < len; i++) { - if (markers[i].id === markerId) { - return i; - } - } - return undefined; } } @@ -559,7 +196,7 @@ export class ModelLine extends AbstractModelLine implements IModelLine { private _lineTokens: ArrayBuffer; constructor(text: string, tabSize: number) { - super(true); + super(); this._metadata = 0; this._setText(text, tabSize); this._state = null; @@ -570,8 +207,8 @@ export class ModelLine extends AbstractModelLine implements IModelLine { return new ModelLine(text, tabSize); } - public split(markersTracker: MarkersTracker, splitColumn: number, forceMoveMarkers: boolean, tabSize: number): IModelLine { - let result = super.split(markersTracker, splitColumn, forceMoveMarkers, tabSize); + public split(splitColumn: number, tabSize: number): IModelLine { + let result = super.split(splitColumn, tabSize); // Mark overflowing tokens for deletion & delete marked tokens this._deleteMarkedTokens(this._markOverflowingTokensForDeletion(0, this.text.length)); @@ -579,10 +216,10 @@ export class ModelLine extends AbstractModelLine implements IModelLine { return result; } - public append(markersTracker: MarkersTracker, myLineNumber: number, other: IModelLine, tabSize: number): void { + public append(other: IModelLine, tabSize: number): void { let thisTextLength = this.text.length; - super.append(markersTracker, myLineNumber, other, tabSize); + super.append(other, tabSize); if (other instanceof ModelLine) { let otherRawTokens = other._lineTokens; @@ -828,7 +465,7 @@ export class MinimalModelLine extends AbstractModelLine implements IModelLine { } constructor(text: string, tabSize: number) { - super(false); + super(); this._setText(text, tabSize); } @@ -836,12 +473,12 @@ export class MinimalModelLine extends AbstractModelLine implements IModelLine { return new MinimalModelLine(text, tabSize); } - public split(markersTracker: MarkersTracker, splitColumn: number, forceMoveMarkers: boolean, tabSize: number): IModelLine { - return super.split(markersTracker, splitColumn, forceMoveMarkers, tabSize); + public split(splitColumn: number, tabSize: number): IModelLine { + return super.split(splitColumn, tabSize); } - public append(markersTracker: MarkersTracker, myLineNumber: number, other: IModelLine, tabSize: number): void { - super.append(markersTracker, myLineNumber, other, tabSize); + public append(other: IModelLine, tabSize: number): void { + super.append(other, tabSize); } // --- BEGIN STATE diff --git a/src/vs/editor/common/model/textModel.ts b/src/vs/editor/common/model/textModel.ts index 3e55882ed96..aa93b0ed106 100644 --- a/src/vs/editor/common/model/textModel.ts +++ b/src/vs/editor/common/model/textModel.ts @@ -8,6 +8,7 @@ import { OrderGuaranteeEventEmitter, BulkListenerCallback } from 'vs/base/common import * as strings from 'vs/base/common/strings'; import { Position, IPosition } from 'vs/editor/common/core/position'; import { Range, IRange } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; import * as editorCommon from 'vs/editor/common/editorCommon'; import { ModelLine, IModelLine, MinimalModelLine } from 'vs/editor/common/model/modelLine'; import { guessIndentation } from 'vs/editor/common/model/indentationGuesser'; @@ -18,8 +19,6 @@ import { TextSource, ITextSource, IRawTextSource, RawTextSource } from 'vs/edito import { IDisposable } from 'vs/base/common/lifecycle'; import * as textModelEvents from 'vs/editor/common/model/textModelEvents'; -const USE_MIMINAL_MODEL_LINE = true; - const LIMIT_FIND_COUNT = 999; export const LONG_LINE_BOUNDARY = 10000; @@ -123,7 +122,7 @@ export class TextModel implements editorCommon.ITextModel { } protected _createModelLine(text: string, tabSize: number): IModelLine { - if (USE_MIMINAL_MODEL_LINE && this._isTooLargeForTokenization) { + if (this._isTooLargeForTokenization) { return new MinimalModelLine(text, tabSize); } return new ModelLine(text, tabSize); @@ -261,22 +260,9 @@ export class TextModel implements editorCommon.ITextModel { return this._alternativeVersionId; } - private _ensureLineStarts(): void { - if (!this._lineStarts) { - const eolLength = this._EOL.length; - const linesLength = this._lines.length; - const lineStartValues = new Uint32Array(linesLength); - for (let i = 0; i < linesLength; i++) { - lineStartValues[i] = this._lines[i].text.length + eolLength; - } - this._lineStarts = new PrefixSumComputer(lineStartValues); - } - } - public getOffsetAt(rawPosition: IPosition): number { this._assertNotDisposed(); let position = this._validatePosition(rawPosition.lineNumber, rawPosition.column, false); - this._ensureLineStarts(); return this._lineStarts.getAccumulatedValue(position.lineNumber - 2) + position.column - 1; } @@ -285,7 +271,6 @@ export class TextModel implements editorCommon.ITextModel { offset = Math.floor(offset); offset = Math.max(0, offset); - this._ensureLineStarts(); let out = this._lineStarts.getIndexOf(offset); let lineLength = this._lines[out.index].text.length; @@ -525,6 +510,12 @@ export class TextModel implements editorCommon.ITextModel { return this._EOL; } + protected _onBeforeEOLChange(): void { + } + + protected _onAfterEOLChange(): void { + } + public setEOL(eol: editorCommon.EndOfLineSequence): void { this._assertNotDisposed(); const newEOL = (eol === editorCommon.EndOfLineSequence.CRLF ? '\r\n' : '\n'); @@ -538,9 +529,11 @@ export class TextModel implements editorCommon.ITextModel { const endLineNumber = this.getLineCount(); const endColumn = this.getLineMaxColumn(endLineNumber); + this._onBeforeEOLChange(); this._EOL = newEOL; - this._lineStarts = null; + this._constructLineStarts(); this._increaseVersionId(); + this._onAfterEOLChange(); this._emitModelRawContentChangedEvent( new textModelEvents.ModelRawContentChangedEvent( @@ -607,6 +600,77 @@ export class TextModel implements editorCommon.ITextModel { return lineNumber; } + /** + * Validates `range` is within buffer bounds, but allows it to sit in between surrogate pairs, etc. + * Will try to not allocate if possible. + */ + protected _validateRangeRelaxedNoAllocations(range: IRange): Range { + const linesCount = this._lines.length; + + const initialStartLineNumber = range.startLineNumber; + const initialStartColumn = range.startColumn; + let startLineNumber: number; + let startColumn: number; + + if (initialStartLineNumber < 1) { + startLineNumber = 1; + startColumn = 1; + } else if (initialStartLineNumber > linesCount) { + startLineNumber = linesCount; + startColumn = this.getLineMaxColumn(startLineNumber); + } else { + startLineNumber = initialStartLineNumber | 0; + if (initialStartColumn <= 1) { + startColumn = 1; + } else { + const maxColumn = this.getLineMaxColumn(startLineNumber); + if (initialStartColumn >= maxColumn) { + startColumn = maxColumn; + } else { + startColumn = initialStartColumn | 0; + } + } + } + + const initialEndLineNumber = range.endLineNumber; + const initialEndColumn = range.endColumn; + let endLineNumber: number; + let endColumn: number; + + if (initialEndLineNumber < 1) { + endLineNumber = 1; + endColumn = 1; + } else if (initialEndLineNumber > linesCount) { + endLineNumber = linesCount; + endColumn = this.getLineMaxColumn(endLineNumber); + } else { + endLineNumber = initialEndLineNumber | 0; + if (initialEndColumn <= 1) { + endColumn = 1; + } else { + const maxColumn = this.getLineMaxColumn(endLineNumber); + if (initialEndColumn >= maxColumn) { + endColumn = maxColumn; + } else { + endColumn = initialEndColumn | 0; + } + } + } + + if ( + initialStartLineNumber === startLineNumber + && initialStartColumn === startColumn + && initialEndLineNumber === endLineNumber + && initialEndColumn === endColumn + && range instanceof Range + && !(range instanceof Selection) + ) { + return range; + } + + return new Range(startLineNumber, startColumn, endLineNumber, endColumn); + } + /** * @param strict Do NOT allow a position inside a high-low surrogate pair */ @@ -723,7 +787,17 @@ export class TextModel implements editorCommon.ITextModel { this._mightContainNonBasicASCII = !textSource.isBasicASCII; this._EOL = textSource.EOL; this._lines = modelLines; - this._lineStarts = null; + this._constructLineStarts(); + } + + private _constructLineStarts(): void { + const eolLength = this._EOL.length; + const linesLength = this._lines.length; + const lineStartValues = new Uint32Array(linesLength); + for (let i = 0; i < linesLength; i++) { + lineStartValues[i] = this._lines[i].text.length + eolLength; + } + this._lineStarts = new PrefixSumComputer(lineStartValues); } 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 2acaf260be2..aaa66c7beab 100644 --- a/src/vs/editor/common/model/textModelEvents.ts +++ b/src/vs/editor/common/model/textModelEvents.ts @@ -88,18 +88,6 @@ export interface IModelContentChangedEvent { * An event describing that model decorations have changed. */ export interface IModelDecorationsChangedEvent { - /** - * Lists of ids for added decorations. - */ - readonly addedDecorations: string[]; - /** - * Lists of ids for changed decorations. - */ - readonly changedDecorations: string[]; - /** - * List of ids for removed decorations. - */ - readonly removedDecorations: string[]; } /** diff --git a/src/vs/editor/common/model/textModelWithDecorations.ts b/src/vs/editor/common/model/textModelWithDecorations.ts index d86288a6b2c..ff79fbfccce 100644 --- a/src/vs/editor/common/model/textModelWithDecorations.ts +++ b/src/vs/editor/common/model/textModelWithDecorations.ts @@ -10,107 +10,12 @@ import * as strings from 'vs/base/common/strings'; import { CharCode } from 'vs/base/common/charCode'; import { Range, IRange } from 'vs/editor/common/core/range'; import * as editorCommon from 'vs/editor/common/editorCommon'; -import { MarkersTracker, LineMarker } from 'vs/editor/common/model/modelLine'; -import { Position } from 'vs/editor/common/core/position'; -import { INewMarker, TextModelWithMarkers } from 'vs/editor/common/model/textModelWithMarkers'; +import { TextModelWithTokens } from 'vs/editor/common/model/textModelWithTokens'; import { LanguageIdentifier } from 'vs/editor/common/modes'; import { ITextSource, IRawTextSource } from 'vs/editor/common/model/textSource'; import * as textModelEvents from 'vs/editor/common/model/textModelEvents'; import { ThemeColor } from 'vs/platform/theme/common/themeService'; - -export const ClassName = { - EditorInfoDecoration: 'infosquiggly', - EditorWarningDecoration: 'warningsquiggly', - EditorErrorDecoration: 'errorsquiggly' -}; - -class DecorationsTracker { - - public addedDecorations: string[]; - public addedDecorationsLen: number; - public changedDecorations: string[]; - public changedDecorationsLen: number; - public removedDecorations: string[]; - public removedDecorationsLen: number; - - constructor() { - this.addedDecorations = []; - this.addedDecorationsLen = 0; - this.changedDecorations = []; - this.changedDecorationsLen = 0; - this.removedDecorations = []; - this.removedDecorationsLen = 0; - } - - // --- Build decoration events - - public addNewDecoration(id: string): void { - this.addedDecorations[this.addedDecorationsLen++] = id; - } - - public addRemovedDecoration(id: string): void { - this.removedDecorations[this.removedDecorationsLen++] = id; - } - - public addMovedDecoration(id: string): void { - this.changedDecorations[this.changedDecorationsLen++] = id; - } - - public addUpdatedDecoration(id: string): void { - this.changedDecorations[this.changedDecorationsLen++] = id; - } -} - -export class InternalDecoration implements editorCommon.IModelDecoration { - _internalDecorationBrand: void; - - public readonly id: string; - public readonly internalId: number; - public readonly ownerId: number; - public readonly startMarker: LineMarker; - public readonly endMarker: LineMarker; - public options: ModelDecorationOptions; - public isForValidation: boolean; - public range: Range; - - constructor(id: string, internalId: number, ownerId: number, range: Range, startMarker: LineMarker, endMarker: LineMarker, options: ModelDecorationOptions) { - this.id = id; - this.internalId = internalId; - this.ownerId = ownerId; - this.range = range; - this.startMarker = startMarker; - this.endMarker = endMarker; - this.setOptions(options); - } - - public setOptions(options: ModelDecorationOptions) { - this.options = options; - this.isForValidation = ( - this.options.className === ClassName.EditorErrorDecoration - || this.options.className === ClassName.EditorWarningDecoration - ); - } - - public setRange(multiLineDecorationsMap: { [key: string]: InternalDecoration; }, range: Range): void { - if (this.range.equalsRange(range)) { - return; - } - - let rangeWasMultiLine = (this.range.startLineNumber !== this.range.endLineNumber); - this.range = range; - let rangeIsMultiline = (this.range.startLineNumber !== this.range.endLineNumber); - - if (rangeWasMultiLine === rangeIsMultiline) { - return; - } - - if (rangeIsMultiline) { - multiLineDecorationsMap[this.id] = this; - } else { - delete multiLineDecorationsMap[this.id]; - } - } -} +import { IntervalNode, IntervalTree, recomputeMaxEnd, getNodeIsInOverviewRuler } from 'vs/editor/common/model/intervalTree'; let _INSTANCE_COUNT = 0; /** @@ -129,7 +34,7 @@ function nextInstanceId(): string { return String.fromCharCode(CharCode.A + result - LETTERS_CNT); } -export class TextModelWithDecorations extends TextModelWithMarkers implements editorCommon.ITextModelWithDecorations { +export class TextModelWithDecorations extends TextModelWithTokens implements editorCommon.ITextModelWithDecorations { /** * Used to workaround broken clients that might attempt using a decoration id generated by a different model. @@ -137,39 +42,26 @@ export class TextModelWithDecorations extends TextModelWithMarkers implements ed */ private readonly _instanceId: string; private _lastDecorationId: number; - - private _currentDecorationsTracker: DecorationsTracker; private _currentDecorationsTrackerCnt: number; - - private _currentMarkersTracker: MarkersTracker; - private _currentMarkersTrackerCnt: number; - - private _decorations: { [decorationId: string]: InternalDecoration; }; - private _internalDecorations: { [internalDecorationId: number]: InternalDecoration; }; - private _multiLineDecorationsMap: { [key: string]: InternalDecoration; }; + private _currentDecorationsTrackerDidChange: boolean; + private _decorations: { [decorationId: string]: IntervalNode; }; + private _decorationsTree: DecorationsTrees; constructor(rawTextSource: IRawTextSource, creationOptions: editorCommon.ITextModelCreationOptions, languageIdentifier: LanguageIdentifier) { super(rawTextSource, creationOptions, languageIdentifier); this._instanceId = nextInstanceId(); this._lastDecorationId = 0; - - // Initialize decorations - this._currentDecorationsTracker = null; this._currentDecorationsTrackerCnt = 0; - - this._currentMarkersTracker = null; - this._currentMarkersTrackerCnt = 0; - + this._currentDecorationsTrackerDidChange = false; this._decorations = Object.create(null); - this._internalDecorations = Object.create(null); - this._multiLineDecorationsMap = Object.create(null); + this._decorationsTree = new DecorationsTrees(); } public dispose(): void { this._decorations = null; - this._internalDecorations = null; - this._multiLineDecorationsMap = null; + this._decorationsTree = null; + super.dispose(); } @@ -178,59 +70,108 @@ export class TextModelWithDecorations extends TextModelWithMarkers implements ed // Destroy all my decorations this._decorations = Object.create(null); - this._internalDecorations = Object.create(null); - this._multiLineDecorationsMap = Object.create(null); - } - - private static _shouldStartMarkerSticksToPreviousCharacter(stickiness: editorCommon.TrackedRangeStickiness): boolean { - if (stickiness === editorCommon.TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges || stickiness === editorCommon.TrackedRangeStickiness.GrowsOnlyWhenTypingBefore) { - return true; - } - return false; - } - - private static _shouldEndMarkerSticksToPreviousCharacter(stickiness: editorCommon.TrackedRangeStickiness): boolean { - if (stickiness === editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges || stickiness === editorCommon.TrackedRangeStickiness.GrowsOnlyWhenTypingBefore) { - return true; - } - return false; + this._decorationsTree = new DecorationsTrees(); } _getTrackedRangesCount(): number { - return Object.keys(this._decorations).length; + return this._decorationsTree.count(); } // --- END TrackedRanges + protected _acquireDecorationsTracker(): void { + if (this._currentDecorationsTrackerCnt === 0) { + this._currentDecorationsTrackerDidChange = false; + } + this._currentDecorationsTrackerCnt++; + } + + protected _releaseDecorationsTracker(): void { + this._currentDecorationsTrackerCnt--; + if (this._currentDecorationsTrackerCnt === 0) { + if (this._currentDecorationsTrackerDidChange) { + this._emitModelDecorationsChangedEvent(); + } + } + } + + protected _adjustDecorationsForEdit(offset: number, length: number, textLength: number, forceMoveMarkers: boolean): void { + this._currentDecorationsTrackerDidChange = true; + this._decorationsTree.acceptReplace(offset, length, textLength, forceMoveMarkers); + } + + protected _onBeforeEOLChange(): void { + super._onBeforeEOLChange(); + + // Ensure all decorations get their `range` set. + const versionId = this.getVersionId(); + const allDecorations = this._decorationsTree.search(0, false, false, versionId); + this._ensureNodesHaveRanges(allDecorations); + } + + protected _onAfterEOLChange(): void { + super._onAfterEOLChange(); + + // Transform back `range` to offsets + const versionId = this.getVersionId(); + const allDecorations = this._decorationsTree.collectNodesPostOrder(); + for (let i = 0, len = allDecorations.length; i < len; i++) { + const node = allDecorations[i]; + + const delta = node.cachedAbsoluteStart - node.start; + + const startOffset = this._lineStarts.getAccumulatedValue(node.range.startLineNumber - 2) + node.range.startColumn - 1; + const endOffset = this._lineStarts.getAccumulatedValue(node.range.endLineNumber - 2) + node.range.endColumn - 1; + + node.cachedAbsoluteStart = startOffset; + node.cachedAbsoluteEnd = endOffset; + node.cachedVersionId = versionId; + + node.start = startOffset - delta; + node.end = endOffset - delta; + + recomputeMaxEnd(node); + } + } + public changeDecorations(callback: (changeAccessor: editorCommon.IModelDecorationsChangeAccessor) => T, ownerId: number = 0): T { this._assertNotDisposed(); try { this._eventEmitter.beginDeferredEmit(); - let decorationsTracker = this._acquireDecorationsTracker(); - return this._changeDecorations(decorationsTracker, ownerId, callback); + this._acquireDecorationsTracker(); + return this._changeDecorations(ownerId, callback); } finally { this._releaseDecorationsTracker(); this._eventEmitter.endDeferredEmit(); } } - private _changeDecorations(decorationsTracker: DecorationsTracker, ownerId: number, callback: (changeAccessor: editorCommon.IModelDecorationsChangeAccessor) => T): T { + private _changeDecorations(ownerId: number, callback: (changeAccessor: editorCommon.IModelDecorationsChangeAccessor) => T): T { let changeAccessor: editorCommon.IModelDecorationsChangeAccessor = { addDecoration: (range: IRange, options: editorCommon.IModelDecorationOptions): string => { - return this._addDecorationImpl(decorationsTracker, ownerId, this.validateRange(range), _normalizeOptions(options)); + this._currentDecorationsTrackerDidChange = true; + return this._deltaDecorationsImpl(ownerId, [], [{ range: range, options: options }])[0]; }, changeDecoration: (id: string, newRange: IRange): void => { - this._changeDecorationImpl(decorationsTracker, id, this.validateRange(newRange)); + this._currentDecorationsTrackerDidChange = true; + this._changeDecorationImpl(id, newRange); }, changeDecorationOptions: (id: string, options: editorCommon.IModelDecorationOptions) => { - this._changeDecorationOptionsImpl(decorationsTracker, id, _normalizeOptions(options)); + this._currentDecorationsTrackerDidChange = true; + this._changeDecorationOptionsImpl(id, _normalizeOptions(options)); }, removeDecoration: (id: string): void => { - this._removeDecorationImpl(decorationsTracker, id); + this._currentDecorationsTrackerDidChange = true; + this._deltaDecorationsImpl(ownerId, [id], []); }, deltaDecorations: (oldDecorations: string[], newDecorations: editorCommon.IModelDeltaDecoration[]): string[] => { - return this._deltaDecorationsImpl(decorationsTracker, ownerId, oldDecorations, this._normalizeDeltaDecorations(newDecorations)); + if (oldDecorations.length === 0 && newDecorations.length === 0) { + // nothing to do + return []; + } + this._currentDecorationsTrackerDidChange = true; + return this._deltaDecorationsImpl(ownerId, oldDecorations, newDecorations); } }; let result: T = null; @@ -252,41 +193,90 @@ export class TextModelWithDecorations extends TextModelWithMarkers implements ed if (!oldDecorations) { oldDecorations = []; } - return this.changeDecorations((changeAccessor) => { - return changeAccessor.deltaDecorations(oldDecorations, newDecorations); - }, ownerId); + if (oldDecorations.length === 0 && newDecorations.length === 0) { + // nothing to do + return []; + } + + try { + this._eventEmitter.beginDeferredEmit(); + this._acquireDecorationsTracker(); + this._currentDecorationsTrackerDidChange = true; + return this._deltaDecorationsImpl(ownerId, oldDecorations, newDecorations); + } finally { + this._releaseDecorationsTracker(); + this._eventEmitter.endDeferredEmit(); + } + } + + _getTrackedRange(id: string): Range { + return this.getDecorationRange(id); + } + + _setTrackedRange(id: string, newRange: Range, newStickiness: editorCommon.TrackedRangeStickiness): string { + const node = (id ? this._decorations[id] : null); + + if (!node) { + if (!newRange) { + // node doesn't exist, the request is to delete => nothing to do + return null; + } + // node doesn't exist, the request is to set => add the tracked range + return this._deltaDecorationsImpl(0, [], [{ range: newRange, options: TRACKED_RANGE_OPTIONS[newStickiness] }])[0]; + } + + if (!newRange) { + // node exists, the request is to delete => delete node + this._decorationsTree.delete(node); + delete this._decorations[node.id]; + return null; + } + + // node exists, the request is to set => change the tracked range and its options + const range = this._validateRangeRelaxedNoAllocations(newRange); + const startOffset = this._lineStarts.getAccumulatedValue(range.startLineNumber - 2) + range.startColumn - 1; + const endOffset = this._lineStarts.getAccumulatedValue(range.endLineNumber - 2) + range.endColumn - 1; + this._decorationsTree.delete(node); + node.reset(this.getVersionId(), startOffset, endOffset, range); + node.setOptions(TRACKED_RANGE_OPTIONS[newStickiness]); + this._decorationsTree.insert(node); + return node.id; } public removeAllDecorationsWithOwnerId(ownerId: number): void { - let toRemove: string[] = []; - - for (let decorationId in this._decorations) { - // No `hasOwnProperty` call due to using Object.create(null) - - let decoration = this._decorations[decorationId]; - - if (decoration.ownerId === ownerId) { - toRemove.push(decoration.id); - } + if (this._isDisposed) { + return; } + const nodes = this._decorationsTree.collectNodesFromOwner(ownerId); + for (let i = 0, len = nodes.length; i < len; i++) { + const node = nodes[i]; - this._removeDecorationsImpl(null, toRemove); + this._decorationsTree.delete(node); + delete this._decorations[node.id]; + } } public getDecorationOptions(decorationId: string): editorCommon.IModelDecorationOptions { - let decoration = this._decorations[decorationId]; - if (!decoration) { + const node = this._decorations[decorationId]; + if (!node) { return null; } - return decoration.options; + return node.options; } public getDecorationRange(decorationId: string): Range { - let decoration = this._decorations[decorationId]; - if (!decoration) { + const node = this._decorations[decorationId]; + if (!node) { return null; } - return decoration.range; + const versionId = this.getVersionId(); + if (node.cachedVersionId !== versionId) { + this._decorationsTree.resolveNode(node, versionId); + } + if (node.range === null) { + node.range = this._getRangeAt(node.cachedAbsoluteStart, node.cachedAbsoluteEnd); + } + return node.range; } public getLineDecorations(lineNumber: number, ownerId: number = 0, filterOutValidation: boolean = false): editorCommon.IModelDecoration[] { @@ -297,116 +287,6 @@ export class TextModelWithDecorations extends TextModelWithMarkers implements ed return this.getLinesDecorations(lineNumber, lineNumber, ownerId, filterOutValidation); } - /** - * Fetch only multi-line decorations that intersect with the given line number range - */ - private _getMultiLineDecorations(filterRange: Range, filterOwnerId: number, filterOutValidation: boolean): InternalDecoration[] { - const filterStartLineNumber = filterRange.startLineNumber; - const filterStartColumn = filterRange.startColumn; - const filterEndLineNumber = filterRange.endLineNumber; - const filterEndColumn = filterRange.endColumn; - - let result: InternalDecoration[] = [], resultLen = 0; - - for (let decorationId in this._multiLineDecorationsMap) { - // No `hasOwnProperty` call due to using Object.create(null) - let decoration = this._multiLineDecorationsMap[decorationId]; - - if (filterOwnerId && decoration.ownerId && decoration.ownerId !== filterOwnerId) { - continue; - } - - if (filterOutValidation && decoration.isForValidation) { - continue; - } - - let range = decoration.range; - - if (range.startLineNumber > filterEndLineNumber) { - continue; - } - if (range.startLineNumber === filterEndLineNumber && range.startColumn > filterEndColumn) { - continue; - } - if (range.endLineNumber < filterStartLineNumber) { - continue; - } - if (range.endLineNumber === filterStartLineNumber && range.endColumn < filterStartColumn) { - continue; - } - - result[resultLen++] = decoration; - } - - return result; - } - - private _getDecorationsInRange(filterRange: Range, filterOwnerId: number, filterOutValidation: boolean): InternalDecoration[] { - const filterStartLineNumber = filterRange.startLineNumber; - const filterStartColumn = filterRange.startColumn; - const filterEndLineNumber = filterRange.endLineNumber; - const filterEndColumn = filterRange.endColumn; - - let result = this._getMultiLineDecorations(filterRange, filterOwnerId, filterOutValidation); - let resultLen = result.length; - let resultMap: { [decorationId: string]: boolean; } = {}; - - for (let i = 0, len = resultLen; i < len; i++) { - resultMap[result[i].id] = true; - } - - for (let lineNumber = filterStartLineNumber; lineNumber <= filterEndLineNumber; lineNumber++) { - let lineMarkers = this._lines[lineNumber - 1].getMarkers(); - if (lineMarkers === null) { - continue; - } - for (let i = 0, len = lineMarkers.length; i < len; i++) { - let lineMarker = lineMarkers[i]; - let internalDecorationId = lineMarker.internalDecorationId; - - if (internalDecorationId === 0) { - // marker does not belong to any decoration - continue; - } - - let decoration = this._internalDecorations[internalDecorationId]; - - if (resultMap.hasOwnProperty(decoration.id)) { - // decoration already in result - continue; - } - - if (filterOwnerId && decoration.ownerId && decoration.ownerId !== filterOwnerId) { - continue; - } - - if (filterOutValidation && decoration.isForValidation) { - continue; - } - - let range = decoration.range; - - if (range.startLineNumber > filterEndLineNumber) { - continue; - } - if (range.startLineNumber === filterEndLineNumber && range.startColumn > filterEndColumn) { - continue; - } - if (range.endLineNumber < filterStartLineNumber) { - continue; - } - if (range.endLineNumber === filterStartLineNumber && range.endColumn < filterStartColumn) { - continue; - } - - result[resultLen++] = decoration; - resultMap[decoration.id] = true; - } - } - - return result; - } - public getLinesDecorations(_startLineNumber: number, _endLineNumber: number, ownerId: number = 0, filterOutValidation: boolean = false): editorCommon.IModelDecoration[] { let lineCount = this.getLineCount(); let startLineNumber = Math.min(lineCount, Math.max(1, _startLineNumber)); @@ -415,422 +295,150 @@ export class TextModelWithDecorations extends TextModelWithMarkers implements ed return this._getDecorationsInRange(new Range(startLineNumber, 1, endLineNumber, endColumn), ownerId, filterOutValidation); } - public getDecorationsInRange(range: IRange, ownerId?: number, filterOutValidation?: boolean): editorCommon.IModelDecoration[] { + public getDecorationsInRange(range: IRange, ownerId: number = 0, filterOutValidation: boolean = false): editorCommon.IModelDecoration[] { let validatedRange = this.validateRange(range); return this._getDecorationsInRange(validatedRange, ownerId, filterOutValidation); } + public getOverviewRulerDecorations(ownerId: number = 0, filterOutValidation: boolean = false): editorCommon.IModelDecoration[] { + const versionId = this.getVersionId(); + const result = this._decorationsTree.search(ownerId, filterOutValidation, true, versionId); + return this._ensureNodesHaveRanges(result); + } + public getAllDecorations(ownerId: number = 0, filterOutValidation: boolean = false): editorCommon.IModelDecoration[] { - let result: InternalDecoration[] = [], resultLen = 0; - - for (let decorationId in this._decorations) { - // No `hasOwnProperty` call due to using Object.create(null) - let decoration = this._decorations[decorationId]; - - if (ownerId && decoration.ownerId && decoration.ownerId !== ownerId) { - continue; - } - - if (filterOutValidation && decoration.isForValidation) { - continue; - } - - result[resultLen++] = decoration; - } - - return result; + const versionId = this.getVersionId(); + const result = this._decorationsTree.search(ownerId, filterOutValidation, false, versionId); + return this._ensureNodesHaveRanges(result); } - protected _acquireMarkersTracker(): MarkersTracker { - if (this._currentMarkersTrackerCnt === 0) { - this._currentMarkersTracker = new MarkersTracker(); - } - this._currentMarkersTrackerCnt++; - return this._currentMarkersTracker; - } - - protected _releaseMarkersTracker(): void { - this._currentMarkersTrackerCnt--; - if (this._currentMarkersTrackerCnt === 0) { - let markersTracker = this._currentMarkersTracker; - this._currentMarkersTracker = null; - this._handleTrackedMarkers(markersTracker); - } - } - - /** - * Handle changed markers (i.e. update decorations ranges and return the changed decorations, unique and sorted by id) - */ - private _handleTrackedMarkers(markersTracker: MarkersTracker): void { - let changedInternalDecorationIds = markersTracker.getDecorationIds(); - if (changedInternalDecorationIds.length === 0) { - return; - } - - changedInternalDecorationIds.sort(); - - let uniqueChangedDecorations: string[] = [], uniqueChangedDecorationsLen = 0; - let previousInternalDecorationId: number = 0; - for (let i = 0, len = changedInternalDecorationIds.length; i < len; i++) { - let internalDecorationId = changedInternalDecorationIds[i]; - if (internalDecorationId === previousInternalDecorationId) { - continue; - } - previousInternalDecorationId = internalDecorationId; - - let decoration = this._internalDecorations[internalDecorationId]; - if (!decoration) { - // perhaps the decoration was removed in the meantime - continue; - } - - let startMarker = decoration.startMarker.position; - let endMarker = decoration.endMarker.position; - let range = TextModelWithDecorations._createRangeFromMarkers(startMarker, endMarker); - decoration.setRange(this._multiLineDecorationsMap, range); - - uniqueChangedDecorations[uniqueChangedDecorationsLen++] = decoration.id; - } - - if (uniqueChangedDecorations.length > 0) { - let e: textModelEvents.IModelDecorationsChangedEvent = { - addedDecorations: [], - changedDecorations: uniqueChangedDecorations, - removedDecorations: [] - }; - this.emitModelDecorationsChangedEvent(e); - } - } - - private static _createRangeFromMarkers(startPosition: Position, endPosition: Position): Range { - if (endPosition.isBefore(startPosition)) { - // This tracked range has turned in on itself (end marker before start marker) - // This can happen in extreme editing conditions where lots of text is removed and lots is added - - // Treat it as a collapsed range - return new Range(startPosition.lineNumber, startPosition.column, startPosition.lineNumber, startPosition.column); - } - return new Range(startPosition.lineNumber, startPosition.column, endPosition.lineNumber, endPosition.column); - } - - private _acquireDecorationsTracker(): DecorationsTracker { - if (this._currentDecorationsTrackerCnt === 0) { - this._currentDecorationsTracker = new DecorationsTracker(); - } - this._currentDecorationsTrackerCnt++; - return this._currentDecorationsTracker; - } - - private _releaseDecorationsTracker(): void { - this._currentDecorationsTrackerCnt--; - if (this._currentDecorationsTrackerCnt === 0) { - let decorationsTracker = this._currentDecorationsTracker; - this._currentDecorationsTracker = null; - this._handleTrackedDecorations(decorationsTracker); - } - } - - private _handleTrackedDecorations(decorationsTracker: DecorationsTracker): void { - if ( - decorationsTracker.addedDecorationsLen === 0 - && decorationsTracker.changedDecorationsLen === 0 - && decorationsTracker.removedDecorationsLen === 0 - ) { - return; - } - - let e: textModelEvents.IModelDecorationsChangedEvent = { - addedDecorations: decorationsTracker.addedDecorations, - changedDecorations: decorationsTracker.changedDecorations, - removedDecorations: decorationsTracker.removedDecorations - }; - this.emitModelDecorationsChangedEvent(e); - } - - private emitModelDecorationsChangedEvent(e: textModelEvents.IModelDecorationsChangedEvent): void { + private _emitModelDecorationsChangedEvent(): void { if (!this._isDisposing) { + let e: textModelEvents.IModelDecorationsChangedEvent = {}; this._eventEmitter.emit(textModelEvents.TextModelEventType.ModelDecorationsChanged, e); } } - private _normalizeDeltaDecorations(deltaDecorations: editorCommon.IModelDeltaDecoration[]): ModelDeltaDecoration[] { - let result: ModelDeltaDecoration[] = []; - for (let i = 0, len = deltaDecorations.length; i < len; i++) { - let deltaDecoration = deltaDecorations[i]; - result.push(new ModelDeltaDecoration(i, this.validateRange(deltaDecoration.range), _normalizeOptions(deltaDecoration.options))); - } - return result; + private _getDecorationsInRange(filterRange: Range, filterOwnerId: number, filterOutValidation: boolean): IntervalNode[] { + const startOffset = this._lineStarts.getAccumulatedValue(filterRange.startLineNumber - 2) + filterRange.startColumn - 1; + const endOffset = this._lineStarts.getAccumulatedValue(filterRange.endLineNumber - 2) + filterRange.endColumn - 1; + + const versionId = this.getVersionId(); + const result = this._decorationsTree.intervalSearch(startOffset, endOffset, filterOwnerId, filterOutValidation, versionId); + + return this._ensureNodesHaveRanges(result); } - private _externalDecorationId(internalId: number): string { - return `${this._instanceId};${internalId}`; - } - - private _addDecorationImpl(decorationsTracker: DecorationsTracker, ownerId: number, _range: Range, options: ModelDecorationOptions): string { - let range = this.validateRange(_range); - - let internalDecorationId = (++this._lastDecorationId); - let decorationId = this._externalDecorationId(internalDecorationId); - - let markers = this._addMarkers([ - { - internalDecorationId: internalDecorationId, - position: new Position(range.startLineNumber, range.startColumn), - stickToPreviousCharacter: TextModelWithDecorations._shouldStartMarkerSticksToPreviousCharacter(options.stickiness) - }, - { - internalDecorationId: internalDecorationId, - position: new Position(range.endLineNumber, range.endColumn), - stickToPreviousCharacter: TextModelWithDecorations._shouldEndMarkerSticksToPreviousCharacter(options.stickiness) + private _ensureNodesHaveRanges(nodes: IntervalNode[]): IntervalNode[] { + for (let i = 0, len = nodes.length; i < len; i++) { + const node = nodes[i]; + if (node.range === null) { + node.range = this._getRangeAt(node.cachedAbsoluteStart, node.cachedAbsoluteEnd); } - ]); - - let decoration = new InternalDecoration(decorationId, internalDecorationId, ownerId, range, markers[0], markers[1], options); - this._decorations[decorationId] = decoration; - this._internalDecorations[internalDecorationId] = decoration; - if (range.startLineNumber !== range.endLineNumber) { - this._multiLineDecorationsMap[decorationId] = decoration; } - - decorationsTracker.addNewDecoration(decorationId); - - return decorationId; + return nodes; } - private _addDecorationsImpl(decorationsTracker: DecorationsTracker, ownerId: number, newDecorations: ModelDeltaDecoration[]): string[] { - let internalDecorationIds: number[] = []; - let decorationIds: string[] = []; - let newMarkers: INewMarker[] = []; + private _getRangeAt(start: number, end: number): Range { + const startResult = this._lineStarts.getIndexOf(start); + const startLineLength = this._lines[startResult.index].text.length; + const startColumn = Math.min(startResult.remainder + 1, startLineLength + 1); - for (let i = 0, len = newDecorations.length; i < len; i++) { - let newDecoration = newDecorations[i]; - let range = newDecoration.range; - let stickiness = newDecoration.options.stickiness; + const endResult = this._lineStarts.getIndexOf(end); + const endLineLength = this._lines[endResult.index].text.length; + const endColumn = Math.min(endResult.remainder + 1, endLineLength + 1); - let internalDecorationId = (++this._lastDecorationId); - let decorationId = this._externalDecorationId(internalDecorationId); - - internalDecorationIds[i] = internalDecorationId; - decorationIds[i] = decorationId; - - newMarkers[2 * i] = { - internalDecorationId: internalDecorationId, - position: new Position(range.startLineNumber, range.startColumn), - stickToPreviousCharacter: TextModelWithDecorations._shouldStartMarkerSticksToPreviousCharacter(stickiness) - }; - - newMarkers[2 * i + 1] = { - internalDecorationId: internalDecorationId, - position: new Position(range.endLineNumber, range.endColumn), - stickToPreviousCharacter: TextModelWithDecorations._shouldEndMarkerSticksToPreviousCharacter(stickiness) - }; - } - - let markerIds = this._addMarkers(newMarkers); - - for (let i = 0, len = newDecorations.length; i < len; i++) { - let newDecoration = newDecorations[i]; - let range = newDecoration.range; - let internalDecorationId = internalDecorationIds[i]; - let decorationId = decorationIds[i]; - let startMarker = markerIds[2 * i]; - let endMarker = markerIds[2 * i + 1]; - - let decoration = new InternalDecoration(decorationId, internalDecorationId, ownerId, range, startMarker, endMarker, newDecoration.options); - this._decorations[decorationId] = decoration; - this._internalDecorations[internalDecorationId] = decoration; - if (range.startLineNumber !== range.endLineNumber) { - this._multiLineDecorationsMap[decorationId] = decoration; - } - - decorationsTracker.addNewDecoration(decorationId); - } - - return decorationIds; + return new Range(startResult.index + 1, startColumn, endResult.index + 1, endColumn); } - private _changeDecorationImpl(decorationsTracker: DecorationsTracker, decorationId: string, newRange: Range): void { - let decoration = this._decorations[decorationId]; - if (!decoration) { + private _changeDecorationImpl(decorationId: string, _range: IRange): void { + const node = this._decorations[decorationId]; + if (!node) { + return; + } + const range = this._validateRangeRelaxedNoAllocations(_range); + const startOffset = this._lineStarts.getAccumulatedValue(range.startLineNumber - 2) + range.startColumn - 1; + const endOffset = this._lineStarts.getAccumulatedValue(range.endLineNumber - 2) + range.endColumn - 1; + + this._decorationsTree.delete(node); + node.reset(this.getVersionId(), startOffset, endOffset, range); + this._decorationsTree.insert(node); + } + + private _changeDecorationOptionsImpl(decorationId: string, options: ModelDecorationOptions): void { + const node = this._decorations[decorationId]; + if (!node) { return; } - let startMarker = decoration.startMarker; - if (newRange.startLineNumber !== startMarker.position.lineNumber) { - // move marker between lines - this._lines[startMarker.position.lineNumber - 1].removeMarker(startMarker); - this._lines[newRange.startLineNumber - 1].addMarker(startMarker); - } - startMarker.setPosition(new Position(newRange.startLineNumber, newRange.startColumn)); + const nodeWasInOverviewRuler = (node.options.overviewRuler.color ? true : false); + const nodeIsInOverviewRuler = (options.overviewRuler.color ? true : false); - let endMarker = decoration.endMarker; - if (newRange.endLineNumber !== endMarker.position.lineNumber) { - // move marker between lines - this._lines[endMarker.position.lineNumber - 1].removeMarker(endMarker); - this._lines[newRange.endLineNumber - 1].addMarker(endMarker); - } - endMarker.setPosition(new Position(newRange.endLineNumber, newRange.endColumn)); - - decoration.setRange(this._multiLineDecorationsMap, newRange); - - decorationsTracker.addMovedDecoration(decorationId); - } - - private _changeDecorationOptionsImpl(decorationsTracker: DecorationsTracker, decorationId: string, options: ModelDecorationOptions): void { - let decoration = this._decorations[decorationId]; - if (!decoration) { - return; - } - - if (decoration.options.stickiness !== options.stickiness) { - decoration.startMarker.stickToPreviousCharacter = TextModelWithDecorations._shouldStartMarkerSticksToPreviousCharacter(options.stickiness); - decoration.endMarker.stickToPreviousCharacter = TextModelWithDecorations._shouldEndMarkerSticksToPreviousCharacter(options.stickiness); - } - - decoration.setOptions(options); - - decorationsTracker.addUpdatedDecoration(decorationId); - } - - private _removeDecorationImpl(decorationsTracker: DecorationsTracker, decorationId: string): void { - let decoration = this._decorations[decorationId]; - if (!decoration) { - return; - } - - this._removeMarkers([decoration.startMarker, decoration.endMarker]); - - delete this._multiLineDecorationsMap[decorationId]; - delete this._decorations[decorationId]; - delete this._internalDecorations[decoration.internalId]; - - if (decorationsTracker) { - decorationsTracker.addRemovedDecoration(decorationId); + if (nodeWasInOverviewRuler !== nodeIsInOverviewRuler) { + // Delete + Insert due to an overview ruler status change + this._decorationsTree.delete(node); + node.setOptions(options); + this._decorationsTree.insert(node); + } else { + node.setOptions(options); } } - private _removeDecorationsImpl(decorationsTracker: DecorationsTracker, decorationIds: string[]): void { - let removeMarkers: LineMarker[] = [], removeMarkersLen = 0; + private _deltaDecorationsImpl(ownerId: number, oldDecorationsIds: string[], newDecorations: editorCommon.IModelDeltaDecoration[]): string[] { + const versionId = this.getVersionId(); - for (let i = 0, len = decorationIds.length; i < len; i++) { - let decorationId = decorationIds[i]; - let decoration = this._decorations[decorationId]; - if (!decoration) { - continue; + const oldDecorationsLen = oldDecorationsIds.length; + let oldDecorationIndex = 0; + + const newDecorationsLen = newDecorations.length; + let newDecorationIndex = 0; + + let result = new Array(newDecorationsLen); + while (oldDecorationIndex < oldDecorationsLen || newDecorationIndex < newDecorationsLen) { + + let node: IntervalNode = null; + + if (oldDecorationIndex < oldDecorationsLen) { + // (1) get ourselves an old node + do { + node = this._decorations[oldDecorationsIds[oldDecorationIndex++]]; + } while (!node && oldDecorationIndex < oldDecorationsLen); + + // (2) remove the node from the tree (if it exists) + if (node) { + this._decorationsTree.delete(node); + } } - if (decorationsTracker) { - decorationsTracker.addRemovedDecoration(decorationId); - } + if (newDecorationIndex < newDecorationsLen) { + // (3) create a new node if necessary + if (!node) { + const internalDecorationId = (++this._lastDecorationId); + const decorationId = `${this._instanceId};${internalDecorationId}`; + node = new IntervalNode(decorationId, 0, 0); + this._decorations[decorationId] = node; + } - removeMarkers[removeMarkersLen++] = decoration.startMarker; - removeMarkers[removeMarkersLen++] = decoration.endMarker; - delete this._multiLineDecorationsMap[decorationId]; - delete this._decorations[decorationId]; - delete this._internalDecorations[decoration.internalId]; - } + // (4) initialize node + const newDecoration = newDecorations[newDecorationIndex]; + const range = this._validateRangeRelaxedNoAllocations(newDecoration.range); + const options = _normalizeOptions(newDecoration.options); + const startOffset = this._lineStarts.getAccumulatedValue(range.startLineNumber - 2) + range.startColumn - 1; + const endOffset = this._lineStarts.getAccumulatedValue(range.endLineNumber - 2) + range.endColumn - 1; - if (removeMarkers.length > 0) { - this._removeMarkers(removeMarkers); - } - } + node.ownerId = ownerId; + node.reset(versionId, startOffset, endOffset, range); + node.setOptions(options); - private _resolveOldDecorations(oldDecorations: string[]): InternalDecoration[] { - let result: InternalDecoration[] = []; - for (let i = 0, len = oldDecorations.length; i < len; i++) { - let id = oldDecorations[i]; - let decoration = this._decorations[id]; - if (!decoration) { - continue; - } + this._decorationsTree.insert(node); - result.push(decoration); - } - return result; - } + result[newDecorationIndex] = node.id; - private _deltaDecorationsImpl(decorationsTracker: DecorationsTracker, ownerId: number, oldDecorationsIds: string[], newDecorations: ModelDeltaDecoration[]): string[] { - - if (oldDecorationsIds.length === 0) { - // Nothing to remove - return this._addDecorationsImpl(decorationsTracker, ownerId, newDecorations); - } - - if (newDecorations.length === 0) { - // Nothing to add - this._removeDecorationsImpl(decorationsTracker, oldDecorationsIds); - return []; - } - - let oldDecorations = this._resolveOldDecorations(oldDecorationsIds); - - oldDecorations.sort((a, b) => Range.compareRangesUsingStarts(a.range, b.range)); - newDecorations.sort((a, b) => Range.compareRangesUsingStarts(a.range, b.range)); - - let result: string[] = [], - oldDecorationsIndex = 0, - oldDecorationsLength = oldDecorations.length, - newDecorationsIndex = 0, - newDecorationsLength = newDecorations.length, - decorationsToAdd: ModelDeltaDecoration[] = [], - decorationsToRemove: string[] = []; - - while (oldDecorationsIndex < oldDecorationsLength && newDecorationsIndex < newDecorationsLength) { - let oldDecoration = oldDecorations[oldDecorationsIndex]; - let newDecoration = newDecorations[newDecorationsIndex]; - let comparison = Range.compareRangesUsingStarts(oldDecoration.range, newDecoration.range); - - if (comparison < 0) { - // `oldDecoration` is before `newDecoration` => remove `oldDecoration` - decorationsToRemove.push(oldDecoration.id); - oldDecorationsIndex++; - continue; - } - - if (comparison > 0) { - // `newDecoration` is before `oldDecoration` => add `newDecoration` - decorationsToAdd.push(newDecoration); - newDecorationsIndex++; - continue; - } - - // The ranges of `oldDecoration` and `newDecoration` are equal - - if (!oldDecoration.options.equals(newDecoration.options)) { - // The options do not match => remove `oldDecoration` - decorationsToRemove.push(oldDecoration.id); - oldDecorationsIndex++; - continue; - } - - // Bingo! We can reuse `oldDecoration` for `newDecoration` - result[newDecoration.index] = oldDecoration.id; - oldDecorationsIndex++; - newDecorationsIndex++; - } - - while (oldDecorationsIndex < oldDecorationsLength) { - // No more new decorations => remove decoration at `oldDecorationsIndex` - decorationsToRemove.push(oldDecorations[oldDecorationsIndex].id); - oldDecorationsIndex++; - } - - while (newDecorationsIndex < newDecorationsLength) { - // No more old decorations => add decoration at `newDecorationsIndex` - decorationsToAdd.push(newDecorations[newDecorationsIndex]); - newDecorationsIndex++; - } - - // Remove `decorationsToRemove` - if (decorationsToRemove.length > 0) { - this._removeDecorationsImpl(decorationsTracker, decorationsToRemove); - } - - // Add `decorationsToAdd` - if (decorationsToAdd.length > 0) { - let newIds = this._addDecorationsImpl(decorationsTracker, ownerId, decorationsToAdd); - for (let i = 0, len = decorationsToAdd.length; i < len; i++) { - result[decorationsToAdd[i].index] = newIds[i]; + newDecorationIndex++; + } else { + if (node) { + delete this._decorations[node.id]; + } } } @@ -838,6 +446,87 @@ export class TextModelWithDecorations extends TextModelWithMarkers implements ed } } +class DecorationsTrees { + + /** + * This tree holds decorations that do not show up in the overview ruler. + */ + private _decorationsTree0: IntervalTree; + + /** + * This tree holds decorations that show up in the overview ruler. + */ + private _decorationsTree1: IntervalTree; + + constructor() { + this._decorationsTree0 = new IntervalTree(); + this._decorationsTree1 = new IntervalTree(); + } + + public intervalSearch(start: number, end: number, filterOwnerId: number, filterOutValidation: boolean, cachedVersionId: number): IntervalNode[] { + const r0 = this._decorationsTree0.intervalSearch(start, end, filterOwnerId, filterOutValidation, cachedVersionId); + const r1 = this._decorationsTree1.intervalSearch(start, end, filterOwnerId, filterOutValidation, cachedVersionId); + return r0.concat(r1); + } + + public search(filterOwnerId: number, filterOutValidation: boolean, overviewRulerOnly: boolean, cachedVersionId: number): IntervalNode[] { + if (overviewRulerOnly) { + return this._decorationsTree1.search(filterOwnerId, filterOutValidation, cachedVersionId); + } else { + const r0 = this._decorationsTree0.search(filterOwnerId, filterOutValidation, cachedVersionId); + const r1 = this._decorationsTree1.search(filterOwnerId, filterOutValidation, cachedVersionId); + return r0.concat(r1); + } + } + + public count(): number { + const c0 = this._decorationsTree0.count(); + const c1 = this._decorationsTree1.count(); + return c0 + c1; + } + + public collectNodesFromOwner(ownerId: number): IntervalNode[] { + const r0 = this._decorationsTree0.collectNodesFromOwner(ownerId); + const r1 = this._decorationsTree1.collectNodesFromOwner(ownerId); + return r0.concat(r1); + } + + public collectNodesPostOrder(): IntervalNode[] { + const r0 = this._decorationsTree0.collectNodesPostOrder(); + const r1 = this._decorationsTree1.collectNodesPostOrder(); + return r0.concat(r1); + } + + public insert(node: IntervalNode): void { + if (getNodeIsInOverviewRuler(node)) { + this._decorationsTree1.insert(node); + } else { + this._decorationsTree0.insert(node); + } + } + + public delete(node: IntervalNode): void { + if (getNodeIsInOverviewRuler(node)) { + this._decorationsTree1.delete(node); + } else { + this._decorationsTree0.delete(node); + } + } + + public resolveNode(node: IntervalNode, cachedVersionId: number): void { + if (getNodeIsInOverviewRuler(node)) { + this._decorationsTree1.resolveNode(node, cachedVersionId); + } else { + this._decorationsTree0.resolveNode(node, cachedVersionId); + } + } + + public acceptReplace(offset: number, length: number, textLength: number, forceMoveMarkers: boolean): void { + this._decorationsTree0.acceptReplace(offset, length, textLength, forceMoveMarkers); + this._decorationsTree1.acceptReplace(offset, length, textLength, forceMoveMarkers); + } +} + function cleanClassName(className: string): string { return className.replace(/[^a-z0-9\-]/gi, ' '); } @@ -847,12 +536,14 @@ export class ModelDecorationOverviewRulerOptions implements editorCommon.IModelD readonly darkColor: string | ThemeColor; readonly hcColor: string | ThemeColor; readonly position: editorCommon.OverviewRulerLane; + _resolvedColor: string; constructor(options: editorCommon.IModelDecorationOverviewRulerOptions) { this.color = strings.empty; this.darkColor = strings.empty; this.hcColor = strings.empty; this.position = editorCommon.OverviewRulerLane.Center; + this._resolvedColor = null; if (options && options.color) { this.color = options.color; @@ -949,18 +640,15 @@ export class ModelDecorationOptions implements editorCommon.IModelDecorationOpti } ModelDecorationOptions.EMPTY = ModelDecorationOptions.register({}); -class ModelDeltaDecoration implements editorCommon.IModelDeltaDecoration { - - index: number; - range: Range; - options: ModelDecorationOptions; - - constructor(index: number, range: Range, options: ModelDecorationOptions) { - this.index = index; - this.range = range; - this.options = options; - } -} +/** + * The order carefully matches the values of the enum. + */ +const TRACKED_RANGE_OPTIONS = [ + ModelDecorationOptions.register({ stickiness: editorCommon.TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges }), + ModelDecorationOptions.register({ stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges }), + ModelDecorationOptions.register({ stickiness: editorCommon.TrackedRangeStickiness.GrowsOnlyWhenTypingBefore }), + ModelDecorationOptions.register({ stickiness: editorCommon.TrackedRangeStickiness.GrowsOnlyWhenTypingAfter }), +]; function _normalizeOptions(options: editorCommon.IModelDecorationOptions): ModelDecorationOptions { if (options instanceof ModelDecorationOptions) { diff --git a/src/vs/editor/common/model/textModelWithMarkers.ts b/src/vs/editor/common/model/textModelWithMarkers.ts deleted file mode 100644 index 6100bfc3999..00000000000 --- a/src/vs/editor/common/model/textModelWithMarkers.ts +++ /dev/null @@ -1,174 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * 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 { IdGenerator } from 'vs/base/common/idGenerator'; -import { Position } from 'vs/editor/common/core/position'; -import { ITextModelWithMarkers, ITextModelCreationOptions } from 'vs/editor/common/editorCommon'; -import { LineMarker } from 'vs/editor/common/model/modelLine'; -import { TextModelWithTokens } from 'vs/editor/common/model/textModelWithTokens'; -import { LanguageIdentifier } from 'vs/editor/common/modes'; -import { ITextSource, IRawTextSource } from 'vs/editor/common/model/textSource'; - -export interface IMarkerIdToMarkerMap { - [key: string]: LineMarker; -} - -export interface INewMarker { - internalDecorationId: number; - position: Position; - stickToPreviousCharacter: boolean; -} - -var _INSTANCE_COUNT = 0; - -export class TextModelWithMarkers extends TextModelWithTokens implements ITextModelWithMarkers { - - private _markerIdGenerator: IdGenerator; - protected _markerIdToMarker: IMarkerIdToMarkerMap; - - constructor(rawTextSource: IRawTextSource, creationOptions: ITextModelCreationOptions, languageIdentifier: LanguageIdentifier) { - super(rawTextSource, creationOptions, languageIdentifier); - this._markerIdGenerator = new IdGenerator((++_INSTANCE_COUNT) + ';'); - this._markerIdToMarker = Object.create(null); - } - - public dispose(): void { - this._markerIdToMarker = null; - super.dispose(); - } - - protected _resetValue(newValue: ITextSource): void { - super._resetValue(newValue); - - // Destroy all my markers - this._markerIdToMarker = Object.create(null); - } - - _addMarker(internalDecorationId: number, lineNumber: number, column: number, stickToPreviousCharacter: boolean): string { - var pos = this.validatePosition(new Position(lineNumber, column)); - - var marker = new LineMarker(this._markerIdGenerator.nextId(), internalDecorationId, pos, stickToPreviousCharacter); - this._markerIdToMarker[marker.id] = marker; - - this._lines[pos.lineNumber - 1].addMarker(marker); - - return marker.id; - } - - protected _addMarkers(newMarkers: INewMarker[]): LineMarker[] { - if (newMarkers.length === 0) { - return []; - } - - let markers: LineMarker[] = []; - for (let i = 0, len = newMarkers.length; i < len; i++) { - let newMarker = newMarkers[i]; - - let marker = new LineMarker(this._markerIdGenerator.nextId(), newMarker.internalDecorationId, newMarker.position, newMarker.stickToPreviousCharacter); - this._markerIdToMarker[marker.id] = marker; - - markers[i] = marker; - } - - let sortedMarkers = markers.slice(0); - sortedMarkers.sort((a, b) => { - return a.position.lineNumber - b.position.lineNumber; - }); - - let currentLineNumber = 0; - let currentMarkers: LineMarker[] = [], currentMarkersLen = 0; - for (let i = 0, len = sortedMarkers.length; i < len; i++) { - let marker = sortedMarkers[i]; - - if (marker.position.lineNumber !== currentLineNumber) { - if (currentLineNumber !== 0) { - this._lines[currentLineNumber - 1].addMarkers(currentMarkers); - } - currentLineNumber = marker.position.lineNumber; - currentMarkers.length = 0; - currentMarkersLen = 0; - } - - currentMarkers[currentMarkersLen++] = marker; - } - this._lines[currentLineNumber - 1].addMarkers(currentMarkers); - - return markers; - } - - _changeMarker(id: string, lineNumber: number, column: number): void { - let marker = this._markerIdToMarker[id]; - if (!marker) { - return; - } - - let newPos = this.validatePosition(new Position(lineNumber, column)); - - if (newPos.lineNumber !== marker.position.lineNumber) { - // Move marker between lines - this._lines[marker.position.lineNumber - 1].removeMarker(marker); - this._lines[newPos.lineNumber - 1].addMarker(marker); - } - - marker.setPosition(newPos); - } - - _changeMarkerStickiness(id: string, newStickToPreviousCharacter: boolean): void { - let marker = this._markerIdToMarker[id]; - if (!marker) { - return; - } - - marker.stickToPreviousCharacter = newStickToPreviousCharacter; - } - - _getMarker(id: string): Position { - let marker = this._markerIdToMarker[id]; - if (!marker) { - return null; - } - - return marker.position; - } - - _getMarkersCount(): number { - return Object.keys(this._markerIdToMarker).length; - } - - _removeMarker(id: string): void { - let marker = this._markerIdToMarker[id]; - if (!marker) { - return; - } - - this._lines[marker.position.lineNumber - 1].removeMarker(marker); - delete this._markerIdToMarker[id]; - } - - protected _removeMarkers(markers: LineMarker[]): void { - markers.sort((a, b) => { - return a.position.lineNumber - b.position.lineNumber; - }); - - let currentLineNumber = 0; - let currentMarkers: { [markerId: string]: boolean; } = null; - for (let i = 0, len = markers.length; i < len; i++) { - let marker = markers[i]; - delete this._markerIdToMarker[marker.id]; - - if (marker.position.lineNumber !== currentLineNumber) { - if (currentLineNumber !== 0) { - this._lines[currentLineNumber - 1].removeMarkers(currentMarkers); - } - currentLineNumber = marker.position.lineNumber; - currentMarkers = Object.create(null); - } - - currentMarkers[marker.id] = true; - } - this._lines[currentLineNumber - 1].removeMarkers(currentMarkers); - } -} diff --git a/src/vs/editor/common/services/modelServiceImpl.ts b/src/vs/editor/common/services/modelServiceImpl.ts index 7eb325a6761..caaa8470b7e 100644 --- a/src/vs/editor/common/services/modelServiceImpl.ts +++ b/src/vs/editor/common/services/modelServiceImpl.ts @@ -26,7 +26,7 @@ import { EDITOR_MODEL_DEFAULTS } from 'vs/editor/common/config/editorOptions'; import { PLAINTEXT_LANGUAGE_IDENTIFIER } from 'vs/editor/common/modes/modesRegistry'; import { IRawTextSource, TextSource, RawTextSource, ITextSource } from 'vs/editor/common/model/textSource'; import * as textModelEvents from 'vs/editor/common/model/textModelEvents'; -import { ClassName } from 'vs/editor/common/model/textModelWithDecorations'; +import { ClassName } from 'vs/editor/common/model/intervalTree'; import { ISequence, LcsDiff } from 'vs/base/common/diff/diff'; import { EditOperation } from 'vs/editor/common/core/editOperation'; import { themeColorFromId, ThemeColor } from 'vs/platform/theme/common/themeService'; @@ -224,7 +224,7 @@ export class ModelServiceImpl implements IModelService { this._markerServiceSubscription = this._markerService.onMarkerChanged(this._handleMarkerChange, this); } - this._configurationServiceSubscription = this._configurationService.onDidUpdateConfiguration(e => this._updateModelOptions()); + this._configurationServiceSubscription = this._configurationService.onDidChangeConfiguration(e => this._updateModelOptions()); this._updateModelOptions(); } @@ -272,7 +272,7 @@ export class ModelServiceImpl implements IModelService { public getCreationOptions(language: string, resource: URI): editorCommon.ITextModelCreationOptions { let creationOptions = this._modelCreationOptionsByLanguageAndResource[language + resource]; if (!creationOptions) { - creationOptions = ModelServiceImpl._readModelOptions(this._configurationService.getConfiguration(null, { overrideIdentifier: language, resource })); + creationOptions = ModelServiceImpl._readModelOptions(this._configurationService.getConfiguration({ overrideIdentifier: language, resource })); this._modelCreationOptionsByLanguageAndResource[language + resource] = creationOptions; } return creationOptions; diff --git a/src/vs/editor/common/services/resourceConfiguration.ts b/src/vs/editor/common/services/resourceConfiguration.ts index 2a959539e9b..e65c4a12104 100644 --- a/src/vs/editor/common/services/resourceConfiguration.ts +++ b/src/vs/editor/common/services/resourceConfiguration.ts @@ -7,6 +7,7 @@ import Event from 'vs/base/common/event'; import URI from 'vs/base/common/uri'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IPosition } from 'vs/editor/common/core/position'; +import { IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; export const ITextResourceConfigurationService = createDecorator('textResourceConfigurationService'); @@ -17,7 +18,7 @@ export interface ITextResourceConfigurationService { /** * Event that fires when the configuration changes. */ - onDidUpdateConfiguration: Event; + onDidChangeConfiguration: Event; /** * Fetches the appropriate section of the for the given resource with appropriate overrides (e.g. language). diff --git a/src/vs/editor/common/services/resourceConfigurationImpl.ts b/src/vs/editor/common/services/resourceConfigurationImpl.ts index 453395b6488..6124db46a06 100644 --- a/src/vs/editor/common/services/resourceConfigurationImpl.ts +++ b/src/vs/editor/common/services/resourceConfigurationImpl.ts @@ -6,7 +6,7 @@ import Event, { Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import URI from 'vs/base/common/uri'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration'; import { IPosition, Position } from 'vs/editor/common/core/position'; import { IModeService } from 'vs/editor/common/services/modeService'; @@ -16,8 +16,8 @@ export class TextResourceConfigurationService extends Disposable implements ITex public _serviceBrand: any; - private readonly _onDidUpdateConfiguration: Emitter = this._register(new Emitter()); - public readonly onDidUpdateConfiguration: Event = this._onDidUpdateConfiguration.event; + private readonly _onDidChangeConfiguration: Emitter = this._register(new Emitter()); + public readonly onDidChangeConfiguration: Event = this._onDidChangeConfiguration.event; constructor( @IConfigurationService private configurationService: IConfigurationService, @@ -25,7 +25,7 @@ export class TextResourceConfigurationService extends Disposable implements ITex @IModeService private modeService: IModeService, ) { super(); - this._register(this.configurationService.onDidUpdateConfiguration(() => this._onDidUpdateConfiguration.fire())); + this._register(this.configurationService.onDidChangeConfiguration(e => this._onDidChangeConfiguration.fire(e))); } getConfiguration(resource: URI, section?: string): T diff --git a/src/vs/editor/common/viewModel/splitLinesCollection.ts b/src/vs/editor/common/viewModel/splitLinesCollection.ts index 5cf9d2c728a..506fba70f96 100644 --- a/src/vs/editor/common/viewModel/splitLinesCollection.ts +++ b/src/vs/editor/common/viewModel/splitLinesCollection.ts @@ -10,10 +10,12 @@ import { Range } from 'vs/editor/common/core/range'; import * as editorCommon from 'vs/editor/common/editorCommon'; import { LineTokens } from 'vs/editor/common/core/lineTokens'; import { PrefixSumComputerWithCache } from 'vs/editor/common/viewModel/prefixSumComputer'; -import { ViewLineData, ICoordinatesConverter } from 'vs/editor/common/viewModel/viewModel'; +import { ViewLineData, ICoordinatesConverter, IOverviewRulerDecorations } from 'vs/editor/common/viewModel/viewModel'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; import { WrappingIndent } from 'vs/editor/common/config/editorOptions'; -import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations'; +import { ModelDecorationOptions, ModelDecorationOverviewRulerOptions } from 'vs/editor/common/model/textModelWithDecorations'; +import { ThemeColor, ITheme } from 'vs/platform/theme/common/themeService'; +import { Color } from 'vs/base/common/color'; export class OutputPosition { _outputPositionBrand: void; @@ -57,6 +59,7 @@ export interface ISplitLine { getModelColumnOfViewPosition(outputLineIndex: number, outputColumn: number): number; getViewPositionOfModelPosition(deltaLineNumber: number, inputColumn: number): Position; + getViewLineNumberOfModelPosition(deltaLineNumber: number, inputColumn: number): number; } export interface IViewModelLinesCollection { @@ -82,6 +85,9 @@ export interface IViewModelLinesCollection { getViewLineMaxColumn(viewLineNumber: number): number; getViewLineData(viewLineNumber: number): ViewLineData; getViewLinesData(viewStartLineNumber: number, viewEndLineNumber: number, needed: boolean[]): ViewLineData[]; + + getAllOverviewRulerDecorations(ownerId: number, filterOutValidation: boolean, theme: ITheme): IOverviewRulerDecorations; + getDecorationsInRange(range: Range, ownerId: number, filterOutValidation: boolean): editorCommon.IModelDecoration[]; } export class CoordinatesConverter implements ICoordinatesConverter { @@ -650,6 +656,83 @@ export class SplitLinesCollection implements IViewModelLinesCollection { // console.log('in -> out ' + inputLineNumber + ',' + inputColumn + ' ===> ' + r.lineNumber + ',' + r); return r; } + + private _getViewLineNumberForModelPosition(inputLineNumber: number, inputColumn: number): number { + let lineIndex = inputLineNumber - 1; + if (this.lines[lineIndex].isVisible()) { + // this model line is visible + const deltaLineNumber = 1 + (lineIndex === 0 ? 0 : this.prefixSumComputer.getAccumulatedValue(lineIndex - 1)); + return this.lines[lineIndex].getViewLineNumberOfModelPosition(deltaLineNumber, inputColumn); + } + + // this model line is not visible + while (lineIndex > 0 && !this.lines[lineIndex].isVisible()) { + lineIndex--; + } + if (lineIndex === 0 && !this.lines[lineIndex].isVisible()) { + // Could not reach a real line + return 1; + } + const deltaLineNumber = 1 + (lineIndex === 0 ? 0 : this.prefixSumComputer.getAccumulatedValue(lineIndex - 1)); + return this.lines[lineIndex].getViewLineNumberOfModelPosition(deltaLineNumber, this.model.getLineMaxColumn(lineIndex + 1)); + } + + public getAllOverviewRulerDecorations(ownerId: number, filterOutValidation: boolean, theme: ITheme): IOverviewRulerDecorations { + const decorations = this.model.getOverviewRulerDecorations(ownerId, filterOutValidation); + const result = new OverviewRulerDecorations(); + for (let i = 0, len = decorations.length; i < len; i++) { + const decoration = decorations[i]; + const opts = decoration.options.overviewRuler; + const lane = opts.position; + if (lane === 0) { + continue; + } + const color = resolveColor(opts, theme); + const viewStartLineNumber = this._getViewLineNumberForModelPosition(decoration.range.startLineNumber, decoration.range.startColumn); + const viewEndLineNumber = this._getViewLineNumberForModelPosition(decoration.range.endLineNumber, decoration.range.endColumn); + + result.accept(color, viewStartLineNumber, viewEndLineNumber, lane); + } + return result.result; + } + + public getDecorationsInRange(range: Range, ownerId: number, filterOutValidation: boolean): editorCommon.IModelDecoration[] { + const modelStart = this.convertViewPositionToModelPosition(range.startLineNumber, range.startColumn); + const modelEnd = this.convertViewPositionToModelPosition(range.endLineNumber, range.endColumn); + + if (modelEnd.lineNumber - modelStart.lineNumber <= range.endLineNumber - range.startLineNumber) { + // most likely there are no hidden lines => fast path + return this.model.getDecorationsInRange(new Range(modelStart.lineNumber, modelStart.column, modelEnd.lineNumber, modelEnd.column), ownerId, filterOutValidation); + } + + let result: editorCommon.IModelDecoration[] = []; + const modelStartLineIndex = modelStart.lineNumber - 1; + const modelEndLineIndex = modelEnd.lineNumber - 1; + + let reqStart: Position = null; + for (let modelLineIndex = modelStartLineIndex; modelLineIndex <= modelEndLineIndex; modelLineIndex++) { + const line = this.lines[modelLineIndex]; + if (line.isVisible()) { + // merge into previous request + if (reqStart === null) { + reqStart = new Position(modelLineIndex + 1, modelLineIndex === modelStartLineIndex ? modelStart.column : 1); + } + } else { + // hit invisible line => flush request + if (reqStart !== null) { + result = result.concat(this.model.getDecorationsInRange(new Range(reqStart.lineNumber, reqStart.column, modelLineIndex + 1, 1), ownerId, filterOutValidation)); + reqStart = null; + } + } + } + + if (reqStart !== null) { + result = result.concat(this.model.getDecorationsInRange(new Range(reqStart.lineNumber, reqStart.column, modelEnd.lineNumber, modelEnd.column), ownerId, filterOutValidation)); + reqStart = null; + } + + return result; + } } class VisibleIdentitySplitLine implements ISplitLine { @@ -711,6 +794,10 @@ class VisibleIdentitySplitLine implements ISplitLine { public getViewPositionOfModelPosition(deltaLineNumber: number, inputColumn: number): Position { return new Position(deltaLineNumber, inputColumn); } + + public getViewLineNumberOfModelPosition(deltaLineNumber: number, inputColumn: number): number { + return deltaLineNumber; + } } class InvisibleIdentitySplitLine implements ISplitLine { @@ -761,6 +848,10 @@ class InvisibleIdentitySplitLine implements ISplitLine { public getViewPositionOfModelPosition(deltaLineNumber: number, inputColumn: number): Position { throw new Error('Not supported'); } + + public getViewLineNumberOfModelPosition(deltaLineNumber: number, inputColumn: number): number { + throw new Error('Not supported'); + } } export class SplitLine implements ISplitLine { @@ -914,6 +1005,14 @@ export class SplitLine implements ISplitLine { // console.log('in -> out ' + deltaLineNumber + ',' + inputColumn + ' ===> ' + (deltaLineNumber+outputLineIndex) + ',' + outputColumn); return new Position(deltaLineNumber + outputLineIndex, outputColumn); } + + public getViewLineNumberOfModelPosition(deltaLineNumber: number, inputColumn: number): number { + if (!this._isVisible) { + throw new Error('Not supported'); + } + const r = this.positionMapper.getOutputPositionOfInputOffset(inputColumn - 1); + return (deltaLineNumber + r.outputLineIndex); + } } function createSplitLine(linePositionMapperFactory: ILineMapperFactory, text: string, tabSize: number, wrappingColumn: number, columnsForFullWidthChar: number, wrappingIndent: WrappingIndent, isVisible: boolean): ISplitLine { @@ -1093,4 +1192,77 @@ export class IdentityLinesCollection implements IViewModelLinesCollection { return result; } + + public getAllOverviewRulerDecorations(ownerId: number, filterOutValidation: boolean, theme: ITheme): IOverviewRulerDecorations { + const decorations = this.model.getOverviewRulerDecorations(ownerId, filterOutValidation); + const result = new OverviewRulerDecorations(); + for (let i = 0, len = decorations.length; i < len; i++) { + const decoration = decorations[i]; + const opts = decoration.options.overviewRuler; + const lane = opts.position; + if (lane === 0) { + continue; + } + const color = resolveColor(opts, theme); + const viewStartLineNumber = decoration.range.startLineNumber; + const viewEndLineNumber = decoration.range.endLineNumber; + + result.accept(color, viewStartLineNumber, viewEndLineNumber, lane); + } + return result.result; + } + + public getDecorationsInRange(range: Range, ownerId: number, filterOutValidation: boolean): editorCommon.IModelDecoration[] { + return this.model.getDecorationsInRange(range, ownerId, filterOutValidation); + } +} + +class OverviewRulerDecorations { + + readonly result: IOverviewRulerDecorations = Object.create(null); + + constructor() { + } + + public accept(color: string, startLineNumber: number, endLineNumber: number, lane: number): void { + let prev = this.result[color]; + + if (prev) { + const prevLane = prev[prev.length - 3]; + const prevEndLineNumber = prev[prev.length - 1]; + if (prevLane === lane && prevEndLineNumber + 1 >= startLineNumber) { + // merge into prev + if (endLineNumber > prevEndLineNumber) { + prev[prev.length - 1] = endLineNumber; + } + return; + } + + // push + prev.push(lane, startLineNumber, endLineNumber); + } else { + this.result[color] = [lane, startLineNumber, endLineNumber]; + } + } +} + + +function resolveColor(opts: ModelDecorationOverviewRulerOptions, theme: ITheme): string { + if (!opts._resolvedColor) { + const themeType = theme.type; + const color = (themeType === 'dark' ? opts.darkColor : themeType === 'light' ? opts.color : opts.hcColor); + opts._resolvedColor = resolveRulerColor(color, theme); + } + return opts._resolvedColor; +} + +function resolveRulerColor(color: string | ThemeColor, theme: ITheme): string { + if (typeof color === 'string') { + return color; + } + let c = color ? theme.getColor(color.id) : null; + if (!c) { + c = Color.transparent; + } + return c.toString(); } diff --git a/src/vs/editor/common/viewModel/viewModel.ts b/src/vs/editor/common/viewModel/viewModel.ts index 2890bf9314f..88787418a97 100644 --- a/src/vs/editor/common/viewModel/viewModel.ts +++ b/src/vs/editor/common/viewModel/viewModel.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { INewScrollPosition, IModelDecoration, EndOfLinePreference, IViewState } from 'vs/editor/common/editorCommon'; +import { INewScrollPosition, EndOfLinePreference, IViewState, IModelDecorationOptions } from 'vs/editor/common/editorCommon'; import { ViewLineToken } from 'vs/editor/common/core/viewLineToken'; import { Position, IPosition } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; @@ -14,6 +14,7 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import { Scrollable, IScrollPosition } from 'vs/base/common/scrollable'; import { IPartialViewLinesViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; import { IEditorWhitespace } from 'vs/editor/common/viewLayout/whitespaceComputer'; +import { ITheme } from 'vs/platform/theme/common/themeService'; export interface IViewWhitespaceViewportData { readonly id: number; @@ -138,7 +139,8 @@ export interface IViewModel { getLineMaxColumn(lineNumber: number): number; getLineFirstNonWhitespaceColumn(lineNumber: number): number; getLineLastNonWhitespaceColumn(lineNumber: number): number; - getAllOverviewRulerDecorations(): ViewModelDecoration[]; + getAllOverviewRulerDecorations(theme: ITheme): IOverviewRulerDecorations; + invalidateOverviewRulerColorCache(): void; getValueInRange(range: Range, eol: EndOfLinePreference): string; getModelLineMaxColumn(modelLineNumber: number): number; @@ -267,15 +269,25 @@ export class InlineDecoration { export class ViewModelDecoration { _viewModelDecorationBrand: void; - public range: Range; - public readonly source: IModelDecoration; + public readonly range: Range; + public readonly options: IModelDecorationOptions; - constructor(source: IModelDecoration) { - this.range = null; - this.source = source; + constructor(range: Range, options: IModelDecorationOptions) { + this.range = range; + this.options = options; } } +/** + * Decorations are encoded in a number array using the following scheme: + * - 3*i = lane + * - 3*i+1 = startLineNumber + * - 3*i+2 = endLineNumber + */ +export interface IOverviewRulerDecorations { + [color: string]: number[]; +} + export class ViewEventsCollector { private _events: ViewEvent[]; diff --git a/src/vs/editor/common/viewModel/viewModelDecorations.ts b/src/vs/editor/common/viewModel/viewModelDecorations.ts index f4311681f82..b5c6aa4b5ba 100644 --- a/src/vs/editor/common/viewModel/viewModelDecorations.ts +++ b/src/vs/editor/common/viewModel/viewModelDecorations.ts @@ -9,7 +9,7 @@ import { Range } from 'vs/editor/common/core/range'; import { Position } from 'vs/editor/common/core/position'; import * as editorCommon from 'vs/editor/common/editorCommon'; import { InlineDecoration, ViewModelDecoration, ICoordinatesConverter } from 'vs/editor/common/viewModel/viewModel'; -import { IModelDecorationsChangedEvent } from 'vs/editor/common/model/textModelEvents'; +import { IViewModelLinesCollection } from 'vs/editor/common/viewModel/splitLinesCollection'; export interface IDecorationsViewportData { /** @@ -27,6 +27,7 @@ export class ViewModelDecorations implements IDisposable { private readonly editorId: number; private readonly model: editorCommon.IModel; private readonly configuration: editorCommon.IConfiguration; + private readonly _linesCollection: IViewModelLinesCollection; private readonly _coordinatesConverter: ICoordinatesConverter; private _decorationsCache: { [decorationId: string]: ViewModelDecoration; }; @@ -34,10 +35,11 @@ export class ViewModelDecorations implements IDisposable { private _cachedModelDecorationsResolver: IDecorationsViewportData; private _cachedModelDecorationsResolverViewRange: Range; - constructor(editorId: number, model: editorCommon.IModel, configuration: editorCommon.IConfiguration, coordinatesConverter: ICoordinatesConverter) { + constructor(editorId: number, model: editorCommon.IModel, configuration: editorCommon.IConfiguration, linesCollection: IViewModelLinesCollection, coordinatesConverter: ICoordinatesConverter) { this.editorId = editorId; this.model = model; this.configuration = configuration; + this._linesCollection = linesCollection; this._coordinatesConverter = coordinatesConverter; this._decorationsCache = Object.create(null); this._clearCachedModelDecorationsResolver(); @@ -58,26 +60,8 @@ export class ViewModelDecorations implements IDisposable { this._clearCachedModelDecorationsResolver(); } - public onModelDecorationsChanged(e: IModelDecorationsChangedEvent): void { - let changedDecorations = e.changedDecorations; - for (let i = 0, len = changedDecorations.length; i < len; i++) { - let changedDecoration = changedDecorations[i]; - let myDecoration = this._decorationsCache[changedDecoration]; - if (!myDecoration) { - continue; - } - - myDecoration.range = null; - } - - let removedDecorations = e.removedDecorations; - if (this._decorationsCache !== null && this._decorationsCache !== undefined) { - for (let i = 0, len = removedDecorations.length; i < len; i++) { - let removedDecoration = removedDecorations[i]; - delete this._decorationsCache[removedDecoration]; - } - } - + public onModelDecorationsChanged(): void { + this._decorationsCache = Object.create(null); this._clearCachedModelDecorationsResolver(); } @@ -88,42 +72,25 @@ export class ViewModelDecorations implements IDisposable { } private _getOrCreateViewModelDecoration(modelDecoration: editorCommon.IModelDecoration): ViewModelDecoration { - let id = modelDecoration.id; + const id = modelDecoration.id; let r = this._decorationsCache[id]; if (!r) { - r = new ViewModelDecoration(modelDecoration); + const modelRange = modelDecoration.range; + const options = modelDecoration.options; + let viewRange: Range; + if (options.isWholeLine) { + const start = this._coordinatesConverter.convertModelPositionToViewPosition(new Position(modelRange.startLineNumber, 1)); + const end = this._coordinatesConverter.convertModelPositionToViewPosition(new Position(modelRange.endLineNumber, this.model.getLineMaxColumn(modelRange.endLineNumber))); + viewRange = new Range(start.lineNumber, start.column, end.lineNumber, end.column); + } else { + viewRange = this._coordinatesConverter.convertModelRangeToViewRange(modelRange); + } + r = new ViewModelDecoration(viewRange, options); this._decorationsCache[id] = r; } - if (r.range === null) { - const modelRange = modelDecoration.range; - if (modelDecoration.options.isWholeLine) { - let start = this._coordinatesConverter.convertModelPositionToViewPosition(new Position(modelRange.startLineNumber, 1)); - let end = this._coordinatesConverter.convertModelPositionToViewPosition(new Position(modelRange.endLineNumber, this.model.getLineMaxColumn(modelRange.endLineNumber))); - r.range = new Range(start.lineNumber, start.column, end.lineNumber, end.column); - } else { - r.range = this._coordinatesConverter.convertModelRangeToViewRange(modelRange); - } - } return r; } - public getAllOverviewRulerDecorations(): ViewModelDecoration[] { - let modelDecorations = this.model.getAllDecorations(this.editorId, this.configuration.editor.readOnly); - let result: ViewModelDecoration[] = [], resultLen = 0; - for (let i = 0, len = modelDecorations.length; i < len; i++) { - let modelDecoration = modelDecorations[i]; - let decorationOptions = modelDecoration.options; - - if (!decorationOptions.overviewRuler.color) { - continue; - } - - let viewModelDecoration = this._getOrCreateViewModelDecoration(modelDecoration); - result[resultLen++] = viewModelDecoration; - } - return result; - } - public getDecorationsViewportData(viewRange: Range): IDecorationsViewportData { var cacheIsValid = true; cacheIsValid = cacheIsValid && (this._cachedModelDecorationsResolver !== null); @@ -136,10 +103,9 @@ export class ViewModelDecorations implements IDisposable { } private _getDecorationsViewportData(viewportRange: Range): IDecorationsViewportData { - let viewportModelRange = this._coordinatesConverter.convertViewRangeToModelRange(viewportRange); - let startLineNumber = viewportRange.startLineNumber; - let endLineNumber = viewportRange.endLineNumber; - let modelDecorations = this.model.getDecorationsInRange(viewportModelRange, this.editorId, this.configuration.editor.readOnly); + const modelDecorations = this._linesCollection.getDecorationsInRange(viewportRange, this.editorId, this.configuration.editor.readOnly); + const startLineNumber = viewportRange.startLineNumber; + const endLineNumber = viewportRange.endLineNumber; let decorationsInViewport: ViewModelDecoration[] = [], decorationsInViewportLen = 0; let inlineDecorations: InlineDecoration[][] = []; diff --git a/src/vs/editor/common/viewModel/viewModelImpl.ts b/src/vs/editor/common/viewModel/viewModelImpl.ts index f1a7fcbf961..25a52e2f2b9 100644 --- a/src/vs/editor/common/viewModel/viewModelImpl.ts +++ b/src/vs/editor/common/viewModel/viewModelImpl.ts @@ -12,7 +12,7 @@ import * as editorCommon from 'vs/editor/common/editorCommon'; import { TokenizationRegistry, ColorId, LanguageId } from 'vs/editor/common/modes'; import { tokenizeLineToHTML } from 'vs/editor/common/modes/textToHtmlTokenizer'; import { ViewModelDecorations } from 'vs/editor/common/viewModel/viewModelDecorations'; -import { MinimapLinesRenderingData, ViewLineRenderingData, ViewModelDecoration, IViewModel, ICoordinatesConverter, ViewEventsCollector } from 'vs/editor/common/viewModel/viewModel'; +import { MinimapLinesRenderingData, ViewLineRenderingData, ViewModelDecoration, IViewModel, ICoordinatesConverter, ViewEventsCollector, IOverviewRulerDecorations } from 'vs/editor/common/viewModel/viewModel'; import { SplitLinesCollection, IViewModelLinesCollection, IdentityLinesCollection } from 'vs/editor/common/viewModel/splitLinesCollection'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; import { MinimapTokensColorTracker } from 'vs/editor/common/view/minimapCharRenderer'; @@ -22,6 +22,8 @@ import { CharacterHardWrappingLineMapperFactory } from 'vs/editor/common/viewMod import { ViewLayout } from 'vs/editor/common/viewLayout/viewLayout'; import { Color } from 'vs/base/common/color'; import { IDisposable } from 'vs/base/common/lifecycle'; +import { ITheme } from 'vs/platform/theme/common/themeService'; +import { ModelDecorationOverviewRulerOptions } from 'vs/editor/common/model/textModelWithDecorations'; const USE_IDENTITY_LINES_COLLECTION = true; @@ -80,7 +82,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel this._isDisposing = false; this._centeredViewLine = -1; - this.decorations = new ViewModelDecorations(this.editorId, this.model, this.configuration, this.coordinatesConverter); + this.decorations = new ViewModelDecorations(this.editorId, this.model, this.configuration, this.lines, this.coordinatesConverter); this._register(this.model.addBulkListener((events: EmitterEvent[]) => { if (this._isDisposing) { @@ -282,8 +284,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel break; } case textModelEvents.TextModelEventType.ModelDecorationsChanged: { - const e = data; - this.decorations.onModelDecorationsChanged(e); + this.decorations.onModelDecorationsChanged(); eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent()); break; } @@ -429,8 +430,17 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel ); } - public getAllOverviewRulerDecorations(): ViewModelDecoration[] { - return this.decorations.getAllOverviewRulerDecorations(); + public getAllOverviewRulerDecorations(theme: ITheme): IOverviewRulerDecorations { + return this.lines.getAllOverviewRulerDecorations(this.editorId, this.configuration.editor.readOnly, theme); + } + + public invalidateOverviewRulerColorCache(): void { + const decorations = this.model.getOverviewRulerDecorations(); + for (let i = 0, len = decorations.length; i < len; i++) { + const decoration = decorations[i]; + const opts = decoration.options.overviewRuler; + opts._resolvedColor = null; + } } public getValueInRange(range: Range, eol: editorCommon.EndOfLinePreference): string { diff --git a/src/vs/editor/contrib/comment/common/lineCommentCommand.ts b/src/vs/editor/contrib/comment/common/lineCommentCommand.ts index 8f0abccfd32..e1d5bec1202 100644 --- a/src/vs/editor/contrib/comment/common/lineCommentCommand.ts +++ b/src/vs/editor/contrib/comment/common/lineCommentCommand.ts @@ -270,7 +270,7 @@ export class LineCommentCommand implements editorCommon.ICommand { */ private _executeBlockComment(model: editorCommon.ITokenizedModel, builder: editorCommon.IEditOperationBuilder, s: Selection): void { model.tokenizeIfCheap(s.startLineNumber); - let languageId = model.getLanguageIdAtPosition(s.startLineNumber, s.startColumn); + let languageId = model.getLanguageIdAtPosition(s.startLineNumber, 1); let config = LanguageConfigurationRegistry.getComments(languageId); if (!config || !config.blockCommentStartToken || !config.blockCommentEndToken) { // Mode does not support block comments diff --git a/src/vs/editor/contrib/comment/test/common/lineCommentCommand.test.ts b/src/vs/editor/contrib/comment/test/common/lineCommentCommand.test.ts index 57c946f76f9..743652c37a7 100644 --- a/src/vs/editor/contrib/comment/test/common/lineCommentCommand.test.ts +++ b/src/vs/editor/contrib/comment/test/common/lineCommentCommand.test.ts @@ -1002,4 +1002,20 @@ suite('Editor Contrib - Line Comment in mixed modes', () => { ); }); + test('issue #36173: Commenting code in JSX tag body', () => { + testLineCommentCommand( + [ + '
', + ' {123}', + '
', + ], + new Selection(2, 4, 2, 4), + [ + '
', + ' {/* {123} */}', + '
', + ], + new Selection(2, 8, 2, 8), + ); + }); }); diff --git a/src/vs/editor/contrib/find/browser/findWidget.ts b/src/vs/editor/contrib/find/browser/findWidget.ts index 726c2451eef..77c25acb251 100644 --- a/src/vs/editor/contrib/find/browser/findWidget.ts +++ b/src/vs/editor/contrib/find/browser/findWidget.ts @@ -48,7 +48,7 @@ const NLS_REPLACE_INPUT_PLACEHOLDER = nls.localize('placeholder.replace', "Repla const NLS_REPLACE_BTN_LABEL = nls.localize('label.replaceButton', "Replace"); const NLS_REPLACE_ALL_BTN_LABEL = nls.localize('label.replaceAllButton', "Replace All"); const NLS_TOGGLE_REPLACE_MODE_BTN_LABEL = nls.localize('label.toggleReplaceButton', "Toggle Replace mode"); -const NLS_MATCHES_COUNT_LIMIT_TITLE = nls.localize('title.matchesCountLimit', "Only the first 999 results are highlighted, but all find operations work on the entire text."); +const NLS_MATCHES_COUNT_LIMIT_TITLE = nls.localize('title.matchesCountLimit', "Only the first {0} results are highlighted, but all find operations work on the entire text.", MATCHES_LIMIT); const NLS_MATCHES_LOCATION = nls.localize('label.matchesLocation', "{0} of {1}"); const NLS_NO_RESULTS = nls.localize('label.noResults', "No Results"); @@ -921,7 +921,7 @@ class SimpleCheckbox extends Widget { this._label = document.createElement('label'); this._label.className = 'label'; - // Connect the label and the checkbox. Checkbox will get checked when the label recieves a click. + // Connect the label and the checkbox. Checkbox will get checked when the label receives a click. this._label.htmlFor = this._checkbox.id; this._label.tabIndex = -1; diff --git a/src/vs/editor/contrib/find/common/findController.ts b/src/vs/editor/contrib/find/common/findController.ts index 09536d1f5f8..4ead5ef999c 100644 --- a/src/vs/editor/contrib/find/common/findController.ts +++ b/src/vs/editor/contrib/find/common/findController.ts @@ -6,25 +6,18 @@ import * as nls from 'vs/nls'; import { HistoryNavigator } from 'vs/base/common/history'; -import { KeyCode, KeyMod, KeyChord } from 'vs/base/common/keyCodes'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { Disposable } from 'vs/base/common/lifecycle'; import { ContextKeyExpr, RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { Range } from 'vs/editor/common/core/range'; -import { Selection } from 'vs/editor/common/core/selection'; import * as strings from 'vs/base/common/strings'; import * as editorCommon from 'vs/editor/common/editorCommon'; -import { editorAction, commonEditorContribution, ServicesAccessor, EditorAction, EditorCommand, CommonEditorRegistry } from 'vs/editor/common/editorCommonExtensions'; +import { editorAction, ServicesAccessor, EditorAction, EditorCommand, CommonEditorRegistry } from 'vs/editor/common/editorCommonExtensions'; import { FIND_IDS, FindModelBoundToEditorModel, ToggleCaseSensitiveKeybinding, ToggleRegexKeybinding, ToggleWholeWordKeybinding, ToggleSearchScopeKeybinding, ShowPreviousFindTermKeybinding, ShowNextFindTermKeybinding } from 'vs/editor/contrib/find/common/findModel'; import { FindReplaceState, FindReplaceStateChangedEvent, INewFindReplaceState } from 'vs/editor/contrib/find/common/findState'; import { getSelectionSearchString } from 'vs/editor/contrib/find/common/find'; -import { DocumentHighlightProviderRegistry } from 'vs/editor/common/modes'; -import { RunOnceScheduler, Delayer } from 'vs/base/common/async'; -import { CursorChangeReason, ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; +import { Delayer } from 'vs/base/common/async'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations'; -import { overviewRulerSelectionHighlightForeground } from 'vs/platform/theme/common/colorRegistry'; -import { themeColorFromId } from 'vs/platform/theme/common/themeService'; export const enum FindStartFocusAction { NoFocusChange, @@ -514,614 +507,6 @@ export class StartFindReplaceAction extends EditorAction { } } -export interface IMultiCursorFindInput { - changeFindSearchString: boolean; - allowMultiline: boolean; - highlightFindOptions: boolean; -} - -export interface IMultiCursorFindResult { - searchText: string; - matchCase: boolean; - wholeWord: boolean; - - currentMatch: Selection; -} - -function multiCursorFind(editor: editorCommon.ICommonCodeEditor, input: IMultiCursorFindInput): IMultiCursorFindResult { - let controller = CommonFindController.get(editor); - if (!controller) { - return null; - } - let state = controller.getState(); - let searchText: string; - let currentMatch: Selection; - - // In any case, if the find widget was ever opened, the options are taken from it - let wholeWord = state.wholeWord; - let matchCase = state.matchCase; - - // Find widget owns what we search for if: - // - focus is not in the editor (i.e. it is in the find widget) - // - and the search widget is visible - // - and the search string is non-empty - if (!editor.isFocused() && state.isRevealed && state.searchString.length > 0) { - // Find widget owns what is searched for - searchText = state.searchString; - } else { - // Selection owns what is searched for - let s = editor.getSelection(); - - if (s.startLineNumber !== s.endLineNumber && !input.allowMultiline) { - // multiline forbidden - return null; - } - - if (s.isEmpty()) { - // selection is empty => expand to current word - let word = editor.getModel().getWordAtPosition(s.getStartPosition()); - if (!word) { - return null; - } - searchText = word.word; - currentMatch = new Selection(s.startLineNumber, word.startColumn, s.startLineNumber, word.endColumn); - } else { - searchText = editor.getModel().getValueInRange(s).replace(/\r\n/g, '\n'); - } - if (input.changeFindSearchString) { - controller.setSearchString(searchText); - } - } - - if (input.highlightFindOptions) { - controller.highlightFindOptions(); - } - - return { - searchText: searchText, - matchCase: matchCase, - wholeWord: wholeWord, - currentMatch: currentMatch - }; -} - -export abstract class SelectNextFindMatchAction extends EditorAction { - protected _getNextMatch(editor: editorCommon.ICommonCodeEditor): Selection { - let r = multiCursorFind(editor, { - changeFindSearchString: true, - allowMultiline: true, - highlightFindOptions: true - }); - if (!r) { - return null; - } - if (r.currentMatch) { - return r.currentMatch; - } - - let allSelections = editor.getSelections(); - let lastAddedSelection = allSelections[allSelections.length - 1]; - - let nextMatch = editor.getModel().findNextMatch(r.searchText, lastAddedSelection.getEndPosition(), false, r.matchCase, r.wholeWord ? editor.getConfiguration().wordSeparators : null, false); - - if (!nextMatch) { - return null; - } - - return new Selection(nextMatch.range.startLineNumber, nextMatch.range.startColumn, nextMatch.range.endLineNumber, nextMatch.range.endColumn); - } -} - -export abstract class SelectPreviousFindMatchAction extends EditorAction { - protected _getPreviousMatch(editor: editorCommon.ICommonCodeEditor): Selection { - let r = multiCursorFind(editor, { - changeFindSearchString: true, - allowMultiline: true, - highlightFindOptions: true - }); - if (!r) { - return null; - } - if (r.currentMatch) { - return r.currentMatch; - } - - let allSelections = editor.getSelections(); - let lastAddedSelection = allSelections[allSelections.length - 1]; - - let previousMatch = editor.getModel().findPreviousMatch(r.searchText, lastAddedSelection.getStartPosition(), false, r.matchCase, r.wholeWord ? editor.getConfiguration().wordSeparators : null, false); - - if (!previousMatch) { - return null; - } - - return new Selection(previousMatch.range.startLineNumber, previousMatch.range.startColumn, previousMatch.range.endLineNumber, previousMatch.range.endColumn); - } -} - -@editorAction -export class AddSelectionToNextFindMatchAction extends SelectNextFindMatchAction { - - constructor() { - super({ - id: FIND_IDS.AddSelectionToNextFindMatchAction, - label: nls.localize('addSelectionToNextFindMatch', "Add Selection To Next Find Match"), - alias: 'Add Selection To Next Find Match', - precondition: null, - kbOpts: { - kbExpr: EditorContextKeys.focus, - primary: KeyMod.CtrlCmd | KeyCode.KEY_D - } - }); - } - - public run(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor): void { - const allSelections = editor.getSelections(); - - // If there are mulitple cursors, handle the case where they do not all select the same text. - if (allSelections.length > 1) { - const model = editor.getModel(); - const controller = CommonFindController.get(editor); - if (!controller) { - return; - } - const findState = controller.getState(); - const caseSensitive = findState.matchCase; - - let selectionsContainSameText = true; - - let selectedText = model.getValueInRange(allSelections[0]); - if (!caseSensitive) { - selectedText = selectedText.toLowerCase(); - } - for (let i = 1, len = allSelections.length; i < len; i++) { - let selection = allSelections[i]; - if (selection.isEmpty()) { - selectionsContainSameText = false; - break; - } - - let thisSelectedText = model.getValueInRange(selection); - if (!caseSensitive) { - thisSelectedText = thisSelectedText.toLowerCase(); - } - if (selectedText !== thisSelectedText) { - selectionsContainSameText = false; - break; - } - } - - if (!selectionsContainSameText) { - let resultingSelections: Selection[] = []; - for (let i = 0, len = allSelections.length; i < len; i++) { - let selection = allSelections[i]; - if (selection.isEmpty()) { - let word = editor.getModel().getWordAtPosition(selection.getStartPosition()); - if (word) { - resultingSelections[i] = new Selection(selection.startLineNumber, word.startColumn, selection.startLineNumber, word.endColumn); - continue; - } - } - resultingSelections[i] = selection; - } - editor.setSelections(resultingSelections); - return; - } - } - - let nextMatch = this._getNextMatch(editor); - - if (!nextMatch) { - return; - } - - editor.setSelections(allSelections.concat(nextMatch)); - editor.revealRangeInCenterIfOutsideViewport(nextMatch, editorCommon.ScrollType.Smooth); - } -} - -@editorAction -export class AddSelectionToPreviousFindMatchAction extends SelectPreviousFindMatchAction { - - constructor() { - super({ - id: FIND_IDS.AddSelectionToPreviousFindMatchAction, - label: nls.localize('addSelectionToPreviousFindMatch', "Add Selection To Previous Find Match"), - alias: 'Add Selection To Previous Find Match', - precondition: null - }); - } - - public run(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor): void { - let previousMatch = this._getPreviousMatch(editor); - - if (!previousMatch) { - return; - } - - let allSelections = editor.getSelections(); - editor.setSelections(allSelections.concat(previousMatch)); - editor.revealRangeInCenterIfOutsideViewport(previousMatch, editorCommon.ScrollType.Smooth); - } -} - -@editorAction -export class MoveSelectionToNextFindMatchAction extends SelectNextFindMatchAction { - - constructor() { - super({ - id: FIND_IDS.MoveSelectionToNextFindMatchAction, - label: nls.localize('moveSelectionToNextFindMatch', "Move Last Selection To Next Find Match"), - alias: 'Move Last Selection To Next Find Match', - precondition: null, - kbOpts: { - kbExpr: EditorContextKeys.focus, - primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_D) - } - }); - } - - public run(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor): void { - let nextMatch = this._getNextMatch(editor); - - if (!nextMatch) { - return; - } - - let allSelections = editor.getSelections(); - editor.setSelections(allSelections.slice(0, allSelections.length - 1).concat(nextMatch)); - editor.revealRangeInCenterIfOutsideViewport(nextMatch, editorCommon.ScrollType.Smooth); - } -} - -@editorAction -export class MoveSelectionToPreviousFindMatchAction extends SelectPreviousFindMatchAction { - - constructor() { - super({ - id: FIND_IDS.MoveSelectionToPreviousFindMatchAction, - label: nls.localize('moveSelectionToPreviousFindMatch', "Move Last Selection To Previous Find Match"), - alias: 'Move Last Selection To Previous Find Match', - precondition: null - }); - } - - public run(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor): void { - let previousMatch = this._getPreviousMatch(editor); - - if (!previousMatch) { - return; - } - - let allSelections = editor.getSelections(); - editor.setSelections(allSelections.slice(0, allSelections.length - 1).concat(previousMatch)); - editor.revealRangeInCenterIfOutsideViewport(previousMatch, editorCommon.ScrollType.Smooth); - } -} - -export abstract class AbstractSelectHighlightsAction extends EditorAction { - public run(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor): void { - let controller = CommonFindController.get(editor); - if (!controller) { - return null; - } - - let matches: Range[] = null; - - const findState = controller.getState(); - if (findState.isRevealed && findState.isRegex && findState.searchString.length > 0) { - - matches = editor.getModel().findMatches(findState.searchString, true, findState.isRegex, findState.matchCase, findState.wholeWord ? editor.getConfiguration().wordSeparators : null, false).map(m => m.range); - - } else { - - let r = multiCursorFind(editor, { - changeFindSearchString: true, - allowMultiline: true, - highlightFindOptions: true - }); - if (!r) { - return; - } - - matches = editor.getModel().findMatches(r.searchText, true, false, r.matchCase, r.wholeWord ? editor.getConfiguration().wordSeparators : null, false).map(m => m.range); - } - - if (matches.length > 0) { - let editorSelection = editor.getSelection(); - for (let i = 0, len = matches.length; i < len; i++) { - let match = matches[i]; - let intersection = match.intersectRanges(editorSelection); - if (intersection) { - // bingo! - matches.splice(i, 1); - matches.unshift(match); - break; - } - } - editor.setSelections(matches.map(m => new Selection(m.startLineNumber, m.startColumn, m.endLineNumber, m.endColumn))); - } - } -} - -@editorAction -export class SelectHighlightsAction extends AbstractSelectHighlightsAction { - constructor() { - super({ - id: 'editor.action.selectHighlights', - label: nls.localize('selectAllOccurrencesOfFindMatch', "Select All Occurrences of Find Match"), - alias: 'Select All Occurrences of Find Match', - precondition: null, - kbOpts: { - kbExpr: EditorContextKeys.focus, - primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_L - } - }); - } -} - -@editorAction -export class CompatChangeAll extends AbstractSelectHighlightsAction { - constructor() { - super({ - id: 'editor.action.changeAll', - label: nls.localize('changeAll.label', "Change All Occurrences"), - alias: 'Change All Occurrences', - precondition: EditorContextKeys.writable, - kbOpts: { - kbExpr: EditorContextKeys.textFocus, - primary: KeyMod.CtrlCmd | KeyCode.F2 - }, - menuOpts: { - group: '1_modification', - order: 1.2 - } - }); - } -} - -class SelectionHighlighterState { - public readonly lastWordUnderCursor: Selection; - public readonly searchText: string; - public readonly matchCase: boolean; - public readonly wordSeparators: string; - - constructor(lastWordUnderCursor: Selection, searchText: string, matchCase: boolean, wordSeparators: string) { - this.searchText = searchText; - this.matchCase = matchCase; - this.wordSeparators = wordSeparators; - } - - /** - * Everything equals except for `lastWordUnderCursor` - */ - public static softEquals(a: SelectionHighlighterState, b: SelectionHighlighterState): boolean { - if (!a && !b) { - return true; - } - if (!a || !b) { - return false; - } - return ( - a.searchText === b.searchText - && a.matchCase === b.matchCase - && a.wordSeparators === b.wordSeparators - ); - } -} - -@commonEditorContribution -export class SelectionHighlighter extends Disposable implements editorCommon.IEditorContribution { - private static ID = 'editor.contrib.selectionHighlighter'; - - private editor: editorCommon.ICommonCodeEditor; - private _isEnabled: boolean; - private decorations: string[]; - private updateSoon: RunOnceScheduler; - private state: SelectionHighlighterState; - - constructor(editor: editorCommon.ICommonCodeEditor) { - super(); - this.editor = editor; - this._isEnabled = editor.getConfiguration().contribInfo.selectionHighlight; - this.decorations = []; - this.updateSoon = this._register(new RunOnceScheduler(() => this._update(), 300)); - this.state = null; - - this._register(editor.onDidChangeConfiguration((e) => { - this._isEnabled = editor.getConfiguration().contribInfo.selectionHighlight; - })); - this._register(editor.onDidChangeCursorSelection((e: ICursorSelectionChangedEvent) => { - - if (!this._isEnabled) { - // Early exit if nothing needs to be done! - // Leave some form of early exit check here if you wish to continue being a cursor position change listener ;) - return; - } - - if (e.selection.isEmpty()) { - if (e.reason === CursorChangeReason.Explicit) { - if (this.state && (!this.state.lastWordUnderCursor || !this.state.lastWordUnderCursor.containsPosition(e.selection.getStartPosition()))) { - // no longer valid - this._setState(null); - } - this.updateSoon.schedule(); - } else { - this._setState(null); - - } - } else { - this._update(); - } - })); - this._register(editor.onDidChangeModel((e) => { - this._setState(null); - })); - this._register(CommonFindController.get(editor).getState().addChangeListener((e) => { - this._update(); - })); - } - - public getId(): string { - return SelectionHighlighter.ID; - } - - private _update(): void { - this._setState(SelectionHighlighter._createState(this._isEnabled, this.editor)); - } - - private static _createState(isEnabled: boolean, editor: editorCommon.ICommonCodeEditor): SelectionHighlighterState { - const model = editor.getModel(); - if (!model) { - return null; - } - - const config = editor.getConfiguration(); - - let lastWordUnderCursor: Selection = null; - if (!isEnabled) { - return null; - } - - const r = multiCursorFind(editor, { - changeFindSearchString: false, - allowMultiline: false, - highlightFindOptions: false - }); - if (!r) { - return null; - } - - const hasFindOccurrences = DocumentHighlightProviderRegistry.has(model); - if (r.currentMatch) { - // This is an empty selection - if (hasFindOccurrences) { - // Do not interfere with semantic word highlighting in the no selection case - return null; - } - - if (!config.contribInfo.occurrencesHighlight) { - return null; - } - - lastWordUnderCursor = r.currentMatch; - } - if (/^[ \t]+$/.test(r.searchText)) { - // whitespace only selection - return null; - } - if (r.searchText.length > 200) { - // very long selection - return null; - } - - const controller = CommonFindController.get(editor); - if (!controller) { - return null; - } - const findState = controller.getState(); - const caseSensitive = findState.matchCase; - - const selections = editor.getSelections(); - let firstSelectedText = model.getValueInRange(selections[0]); - if (!caseSensitive) { - firstSelectedText = firstSelectedText.toLowerCase(); - } - for (let i = 1; i < selections.length; i++) { - let selectedText = model.getValueInRange(selections[i]); - if (!caseSensitive) { - selectedText = selectedText.toLowerCase(); - } - if (firstSelectedText !== selectedText) { - // not all selections have the same text - return null; - } - } - - return new SelectionHighlighterState(lastWordUnderCursor, r.searchText, r.matchCase, r.wholeWord ? editor.getConfiguration().wordSeparators : null); - } - - - private _setState(state: SelectionHighlighterState): void { - if (SelectionHighlighterState.softEquals(this.state, state)) { - this.state = state; - return; - } - this.state = state; - - if (!this.state) { - if (this.decorations.length > 0) { - this.decorations = this.editor.deltaDecorations(this.decorations, []); - } - return; - } - - const model = this.editor.getModel(); - const hasFindOccurrences = DocumentHighlightProviderRegistry.has(model); - - let allMatches = model.findMatches(this.state.searchText, true, false, this.state.matchCase, this.state.wordSeparators, false).map(m => m.range); - allMatches.sort(Range.compareRangesUsingStarts); - - let selections = this.editor.getSelections(); - selections.sort(Range.compareRangesUsingStarts); - - // do not overlap with selection (issue #64 and #512) - let matches: Range[] = []; - for (let i = 0, j = 0, len = allMatches.length, lenJ = selections.length; i < len;) { - const match = allMatches[i]; - - if (j >= lenJ) { - // finished all editor selections - matches.push(match); - i++; - } else { - const cmp = Range.compareRangesUsingStarts(match, selections[j]); - if (cmp < 0) { - // match is before sel - matches.push(match); - i++; - } else if (cmp > 0) { - // sel is before match - j++; - } else { - // sel is equal to match - i++; - j++; - } - } - } - - const decorations = matches.map(r => { - return { - range: r, - // Show in overviewRuler only if model has no semantic highlighting - options: (hasFindOccurrences ? SelectionHighlighter._SELECTION_HIGHLIGHT : SelectionHighlighter._SELECTION_HIGHLIGHT_OVERVIEW) - }; - }); - - this.decorations = this.editor.deltaDecorations(this.decorations, decorations); - } - - private static _SELECTION_HIGHLIGHT_OVERVIEW = ModelDecorationOptions.register({ - stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, - className: 'selectionHighlight', - overviewRuler: { - color: themeColorFromId(overviewRulerSelectionHighlightForeground), - darkColor: themeColorFromId(overviewRulerSelectionHighlightForeground), - position: editorCommon.OverviewRulerLane.Center - } - }); - - private static _SELECTION_HIGHLIGHT = ModelDecorationOptions.register({ - stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, - className: 'selectionHighlight', - }); - - public dispose(): void { - this._setState(null); - super.dispose(); - } -} @editorAction export class ShowNextFindTermAction extends MatchFindAction { diff --git a/src/vs/editor/contrib/find/common/findDecorations.ts b/src/vs/editor/contrib/find/common/findDecorations.ts index 202e32602fa..09cb7eb07d0 100644 --- a/src/vs/editor/contrib/find/common/findDecorations.ts +++ b/src/vs/editor/contrib/find/common/findDecorations.ts @@ -16,6 +16,7 @@ export class FindDecorations implements IDisposable { private _editor: editorCommon.ICommonCodeEditor; private _decorations: string[]; + private _overviewRulerApproximateDecorations: string[]; private _findScopeDecorationId: string; private _rangeHighlightDecorationId: string; private _highlightedDecorationId: string; @@ -24,6 +25,7 @@ export class FindDecorations implements IDisposable { constructor(editor: editorCommon.ICommonCodeEditor) { this._editor = editor; this._decorations = []; + this._overviewRulerApproximateDecorations = []; this._findScopeDecorationId = null; this._rangeHighlightDecorationId = null; this._highlightedDecorationId = null; @@ -35,6 +37,7 @@ export class FindDecorations implements IDisposable { this._editor = null; this._decorations = []; + this._overviewRulerApproximateDecorations = []; this._findScopeDecorationId = null; this._rangeHighlightDecorationId = null; this._highlightedDecorationId = null; @@ -43,6 +46,7 @@ export class FindDecorations implements IDisposable { public reset(): void { this._decorations = []; + this._overviewRulerApproximateDecorations = []; this._findScopeDecorationId = null; this._rangeHighlightDecorationId = null; this._highlightedDecorationId = null; @@ -68,11 +72,21 @@ export class FindDecorations implements IDisposable { this.setCurrentFindMatch(null); } + private _getDecorationIndex(decorationId: string): number { + const index = this._decorations.indexOf(decorationId); + if (index >= 0) { + return index + 1; + } + return 1; + } + public getCurrentMatchesPosition(desiredRange: Range): number { - for (let i = 0, len = this._decorations.length; i < len; i++) { - let range = this._editor.getModel().getDecorationRange(this._decorations[i]); - if (desiredRange.equalsRange(range)) { - return (i + 1); + let candidates = this._editor.getModel().getDecorationsInRange(desiredRange); + for (let i = 0, len = candidates.length; i < len; i++) { + const candidate = candidates[i]; + const candidateOpts = candidate.options; + if (candidateOpts === FindDecorations._FIND_MATCH_DECORATION || candidateOpts === FindDecorations._CURRENT_FIND_MATCH_DECORATION) { + return this._getDecorationIndex(candidate.id); } } return 1; @@ -95,12 +109,12 @@ export class FindDecorations implements IDisposable { if (this._highlightedDecorationId !== null || newCurrentDecorationId !== null) { this._editor.changeDecorations((changeAccessor: editorCommon.IModelDecorationsChangeAccessor) => { if (this._highlightedDecorationId !== null) { - changeAccessor.changeDecorationOptions(this._highlightedDecorationId, FindDecorations.createFindMatchDecorationOptions(false)); + changeAccessor.changeDecorationOptions(this._highlightedDecorationId, FindDecorations._FIND_MATCH_DECORATION); this._highlightedDecorationId = null; } if (newCurrentDecorationId !== null) { this._highlightedDecorationId = newCurrentDecorationId; - changeAccessor.changeDecorationOptions(this._highlightedDecorationId, FindDecorations.createFindMatchDecorationOptions(true)); + changeAccessor.changeDecorationOptions(this._highlightedDecorationId, FindDecorations._CURRENT_FIND_MATCH_DECORATION); } if (this._rangeHighlightDecorationId !== null) { changeAccessor.removeDecoration(this._rangeHighlightDecorationId); @@ -121,34 +135,82 @@ export class FindDecorations implements IDisposable { return matchPosition; } - public set(matches: Range[], findScope: Range): void { - let newDecorations: editorCommon.IModelDeltaDecoration[] = matches.map((match) => { - return { - range: match, - options: FindDecorations.createFindMatchDecorationOptions(false) - }; - }); - if (findScope) { - newDecorations.unshift({ - range: findScope, - options: FindDecorations._FIND_SCOPE_DECORATION - }); - } - let tmpDecorations = this._editor.deltaDecorations(this._allDecorations(), newDecorations); + public set(findMatches: editorCommon.FindMatch[], findScope: Range): void { + this._editor.changeDecorations((accessor) => { - if (findScope) { - this._findScopeDecorationId = tmpDecorations.shift(); - } else { - this._findScopeDecorationId = null; - } - this._decorations = tmpDecorations; - this._rangeHighlightDecorationId = null; - this._highlightedDecorationId = null; + let findMatchesOptions: ModelDecorationOptions = FindDecorations._FIND_MATCH_DECORATION; + let newOverviewRulerApproximateDecorations: editorCommon.IModelDeltaDecoration[] = []; + + if (findMatches.length > 1000) { + // we go into a mode where the overview ruler gets "approximate" decorations + // the reason is that the overview ruler paints all the decorations in the file and we don't want to cause freezes + findMatchesOptions = FindDecorations._FIND_MATCH_NO_OVERVIEW_DECORATION; + + // approximate a distance in lines where matches should be merged + const lineCount = this._editor.getModel().getLineCount(); + const height = this._editor.getLayoutInfo().height; + const approxPixelsPerLine = height / lineCount; + const mergeLinesDelta = Math.max(2, Math.ceil(3 / approxPixelsPerLine)); + + // merge decorations as much as possible + let prevStartLineNumber = findMatches[0].range.startLineNumber; + let prevEndLineNumber = findMatches[0].range.endLineNumber; + for (let i = 1, len = findMatches.length; i < len; i++) { + const range = findMatches[i].range; + if (prevEndLineNumber + mergeLinesDelta >= range.startLineNumber) { + if (range.endLineNumber > prevEndLineNumber) { + prevEndLineNumber = range.endLineNumber; + } + } else { + newOverviewRulerApproximateDecorations.push({ + range: new Range(prevStartLineNumber, 1, prevEndLineNumber, 1), + options: FindDecorations._FIND_MATCH_ONLY_OVERVIEW_DECORATION + }); + prevStartLineNumber = range.startLineNumber; + prevEndLineNumber = range.endLineNumber; + } + } + + newOverviewRulerApproximateDecorations.push({ + range: new Range(prevStartLineNumber, 1, prevEndLineNumber, 1), + options: FindDecorations._FIND_MATCH_ONLY_OVERVIEW_DECORATION + }); + } + + // Find matches + let newFindMatchesDecorations: editorCommon.IModelDeltaDecoration[] = new Array(findMatches.length); + for (let i = 0, len = findMatches.length; i < len; i++) { + newFindMatchesDecorations[i] = { + range: findMatches[i].range, + options: findMatchesOptions + }; + } + this._decorations = accessor.deltaDecorations(this._decorations, newFindMatchesDecorations); + + // Overview ruler approximate decorations + this._overviewRulerApproximateDecorations = accessor.deltaDecorations(this._overviewRulerApproximateDecorations, newOverviewRulerApproximateDecorations); + + // Range highlight + if (this._rangeHighlightDecorationId) { + accessor.removeDecoration(this._rangeHighlightDecorationId); + this._rangeHighlightDecorationId = null; + } + + // Find scope + if (this._findScopeDecorationId) { + accessor.removeDecoration(this._findScopeDecorationId); + this._findScopeDecorationId = null; + } + if (findScope) { + this._findScopeDecorationId = accessor.addDecoration(findScope, FindDecorations._FIND_SCOPE_DECORATION); + } + }); } private _allDecorations(): string[] { let result: string[] = []; result = result.concat(this._decorations); + result = result.concat(this._overviewRulerApproximateDecorations); if (this._findScopeDecorationId) { result.push(this._findScopeDecorationId); } @@ -158,10 +220,6 @@ export class FindDecorations implements IDisposable { return result; } - private static createFindMatchDecorationOptions(isCurrent: boolean): ModelDecorationOptions { - return (isCurrent ? this._CURRENT_FIND_MATCH_DECORATION : this._FIND_MATCH_DECORATION); - } - private static _CURRENT_FIND_MATCH_DECORATION = ModelDecorationOptions.register({ stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, className: 'currentFindMatch', @@ -184,6 +242,21 @@ export class FindDecorations implements IDisposable { } }); + private static _FIND_MATCH_NO_OVERVIEW_DECORATION = ModelDecorationOptions.register({ + stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + className: 'findMatch', + showIfCollapsed: true + }); + + private static _FIND_MATCH_ONLY_OVERVIEW_DECORATION = ModelDecorationOptions.register({ + stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + overviewRuler: { + color: themeColorFromId(editorFindMatchHighlight), + darkColor: themeColorFromId(editorFindMatchHighlight), + position: editorCommon.OverviewRulerLane.Center + } + }); + private static _RANGE_HIGHLIGHT_DECORATION = ModelDecorationOptions.register({ stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, className: 'rangeHighlight', diff --git a/src/vs/editor/contrib/find/common/findModel.ts b/src/vs/editor/contrib/find/common/findModel.ts index be3705b47f7..1536e8c9541 100644 --- a/src/vs/editor/contrib/find/common/findModel.ts +++ b/src/vs/editor/contrib/find/common/findModel.ts @@ -50,10 +50,6 @@ export const FIND_IDS = { PreviousMatchFindAction: 'editor.action.previousMatchFindAction', NextSelectionMatchFindAction: 'editor.action.nextSelectionMatchFindAction', PreviousSelectionMatchFindAction: 'editor.action.previousSelectionMatchFindAction', - AddSelectionToNextFindMatchAction: 'editor.action.addSelectionToNextFindMatch', - AddSelectionToPreviousFindMatchAction: 'editor.action.addSelectionToPreviousFindMatch', - MoveSelectionToNextFindMatchAction: 'editor.action.moveSelectionToNextFindMatch', - MoveSelectionToPreviousFindMatchAction: 'editor.action.moveSelectionToPreviousFindMatch', StartFindReplaceAction: 'editor.action.startFindReplaceAction', CloseFindWidgetCommand: 'closeFindWidget', ToggleCaseSensitiveCommand: 'toggleFindCaseSensitive', @@ -67,7 +63,7 @@ export const FIND_IDS = { ShowNextFindTermAction: 'find.history.showNext' }; -export const MATCHES_LIMIT = 999; +export const MATCHES_LIMIT = 19999; export class FindModelBoundToEditorModel { @@ -130,6 +126,10 @@ export class FindModelBoundToEditorModel { // The find model is disposed during a find state changed event return; } + if (!this._editor.getModel()) { + // The find model will be disposed momentarily + return; + } if (e.searchString || e.isReplaceRevealed || e.isRegex || e.wholeWord || e.matchCase || e.searchScope) { if (e.searchScope) { this.research(e.moveCursor, this._state.searchScope); @@ -171,7 +171,7 @@ export class FindModelBoundToEditorModel { } let findMatches = this._findMatches(findScope, false, MATCHES_LIMIT); - this._decorations.set(findMatches.map(match => match.range), findScope); + this._decorations.set(findMatches, findScope); this._state.changeMatchInfo( this._decorations.getCurrentMatchesPosition(this._editor.getSelection()), diff --git a/src/vs/editor/contrib/find/common/findState.ts b/src/vs/editor/contrib/find/common/findState.ts index 0dccd531f7a..000ee49bfd5 100644 --- a/src/vs/editor/contrib/find/common/findState.ts +++ b/src/vs/editor/contrib/find/common/findState.ts @@ -25,17 +25,36 @@ export interface FindReplaceStateChangedEvent { currentMatch: boolean; } +export const enum FindOptionOverride { + NotSet = 0, + True = 1, + False = 2 +} + export interface INewFindReplaceState { searchString?: string; replaceString?: string; isRevealed?: boolean; isReplaceRevealed?: boolean; isRegex?: boolean; + isRegexOverride?: FindOptionOverride; wholeWord?: boolean; + wholeWordOverride?: FindOptionOverride; matchCase?: boolean; + matchCaseOverride?: FindOptionOverride; searchScope?: Range; } +function effectiveOptionValue(override: FindOptionOverride, value: boolean): boolean { + if (override === FindOptionOverride.True) { + return true; + } + if (override === FindOptionOverride.False) { + return false; + } + return value; +} + export class FindReplaceState implements IDisposable { private static _CHANGED_EVENT = 'changed'; @@ -45,8 +64,11 @@ export class FindReplaceState implements IDisposable { private _isRevealed: boolean; private _isReplaceRevealed: boolean; private _isRegex: boolean; + private _isRegexOverride: FindOptionOverride; private _wholeWord: boolean; + private _wholeWordOverride: FindOptionOverride; private _matchCase: boolean; + private _matchCaseOverride: FindOptionOverride; private _searchScope: Range; private _matchesPosition: number; private _matchesCount: number; @@ -57,9 +79,9 @@ export class FindReplaceState implements IDisposable { public get replaceString(): string { return this._replaceString; } public get isRevealed(): boolean { return this._isRevealed; } public get isReplaceRevealed(): boolean { return this._isReplaceRevealed; } - public get isRegex(): boolean { return this._isRegex; } - public get wholeWord(): boolean { return this._wholeWord; } - public get matchCase(): boolean { return this._matchCase; } + public get isRegex(): boolean { return effectiveOptionValue(this._isRegexOverride, this._isRegex); } + public get wholeWord(): boolean { return effectiveOptionValue(this._wholeWordOverride, this._wholeWord); } + public get matchCase(): boolean { return effectiveOptionValue(this._matchCaseOverride, this._matchCase); } public get searchScope(): Range { return this._searchScope; } public get matchesPosition(): number { return this._matchesPosition; } public get matchesCount(): number { return this._matchesCount; } @@ -71,8 +93,11 @@ export class FindReplaceState implements IDisposable { this._isRevealed = false; this._isReplaceRevealed = false; this._isRegex = false; + this._isRegexOverride = FindOptionOverride.NotSet; this._wholeWord = false; + this._wholeWordOverride = FindOptionOverride.NotSet; this._matchCase = false; + this._matchCaseOverride = FindOptionOverride.NotSet; this._searchScope = null; this._matchesPosition = 0; this._matchesCount = 0; @@ -155,6 +180,10 @@ export class FindReplaceState implements IDisposable { }; let somethingChanged = false; + const oldEffectiveIsRegex = this.isRegex; + const oldEffectiveWholeWords = this.wholeWord; + const oldEffectiveMatchCase = this.matchCase; + if (typeof newState.searchString !== 'undefined') { if (this._searchString !== newState.searchString) { this._searchString = newState.searchString; @@ -184,25 +213,13 @@ export class FindReplaceState implements IDisposable { } } if (typeof newState.isRegex !== 'undefined') { - if (this._isRegex !== newState.isRegex) { - this._isRegex = newState.isRegex; - changeEvent.isRegex = true; - somethingChanged = true; - } + this._isRegex = newState.isRegex; } if (typeof newState.wholeWord !== 'undefined') { - if (this._wholeWord !== newState.wholeWord) { - this._wholeWord = newState.wholeWord; - changeEvent.wholeWord = true; - somethingChanged = true; - } + this._wholeWord = newState.wholeWord; } if (typeof newState.matchCase !== 'undefined') { - if (this._matchCase !== newState.matchCase) { - this._matchCase = newState.matchCase; - changeEvent.matchCase = true; - somethingChanged = true; - } + this._matchCase = newState.matchCase; } if (typeof newState.searchScope !== 'undefined') { if (!Range.equalsRange(this._searchScope, newState.searchScope)) { @@ -212,6 +229,24 @@ export class FindReplaceState implements IDisposable { } } + // Overrides get set when they explicitly come in and get reset anytime something else changes + this._isRegexOverride = (typeof newState.isRegexOverride !== 'undefined' ? newState.isRegexOverride : FindOptionOverride.NotSet); + this._wholeWordOverride = (typeof newState.wholeWordOverride !== 'undefined' ? newState.wholeWordOverride : FindOptionOverride.NotSet); + this._matchCaseOverride = (typeof newState.matchCaseOverride !== 'undefined' ? newState.matchCaseOverride : FindOptionOverride.NotSet); + + if (oldEffectiveIsRegex !== this.isRegex) { + somethingChanged = true; + changeEvent.isRegex = true; + } + if (oldEffectiveWholeWords !== this.wholeWord) { + somethingChanged = true; + changeEvent.wholeWord = true; + } + if (oldEffectiveMatchCase !== this.matchCase) { + somethingChanged = true; + changeEvent.matchCase = true; + } + if (somethingChanged) { this._eventEmitter.emit(FindReplaceState._CHANGED_EVENT, changeEvent); } diff --git a/src/vs/editor/contrib/find/test/common/findController.test.ts b/src/vs/editor/contrib/find/test/common/findController.test.ts index 20559f5713b..323f3f0ce42 100644 --- a/src/vs/editor/contrib/find/test/common/findController.test.ts +++ b/src/vs/editor/contrib/find/test/common/findController.test.ts @@ -11,20 +11,16 @@ import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Position } from 'vs/editor/common/core/position'; import { Selection } from 'vs/editor/common/core/selection'; import { Range } from 'vs/editor/common/core/range'; -import { EndOfLineSequence, ICommonCodeEditor, Handler } from 'vs/editor/common/editorCommon'; -import { - CommonFindController, FindStartFocusAction, IFindStartOptions, - NextMatchFindAction, StartFindAction, SelectHighlightsAction, - AddSelectionToNextFindMatchAction -} from 'vs/editor/contrib/find/common/findController'; -import { MockCodeEditor, withMockCodeEditor } from 'vs/editor/test/common/mocks/mockCodeEditor'; +import { ICommonCodeEditor } from 'vs/editor/common/editorCommon'; +import { CommonFindController, FindStartFocusAction, IFindStartOptions, NextMatchFindAction, StartFindAction } from 'vs/editor/contrib/find/common/findController'; +import { withMockCodeEditor } from 'vs/editor/test/common/mocks/mockCodeEditor'; import { HistoryNavigator } from 'vs/base/common/history'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { Delayer } from 'vs/base/common/async'; -class TestFindController extends CommonFindController { +export class TestFindController extends CommonFindController { public hasFocus: boolean; public delayUpdateHistory: boolean = false; @@ -188,60 +184,6 @@ suite('FindController', () => { }); }); - test('issue #8817: Cursor position changes when you cancel multicursor', () => { - withMockCodeEditor([ - 'var x = (3 * 5)', - 'var y = (3 * 5)', - 'var z = (3 * 5)', - ], { serviceCollection: serviceCollection }, (editor, cursor) => { - - let findController = editor.registerAndInstantiateContribution(TestFindController); - let selectHighlightsAction = new SelectHighlightsAction(); - - editor.setSelection(new Selection(2, 9, 2, 16)); - - selectHighlightsAction.run(null, editor); - assert.deepEqual(editor.getSelections().map(fromRange), [ - [2, 9, 2, 16], - [1, 9, 1, 16], - [3, 9, 3, 16], - ]); - - editor.trigger('test', 'removeSecondaryCursors', null); - - assert.deepEqual(fromRange(editor.getSelection()), [2, 9, 2, 16]); - - findController.dispose(); - }); - }); - - test('issue #5400: "Select All Occurrences of Find Match" does not select all if find uses regex', () => { - withMockCodeEditor([ - 'something', - 'someething', - 'someeething', - 'nothing' - ], { serviceCollection: serviceCollection }, (editor, cursor) => { - - let findController = editor.registerAndInstantiateContribution(TestFindController); - let selectHighlightsAction = new SelectHighlightsAction(); - - editor.setSelection(new Selection(1, 1, 1, 1)); - findController.getState().change({ searchString: 'some+thing', isRegex: true, isRevealed: true }, false); - - selectHighlightsAction.run(null, editor); - assert.deepEqual(editor.getSelections().map(fromRange), [ - [1, 1, 1, 10], - [2, 1, 2, 11], - [3, 1, 3, 12], - ]); - - assert.equal(findController.getState().searchString, 'some+thing'); - - findController.dispose(); - }); - }); - test('issue #9043: Clear search scope when find widget is hidden', () => { withMockCodeEditor([ 'var x = (3 * 5)', @@ -380,119 +322,6 @@ suite('FindController', () => { }); }); - test('AddSelectionToNextFindMatchAction can work with multiline', () => { - withMockCodeEditor([ - '', - 'qwe', - 'rty', - '', - 'qwe', - '', - 'rty', - 'qwe', - 'rty' - ], { serviceCollection: serviceCollection }, (editor, cursor) => { - - let findController = editor.registerAndInstantiateContribution(TestFindController); - let addSelectionToNextFindMatch = new AddSelectionToNextFindMatchAction(); - - editor.setSelection(new Selection(2, 1, 3, 4)); - - addSelectionToNextFindMatch.run(null, editor); - assert.deepEqual(editor.getSelections().map(fromRange), [ - [2, 1, 3, 4], - [8, 1, 9, 4] - ]); - - editor.trigger('test', 'removeSecondaryCursors', null); - - assert.deepEqual(fromRange(editor.getSelection()), [2, 1, 3, 4]); - - findController.dispose(); - }); - }); - - test('issue #6661: AddSelectionToNextFindMatchAction can work with touching ranges', () => { - withMockCodeEditor([ - 'abcabc', - 'abc', - 'abcabc', - ], { serviceCollection: serviceCollection }, (editor, cursor) => { - - let findController = editor.registerAndInstantiateContribution(TestFindController); - let addSelectionToNextFindMatch = new AddSelectionToNextFindMatchAction(); - - editor.setSelection(new Selection(1, 1, 1, 4)); - - addSelectionToNextFindMatch.run(null, editor); - assert.deepEqual(editor.getSelections().map(fromRange), [ - [1, 1, 1, 4], - [1, 4, 1, 7] - ]); - - addSelectionToNextFindMatch.run(null, editor); - addSelectionToNextFindMatch.run(null, editor); - addSelectionToNextFindMatch.run(null, editor); - assert.deepEqual(editor.getSelections().map(fromRange), [ - [1, 1, 1, 4], - [1, 4, 1, 7], - [2, 1, 2, 4], - [3, 1, 3, 4], - [3, 4, 3, 7] - ]); - - editor.trigger('test', Handler.Type, { text: 'z' }); - assert.deepEqual(editor.getSelections().map(fromRange), [ - [1, 2, 1, 2], - [1, 3, 1, 3], - [2, 2, 2, 2], - [3, 2, 3, 2], - [3, 3, 3, 3] - ]); - assert.equal(editor.getValue(), [ - 'zz', - 'z', - 'zz', - ].join('\n')); - - findController.dispose(); - }); - }); - - test('issue #23541: Multiline Ctrl+D does not work in CRLF files', () => { - withMockCodeEditor([ - '', - 'qwe', - 'rty', - '', - 'qwe', - '', - 'rty', - 'qwe', - 'rty' - ], { serviceCollection: serviceCollection }, (editor, cursor) => { - - editor.getModel().setEOL(EndOfLineSequence.CRLF); - - let findController = editor.registerAndInstantiateContribution(TestFindController); - let addSelectionToNextFindMatch = new AddSelectionToNextFindMatchAction(); - - editor.setSelection(new Selection(2, 1, 3, 4)); - - addSelectionToNextFindMatch.run(null, editor); - assert.deepEqual(editor.getSelections().map(fromRange), [ - [2, 1, 3, 4], - [8, 1, 9, 4] - ]); - - editor.trigger('test', 'removeSecondaryCursors', null); - - assert.deepEqual(fromRange(editor.getSelection()), [2, 1, 3, 4]); - - findController.dispose(); - }); - }); - test('issue #18111: Regex replace with single space replaces with no space', () => { withMockCodeEditor([ 'HRESULT OnAmbientPropertyChange(DISPID dispid);' @@ -555,237 +384,6 @@ suite('FindController', () => { } return result; } - - function testAddSelectionToNextFindMatchAction(text: string[], callback: (editor: MockCodeEditor, action: AddSelectionToNextFindMatchAction, findController: TestFindController) => void): void { - withMockCodeEditor(text, { serviceCollection: serviceCollection }, (editor, cursor) => { - - let findController = editor.registerAndInstantiateContribution(TestFindController); - - let action = new AddSelectionToNextFindMatchAction(); - - callback(editor, action, findController); - - findController.dispose(); - }); - } - - test('AddSelectionToNextFindMatchAction starting with single collapsed selection', () => { - const text = [ - 'abc pizza', - 'abc house', - 'abc bar' - ]; - testAddSelectionToNextFindMatchAction(text, (editor, action, findController) => { - editor.setSelections([ - new Selection(1, 2, 1, 2), - ]); - - action.run(null, editor); - assert.deepEqual(editor.getSelections(), [ - new Selection(1, 1, 1, 4), - ]); - - action.run(null, editor); - assert.deepEqual(editor.getSelections(), [ - new Selection(1, 1, 1, 4), - new Selection(2, 1, 2, 4), - ]); - - action.run(null, editor); - assert.deepEqual(editor.getSelections(), [ - new Selection(1, 1, 1, 4), - new Selection(2, 1, 2, 4), - new Selection(3, 1, 3, 4), - ]); - - action.run(null, editor); - assert.deepEqual(editor.getSelections(), [ - new Selection(1, 1, 1, 4), - new Selection(2, 1, 2, 4), - new Selection(3, 1, 3, 4), - ]); - }); - }); - - test('AddSelectionToNextFindMatchAction starting with two selections, one being collapsed 1)', () => { - const text = [ - 'abc pizza', - 'abc house', - 'abc bar' - ]; - testAddSelectionToNextFindMatchAction(text, (editor, action, findController) => { - editor.setSelections([ - new Selection(1, 1, 1, 4), - new Selection(2, 2, 2, 2), - ]); - - action.run(null, editor); - assert.deepEqual(editor.getSelections(), [ - new Selection(1, 1, 1, 4), - new Selection(2, 1, 2, 4), - ]); - - action.run(null, editor); - assert.deepEqual(editor.getSelections(), [ - new Selection(1, 1, 1, 4), - new Selection(2, 1, 2, 4), - new Selection(3, 1, 3, 4), - ]); - - action.run(null, editor); - assert.deepEqual(editor.getSelections(), [ - new Selection(1, 1, 1, 4), - new Selection(2, 1, 2, 4), - new Selection(3, 1, 3, 4), - ]); - }); - }); - - test('AddSelectionToNextFindMatchAction starting with two selections, one being collapsed 2)', () => { - const text = [ - 'abc pizza', - 'abc house', - 'abc bar' - ]; - testAddSelectionToNextFindMatchAction(text, (editor, action, findController) => { - editor.setSelections([ - new Selection(1, 2, 1, 2), - new Selection(2, 1, 2, 4), - ]); - - action.run(null, editor); - assert.deepEqual(editor.getSelections(), [ - new Selection(1, 1, 1, 4), - new Selection(2, 1, 2, 4), - ]); - - action.run(null, editor); - assert.deepEqual(editor.getSelections(), [ - new Selection(1, 1, 1, 4), - new Selection(2, 1, 2, 4), - new Selection(3, 1, 3, 4), - ]); - - action.run(null, editor); - assert.deepEqual(editor.getSelections(), [ - new Selection(1, 1, 1, 4), - new Selection(2, 1, 2, 4), - new Selection(3, 1, 3, 4), - ]); - }); - }); - - test('AddSelectionToNextFindMatchAction starting with all collapsed selections', () => { - const text = [ - 'abc pizza', - 'abc house', - 'abc bar' - ]; - testAddSelectionToNextFindMatchAction(text, (editor, action, findController) => { - editor.setSelections([ - new Selection(1, 2, 1, 2), - new Selection(2, 2, 2, 2), - new Selection(3, 1, 3, 1), - ]); - - action.run(null, editor); - assert.deepEqual(editor.getSelections(), [ - new Selection(1, 1, 1, 4), - new Selection(2, 1, 2, 4), - new Selection(3, 1, 3, 4), - ]); - - action.run(null, editor); - assert.deepEqual(editor.getSelections(), [ - new Selection(1, 1, 1, 4), - new Selection(2, 1, 2, 4), - new Selection(3, 1, 3, 4), - ]); - }); - }); - - test('AddSelectionToNextFindMatchAction starting with all collapsed selections on different words', () => { - const text = [ - 'abc pizza', - 'abc house', - 'abc bar' - ]; - testAddSelectionToNextFindMatchAction(text, (editor, action, findController) => { - editor.setSelections([ - new Selection(1, 6, 1, 6), - new Selection(2, 6, 2, 6), - new Selection(3, 6, 3, 6), - ]); - - action.run(null, editor); - assert.deepEqual(editor.getSelections(), [ - new Selection(1, 5, 1, 10), - new Selection(2, 5, 2, 10), - new Selection(3, 5, 3, 8), - ]); - - action.run(null, editor); - assert.deepEqual(editor.getSelections(), [ - new Selection(1, 5, 1, 10), - new Selection(2, 5, 2, 10), - new Selection(3, 5, 3, 8), - ]); - }); - }); - - test('issue #20651: AddSelectionToNextFindMatchAction case insensitive', () => { - const text = [ - 'test', - 'testte', - 'Test', - 'testte', - 'test' - ]; - testAddSelectionToNextFindMatchAction(text, (editor, action, findController) => { - editor.setSelections([ - new Selection(1, 1, 1, 5), - ]); - - action.run(null, editor); - assert.deepEqual(editor.getSelections(), [ - new Selection(1, 1, 1, 5), - new Selection(2, 1, 2, 5), - ]); - - action.run(null, editor); - assert.deepEqual(editor.getSelections(), [ - new Selection(1, 1, 1, 5), - new Selection(2, 1, 2, 5), - new Selection(3, 1, 3, 5), - ]); - - action.run(null, editor); - assert.deepEqual(editor.getSelections(), [ - new Selection(1, 1, 1, 5), - new Selection(2, 1, 2, 5), - new Selection(3, 1, 3, 5), - new Selection(4, 1, 4, 5), - ]); - - action.run(null, editor); - assert.deepEqual(editor.getSelections(), [ - new Selection(1, 1, 1, 5), - new Selection(2, 1, 2, 5), - new Selection(3, 1, 3, 5), - new Selection(4, 1, 4, 5), - new Selection(5, 1, 5, 5), - ]); - - action.run(null, editor); - assert.deepEqual(editor.getSelections(), [ - new Selection(1, 1, 1, 5), - new Selection(2, 1, 2, 5), - new Selection(3, 1, 3, 5), - new Selection(4, 1, 4, 5), - new Selection(5, 1, 5, 5), - ]); - }); - }); }); suite('FindController query options persistence', () => { diff --git a/src/vs/editor/contrib/folding/browser/folding.ts b/src/vs/editor/contrib/folding/browser/folding.ts index 5cac2cdf29d..e9f50ac58f7 100644 --- a/src/vs/editor/contrib/folding/browser/folding.ts +++ b/src/vs/editor/contrib/folding/browser/folding.ts @@ -9,67 +9,82 @@ import * as nls from 'vs/nls'; import * as types from 'vs/base/common/types'; import * as dom from 'vs/base/browser/dom'; -import { RunOnceScheduler } from 'vs/base/common/async'; +import { RunOnceScheduler, Delayer } from 'vs/base/common/async'; import { KeyCode, KeyMod, KeyChord } from 'vs/base/common/keyCodes'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { TPromise } from 'vs/base/common/winjs.base'; -import * as editorCommon from 'vs/editor/common/editorCommon'; -import { Range } from 'vs/editor/common/core/range'; +import { ICommonCodeEditor, ScrollType } from 'vs/editor/common/editorCommon'; import { editorAction, ServicesAccessor, EditorAction, CommonEditorRegistry } from 'vs/editor/common/editorCommonExtensions'; import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions'; -import { CollapsibleRegion, getCollapsibleRegionsToFoldAtLine, getCollapsibleRegionsToUnfoldAtLine, doesLineBelongsToCollapsibleRegion, IFoldingRange } from 'vs/editor/contrib/folding/common/foldingModel'; +import { FoldingModel, setCollapseStateAtLevel, setCollapseStateDown, CollapseMemento, setCollapseStateLevelsDown, setCollapseStateLevelsUp } from 'vs/editor/contrib/folding/common/foldingModel'; import { computeRanges, limitByIndent } from 'vs/editor/contrib/folding/common/indentFoldStrategy'; -import { IFoldingController, ID } from 'vs/editor/contrib/folding/common/folding'; -import { Selection } from 'vs/editor/common/core/selection'; +import { FoldingDecorationProvider } from './foldingDecorations'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions'; +import { IMarginData } from 'vs/editor/browser/controller/mouseTarget'; +import { HiddenRangeModel } from 'vs/editor/contrib/folding/common/hiddenRangeModel'; +import { IRange } from 'vs/editor/common/core/range'; + +export const ID = 'editor.contrib.folding'; @editorContribution -export class FoldingController implements IFoldingController { +export class FoldingController { static MAX_FOLDING_REGIONS = 5000; - public static get(editor: editorCommon.ICommonCodeEditor): FoldingController { + + public static get(editor: ICommonCodeEditor): FoldingController { return editor.getContribution(ID); } private editor: ICodeEditor; private _isEnabled: boolean; - private _showFoldingControls: 'always' | 'mouseover'; + private _autoHideFoldingControls: boolean; + + private foldingDecorationProvider: FoldingDecorationProvider; + + private foldingModel: FoldingModel; + private hiddenRangeModel: HiddenRangeModel; + + private foldingModelPromise: TPromise; + private updateScheduler: Delayer; + private globalToDispose: IDisposable[]; - private computeToken: number; private cursorChangedScheduler: RunOnceScheduler; - private contentChangedScheduler: RunOnceScheduler; - private localToDispose: IDisposable[]; - private decorations: CollapsibleRegion[]; + private localToDispose: IDisposable[]; constructor(editor: ICodeEditor) { this.editor = editor; this._isEnabled = this.editor.getConfiguration().contribInfo.folding; - this._showFoldingControls = this.editor.getConfiguration().contribInfo.showFoldingControls; + this._autoHideFoldingControls = this.editor.getConfiguration().contribInfo.showFoldingControls === 'mouseover'; this.globalToDispose = []; this.localToDispose = []; - this.decorations = []; - this.computeToken = 0; + + this.foldingDecorationProvider = new FoldingDecorationProvider(); + this.foldingDecorationProvider.autoHideFoldingControls = this._autoHideFoldingControls; this.globalToDispose.push(this.editor.onDidChangeModel(() => this.onModelChanged())); + this.globalToDispose.push(this.editor.onDidChangeConfiguration((e: IConfigurationChangedEvent) => { - let oldIsEnabled = this._isEnabled; - this._isEnabled = this.editor.getConfiguration().contribInfo.folding; - if (oldIsEnabled !== this._isEnabled) { - this.onModelChanged(); - } - let oldShowFoldingControls = this._showFoldingControls; - this._showFoldingControls = this.editor.getConfiguration().contribInfo.showFoldingControls; - if (oldShowFoldingControls !== this._showFoldingControls) { - this.updateHideFoldIconClass(); + if (e.contribInfo) { + let oldIsEnabled = this._isEnabled; + this._isEnabled = this.editor.getConfiguration().contribInfo.folding; + if (oldIsEnabled !== this._isEnabled) { + this.onModelChanged(); + } + let oldShowFoldingControls = this._autoHideFoldingControls; + this._autoHideFoldingControls = this.editor.getConfiguration().contribInfo.showFoldingControls === 'mouseover'; + if (oldShowFoldingControls !== this._autoHideFoldingControls) { + this.foldingDecorationProvider.autoHideFoldingControls = this._autoHideFoldingControls; + this.onModelContentChanged(); + } } })); - + this.globalToDispose.push({ dispose: () => dispose(this.localToDispose) }); this.onModelChanged(); } @@ -78,226 +93,131 @@ export class FoldingController implements IFoldingController { } public dispose(): void { - this.cleanState(); this.globalToDispose = dispose(this.globalToDispose); } - private updateHideFoldIconClass(): void { - let domNode = this.editor.getDomNode(); - if (domNode) { - dom.toggleClass(domNode, 'alwaysShowFoldIcons', this._showFoldingControls === 'always'); - } - } - /** * Store view state. */ - public saveViewState(): any { + public saveViewState(): { collapsedRegions?: CollapseMemento, lineCount?: number } { let model = this.editor.getModel(); - if (!model) { + if (!model || !this._isEnabled) { return {}; } - var collapsedRegions: IFoldingRange[] = []; - this.decorations.forEach(d => { - if (d.isCollapsed) { - var range = d.getDecorationRange(model); - if (range) { - collapsedRegions.push({ startLineNumber: range.startLineNumber, endLineNumber: range.endLineNumber, indent: d.indent, isCollapsed: true }); - } - } - }); - return { collapsedRegions: collapsedRegions, lineCount: model.getLineCount() }; + return { collapsedRegions: this.foldingModel.getMemento(), lineCount: model.getLineCount() }; } /** * Restore view state. */ - public restoreViewState(state: any): void { + public restoreViewState(state: { collapsedRegions?: CollapseMemento, lineCount?: number }): void { let model = this.editor.getModel(); - if (!model) { + if (!model || !this._isEnabled) { return; } - if (!this._isEnabled) { + if (!state || !state.collapsedRegions || state.lineCount !== model.getLineCount()) { return; } - if (!state || !Array.isArray(state.collapsedRegions) || state.collapsedRegions.length === 0 || state.lineCount !== model.getLineCount()) { - return; - } - let newFolded = state.collapsedRegions; - if (this.decorations.length > 0) { - let hasChanges = false; - let i = 0; - this.editor.changeDecorations(changeAccessor => { - this.decorations.forEach(d => { - if (i === newFolded.length || d.startLineNumber < newFolded[i].startLineNumber) { - if (d.isCollapsed) { - d.setCollapsed(false, changeAccessor); - hasChanges = true; - } - } else if (d.startLineNumber === newFolded[i].startLineNumber) { - if (!d.isCollapsed) { - d.setCollapsed(true, changeAccessor); - hasChanges = true; - } - i++; - } else { - return; // folding regions doesn't match, don't try to restore - } - }); - }); - if (hasChanges) { - this.updateHiddenAreas(void 0); - } - } - } - - private cleanState(): void { - this.localToDispose = dispose(this.localToDispose); - } - - private applyRegions(regions: IFoldingRange[]) { - let model = this.editor.getModel(); - if (!model) { - return; - } - let updateHiddenRegions = false; - regions = limitByIndent(regions, FoldingController.MAX_FOLDING_REGIONS).sort((r1, r2) => r1.startLineNumber - r2.startLineNumber); - - this.editor.changeDecorations(changeAccessor => { - - let newDecorations: CollapsibleRegion[] = []; - - let k = 0, i = 0; - while (i < this.decorations.length && k < regions.length) { - let dec = this.decorations[i]; - let decRange = dec.getDecorationRange(model); - if (!decRange) { - updateHiddenRegions = updateHiddenRegions || dec.isCollapsed; - dec.dispose(changeAccessor); - i++; - } else { - while (k < regions.length && decRange.startLineNumber > regions[k].startLineNumber) { - let region = regions[k]; - updateHiddenRegions = updateHiddenRegions || region.isCollapsed; - newDecorations.push(new CollapsibleRegion(region, model, changeAccessor)); - k++; - } - if (k < regions.length) { - let currRange = regions[k]; - if (decRange.startLineNumber < currRange.startLineNumber) { - updateHiddenRegions = updateHiddenRegions || dec.isCollapsed; - dec.dispose(changeAccessor); - i++; - } else if (decRange.startLineNumber === currRange.startLineNumber) { - if (dec.isCollapsed && (dec.startLineNumber !== currRange.startLineNumber || dec.endLineNumber !== currRange.endLineNumber)) { - updateHiddenRegions = true; - } - currRange.isCollapsed = dec.isCollapsed; // preserve collapse state - dec.update(currRange, model, changeAccessor); - newDecorations.push(dec); - i++; - k++; - } - } + // set the hidden ranges right away, before waiting for the folding model. + if (this.hiddenRangeModel.applyMemento(state.collapsedRegions)) { + this.getFoldingModel().then(foldingModel => { + if (foldingModel) { + foldingModel.applyMemento(state.collapsedRegions); } - } - while (i < this.decorations.length) { - let dec = this.decorations[i]; - updateHiddenRegions = updateHiddenRegions || dec.isCollapsed; - dec.dispose(changeAccessor); - i++; - } - while (k < regions.length) { - let region = regions[k]; - updateHiddenRegions = updateHiddenRegions || region.isCollapsed; - newDecorations.push(new CollapsibleRegion(region, model, changeAccessor)); - k++; - } - this.decorations = newDecorations; - }); - if (updateHiddenRegions) { - this.updateHiddenAreas(); + }); } - } private onModelChanged(): void { - this.cleanState(); - this.updateHideFoldIconClass(); + this.localToDispose = dispose(this.localToDispose); let model = this.editor.getModel(); if (!this._isEnabled || !model) { return; } - this.computeAndApplyCollapsibleRegions(); - this.contentChangedScheduler = new RunOnceScheduler(() => this.computeAndApplyCollapsibleRegions(), 200); + this.foldingModel = new FoldingModel(model, this.foldingDecorationProvider); + this.localToDispose.push(this.foldingModel); + + this.hiddenRangeModel = new HiddenRangeModel(this.foldingModel); + this.localToDispose.push(this.hiddenRangeModel); + this.localToDispose.push(this.hiddenRangeModel.onDidChange(hr => this.onHiddenRangesChanges(hr))); + + this.updateScheduler = new Delayer(200); + this.cursorChangedScheduler = new RunOnceScheduler(() => this.revealCursor(), 200); - 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) => { - - if (!this._isEnabled) { - // Early exit if nothing needs to be done! - // Leave some form of early exit check here if you wish to continue being a cursor position change listener ;) - return; - } - - this.cursorChangedScheduler.schedule(); - })); + this.localToDispose.push(this.editor.onDidChangeModelLanguageConfiguration(e => this.onModelContentChanged())); // covers model language changes as well + this.localToDispose.push(this.editor.onDidChangeModelContent(e => this.onModelContentChanged())); + this.localToDispose.push(this.editor.onDidChangeCursorPosition(e => this.onCursorPositionChanged())); this.localToDispose.push(this.editor.onMouseDown(e => this.onEditorMouseDown(e))); this.localToDispose.push(this.editor.onMouseUp(e => this.onEditorMouseUp(e))); - - this.localToDispose.push({ dispose: () => this.disposeDecorations() }); - } - - private computeAndApplyCollapsibleRegions(): void { - let model = this.editor.getModel(); - this.applyRegions(model ? computeRanges(model) : []); - } - - private disposeDecorations() { - this.editor.changeDecorations(changeAccessor => { - this.decorations.forEach(dec => dec.dispose(changeAccessor)); + this.localToDispose.push({ + dispose: () => { + this.updateScheduler.cancel(); + this.updateScheduler = null; + this.foldingModel = null; + this.foldingModelPromise = null; + this.hiddenRangeModel = null; + this.cursorChangedScheduler = null; + } }); - this.decorations = []; - this.editor.setHiddenAreas([]); + this.onModelContentChanged(); + } + + private computeRanges() { + let editorModel = this.editor.getModel(); + if (editorModel) { + let ranges = computeRanges(editorModel); + ranges = limitByIndent(ranges, FoldingController.MAX_FOLDING_REGIONS).sort((r1, r2) => r1.startLineNumber - r2.startLineNumber); + return ranges; + } + + return []; + } + + public getFoldingModel() { + return this.foldingModelPromise; + } + + private onModelContentChanged() { + this.foldingModelPromise = this.updateScheduler.trigger(() => { + if (this.foldingModel) { // null if editor has been disposed, or folding turned off + this.foldingModel.update(this.computeRanges()); + } + return this.foldingModel; + }); + } + + private onHiddenRangesChanges(hiddenRanges: IRange[]) { + let selections = this.editor.getSelections(); + if (this.hiddenRangeModel.adjustSelections(selections)) { + this.editor.setSelections(selections); + } + this.editor.setHiddenAreas(hiddenRanges); + } + + private onCursorPositionChanged() { + if (this.hiddenRangeModel.hasRanges()) { + this.cursorChangedScheduler.schedule(); + } } private revealCursor() { - let model = this.editor.getModel(); - if (!model) { - return; - } - let hasChanges = false; - let selections = this.editor.getSelections(); - - this.editor.changeDecorations(changeAccessor => { - return this.decorations.forEach(dec => { - if (dec.isCollapsed) { - let decRange = dec.getDecorationRange(model); - if (decRange) { - for (let selection of selections) { - // reveal if cursor in in one of the collapsed line (not the first) - if (decRange.startLineNumber < selection.selectionStartLineNumber && selection.selectionStartLineNumber <= decRange.endLineNumber) { - dec.setCollapsed(false, changeAccessor); - hasChanges = true; - break; - } - } + this.getFoldingModel().then(foldingModel => { // null is returned if folding got disabled in the meantime + if (foldingModel) { + let selections = this.editor.getSelections(); + for (let selection of selections) { + let lineNumber = selection.selectionStartLineNumber; + if (this.hiddenRangeModel.isHidden(lineNumber)) { + let toToggle = foldingModel.getAllRegionsAtLine(lineNumber, r => r.isCollapsed && lineNumber > r.range.startLineNumber); + foldingModel.toggleCollapseState(toToggle); } } - }); + } }); - if (hasChanges) { - this.updateHiddenAreas(this.editor.getPosition().lineNumber); - } + } private mouseDownInfo: { lineNumber: number, iconClicked: boolean }; @@ -305,9 +225,6 @@ export class FoldingController implements IFoldingController { private onEditorMouseDown(e: IEditorMouseEvent): void { this.mouseDownInfo = null; - if (this.decorations.length === 0) { - return; - } let range = e.target.range; if (!range) { return; @@ -315,16 +232,21 @@ export class FoldingController implements IFoldingController { if (!e.event.leftButton) { return; } - - let model = this.editor.getModel(); - - let iconClicked = false; switch (e.target.type) { case MouseTargetType.GUTTER_LINE_DECORATIONS: + const data = e.target.detail as IMarginData; + const gutterOffsetX = data.offsetX - data.glyphMarginWidth - data.lineNumbersWidth; + + // TODO@joao TODO@alex TODO@martin this is such that we don't collide with dirty diff + if (gutterOffsetX <= 12) { + return; + } + iconClicked = true; break; - case MouseTargetType.CONTENT_EMPTY: + case MouseTargetType.CONTENT_EMPTY: { + let model = this.editor.getModel(); if (range.startColumn === model.getLineMaxColumn(range.startLineNumber)) { let editorCoords = dom.getDomNodePagePosition(this.editor.getDomNode()); let pos = this.editor.getScrolledVisiblePosition(range.getEndPosition()); @@ -334,11 +256,14 @@ export class FoldingController implements IFoldingController { break; } return; - case MouseTargetType.CONTENT_TEXT: + } + case MouseTargetType.CONTENT_TEXT: { + let model = this.editor.getModel(); if (range.startColumn === model.getLineMaxColumn(range.startLineNumber)) { break; } return; + } default: return; } @@ -358,219 +283,71 @@ export class FoldingController implements IFoldingController { return; } - let model = this.editor.getModel(); - if (iconClicked) { if (e.target.type !== MouseTargetType.GUTTER_LINE_DECORATIONS) { return; } } else { + let model = this.editor.getModel(); if (range.startColumn !== model.getLineMaxColumn(lineNumber)) { return; } } - this.editor.changeDecorations(changeAccessor => { - for (let i = 0; i < this.decorations.length; i++) { - let dec = this.decorations[i]; - let decRange = dec.getDecorationRange(model); - if (decRange && decRange.startLineNumber === lineNumber) { - if (iconClicked || dec.isCollapsed) { - dec.setCollapsed(!dec.isCollapsed, changeAccessor); - this.updateHiddenAreas(lineNumber); + this.getFoldingModel().then(foldingModel => { + if (foldingModel) { + let region = foldingModel.getRegionAtLine(lineNumber); + if (region) { + if (iconClicked || region.isCollapsed) { + foldingModel.toggleCollapseState([region]); + this.reveal(lineNumber); } - return; } } }); } - private updateHiddenAreas(focusLine?: number): void { - let model = this.editor.getModel(); - var selections: Selection[] = this.editor.getSelections(); - var updateSelections = false; - let hiddenAreas: Range[] = []; - this.decorations.filter(dec => dec.isCollapsed).forEach(dec => { - let decRange = dec.getDecorationRange(model); - if (!decRange) { - return; - } - let isLineHidden = (line: number) => line > decRange.startLineNumber && line <= decRange.endLineNumber; - hiddenAreas.push(new Range(decRange.startLineNumber + 1, 1, decRange.endLineNumber, 1)); - selections.forEach((selection, i) => { - if (isLineHidden(selection.getStartPosition().lineNumber)) { - selections[i] = selection = selection.setStartPosition(decRange.startLineNumber, model.getLineMaxColumn(decRange.startLineNumber)); - updateSelections = true; - } - if (isLineHidden(selection.getEndPosition().lineNumber)) { - selections[i] = selection.setEndPosition(decRange.startLineNumber, model.getLineMaxColumn(decRange.startLineNumber)); - updateSelections = true; - } - }); - }); - if (updateSelections) { - this.editor.setSelections(selections); - } - this.editor.setHiddenAreas(hiddenAreas); - if (focusLine) { - this.editor.revealPositionInCenterIfOutsideViewport({ lineNumber: focusLine, column: 1 }, editorCommon.ScrollType.Smooth); - } - } - - public unfold(levels: number): void { - let model = this.editor.getModel(); - let hasChanges = false; - let selections = this.editor.getSelections(); - let selectionsHasChanged = false; - selections.forEach((selection, index) => { - let toUnfold: CollapsibleRegion[] = getCollapsibleRegionsToUnfoldAtLine(this.decorations, model, selection.startLineNumber, levels); - if (toUnfold.length > 0) { - toUnfold.forEach((collapsibleRegion, index) => { - this.editor.changeDecorations(changeAccessor => { - collapsibleRegion.setCollapsed(false, changeAccessor); - hasChanges = true; - }); - }); - if (!doesLineBelongsToCollapsibleRegion(toUnfold[0].foldingRange, selection.startLineNumber)) { - let lineNumber = toUnfold[0].startLineNumber, column = model.getLineMaxColumn(toUnfold[0].startLineNumber); - selections[index] = selection.setEndPosition(lineNumber, column).setStartPosition(lineNumber, column); - selectionsHasChanged = true; - } - } - }); - if (selectionsHasChanged) { - this.editor.setSelections(selections); - } - if (hasChanges) { - this.updateHiddenAreas(selections[0].startLineNumber); - } - } - - public fold(levels: number, up: boolean): void { - let hasChanges = false; - let selections = this.editor.getSelections(); - selections.forEach(selection => { - let lineNumber = selection.startLineNumber; - let toFold: CollapsibleRegion[] = getCollapsibleRegionsToFoldAtLine(this.decorations, this.editor.getModel(), lineNumber, levels, up); - toFold.forEach(collapsibleRegion => this.editor.changeDecorations(changeAccessor => { - collapsibleRegion.setCollapsed(true, changeAccessor); - hasChanges = true; - })); - }); - if (hasChanges) { - this.updateHiddenAreas(selections[0].startLineNumber); - } - } - - public foldUnfoldRecursively(isFold: boolean): void { - let hasChanges = false; - let model = this.editor.getModel(); - let selections = this.editor.getSelections(); - selections.forEach(selection => { - let lineNumber = selection.startLineNumber; - let endLineNumber: number; - let decToFoldUnfold: CollapsibleRegion[] = []; - for (let i = 0, len = this.decorations.length; i < len; i++) { - let dec = this.decorations[i]; - let decRange = dec.getDecorationRange(model); - if (!decRange) { - continue; - } - if (decRange.startLineNumber >= lineNumber && (decRange.endLineNumber <= endLineNumber || typeof endLineNumber === 'undefined')) { - //Protect against cursor not being in decoration and lower decoration folding/unfolding - if (decRange.startLineNumber !== lineNumber && typeof endLineNumber === 'undefined') { - return; - } - endLineNumber = endLineNumber || decRange.endLineNumber; - decToFoldUnfold.push(dec); - } - }; - if (decToFoldUnfold.length > 0) { - decToFoldUnfold.forEach(dec => { - this.editor.changeDecorations(changeAccessor => { - dec.setCollapsed(isFold, changeAccessor); - hasChanges = true; - }); - }); - } - }); - if (hasChanges) { - this.updateHiddenAreas(selections[0].startLineNumber); - } - } - - public foldAll(): void { - this.changeAll(true); - } - - public unfoldAll(): void { - this.changeAll(false); - } - - private changeAll(collapse: boolean): void { - if (this.decorations.length > 0) { - let hasChanges = true; - this.editor.changeDecorations(changeAccessor => { - this.decorations.forEach(d => { - if (collapse !== d.isCollapsed) { - d.setCollapsed(collapse, changeAccessor); - hasChanges = true; - } - }); - }); - if (hasChanges) { - this.updateHiddenAreas(this.editor.getPosition().lineNumber); - } - } - } - - public foldLevel(foldLevel: number, selectedLineNumbers: number[]): void { - let model = this.editor.getModel(); - let foldingRegionStack: Range[] = [model.getFullModelRange()]; // sentinel - - let hasChanges = false; - this.editor.changeDecorations(changeAccessor => { - this.decorations.forEach(dec => { - let decRange = dec.getDecorationRange(model); - if (decRange) { - while (!Range.containsRange(foldingRegionStack[foldingRegionStack.length - 1], decRange)) { - foldingRegionStack.pop(); - } - foldingRegionStack.push(decRange); - if (foldingRegionStack.length === foldLevel + 1 && !dec.isCollapsed && !selectedLineNumbers.some(lineNumber => decRange.startLineNumber < lineNumber && lineNumber <= decRange.endLineNumber)) { - dec.setCollapsed(true, changeAccessor); - hasChanges = true; - } - } - }); - }); - if (hasChanges) { - this.updateHiddenAreas(selectedLineNumbers[0]); - } + public reveal(focusLine: number): void { + this.editor.revealPositionInCenterIfOutsideViewport({ lineNumber: focusLine, column: 1 }, ScrollType.Smooth); } } abstract class FoldingAction extends EditorAction { - abstract invoke(foldingController: FoldingController, editor: editorCommon.ICommonCodeEditor, args: T): void; + abstract invoke(foldingController: FoldingController, foldingModel: FoldingModel, editor: ICommonCodeEditor, args: T): void; - public runEditorCommand(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor, args: T): void | TPromise { + public runEditorCommand(accessor: ServicesAccessor, editor: ICommonCodeEditor, args: T): void | TPromise { let foldingController = FoldingController.get(editor); if (!foldingController) { return; } this.reportTelemetry(accessor, editor); - this.invoke(foldingController, editor, args); + return foldingController.getFoldingModel().then(foldingModel => { + if (foldingModel) { + this.invoke(foldingController, foldingModel, editor, args); + } + }); } - public run(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor): void { + protected getSelectedLines(editor: ICommonCodeEditor) { + return editor.getSelections().map(s => s.startLineNumber); + } + protected getLineNumbers(args: FoldingArguments, editor: ICommonCodeEditor) { + if (args && args.selectionLines) { + return args.selectionLines.map(l => l + 1); // to 0-bases line numbers + } + return this.getSelectedLines(editor); + } + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void { } } interface FoldingArguments { levels?: number; direction?: 'up' | 'down'; + selectionLines?: number[]; } function foldingArgumentsConstraint(args: any) { @@ -585,6 +362,9 @@ function foldingArgumentsConstraint(args: any) { if (!types.isUndefined(foldingArgs.direction) && !types.isString(foldingArgs.direction)) { return false; } + if (!types.isUndefined(foldingArgs.selectionLines) && (!types.isArray(foldingArgs.selectionLines) || !foldingArgs.selectionLines.every(types.isNumber))) { + return false; + } } return true; } @@ -611,7 +391,9 @@ class UnfoldAction extends FoldingAction { { name: 'Unfold editor argument', description: `Property-value pairs that can be passed through this argument: - * 'level': Number of levels to unfold + * 'levels': Number of levels to unfold. If not set, defaults to 1. + * 'direction': If 'up', unfold given number of levels up otherwise unfolds down + * 'selectionLines': The start lines (0-based) of the editor selections to apply the unfold action to. If not set, the active selection(s) will be used. `, constraint: foldingArgumentsConstraint } @@ -620,8 +402,14 @@ class UnfoldAction extends FoldingAction { }); } - invoke(foldingController: FoldingController, editor: editorCommon.ICommonCodeEditor, args: FoldingArguments): void { - foldingController.unfold(args ? args.levels || 1 : 1); + invoke(foldingController: FoldingController, foldingModel: FoldingModel, editor: ICommonCodeEditor, args: FoldingArguments): void { + let levels = args && args.levels || 1; + let lineNumbers = this.getLineNumbers(args, editor); + if (args && args.direction === 'up') { + setCollapseStateLevelsUp(foldingModel, levels, false, lineNumbers); + } else { + setCollapseStateLevelsDown(foldingModel, levels, false, lineNumbers); + } } } @@ -641,8 +429,8 @@ class UnFoldRecursivelyAction extends FoldingAction { }); } - invoke(foldingController: FoldingController, editor: editorCommon.ICommonCodeEditor, args: any): void { - foldingController.foldUnfoldRecursively(false); + invoke(foldingController: FoldingController, foldingModel: FoldingModel, editor: ICommonCodeEditor, args: any): void { + setCollapseStateDown(foldingModel, false, this.getSelectedLines(editor)); } } @@ -668,8 +456,9 @@ class FoldAction extends FoldingAction { { name: 'Fold editor argument', description: `Property-value pairs that can be passed through this argument: - * 'levels': Number of levels to fold - * 'up': If 'true', folds given number of levels up otherwise folds down + * 'levels': Number of levels to fold. Defauts to 1 + * 'direction': If 'up', folds given number of levels up otherwise folds down + * 'selectionLines': The start lines (0-based) of the editor selections to apply the fold action to. If not set, the active selection(s) will be used. `, constraint: foldingArgumentsConstraint } @@ -678,9 +467,14 @@ class FoldAction extends FoldingAction { }); } - invoke(foldingController: FoldingController, editor: editorCommon.ICommonCodeEditor, args: FoldingArguments): void { - args = args ? args : { levels: 1, direction: 'up' }; - foldingController.fold(args.levels || 1, args.direction === 'up'); + invoke(foldingController: FoldingController, foldingModel: FoldingModel, editor: ICommonCodeEditor, args: FoldingArguments): void { + let levels = args && args.levels || 1; + let lineNumbers = this.getLineNumbers(args, editor); + if (args && args.direction === 'up') { + setCollapseStateLevelsUp(foldingModel, levels, true, lineNumbers); + } else { + setCollapseStateLevelsDown(foldingModel, levels, true, lineNumbers); + } } } @@ -700,8 +494,13 @@ class FoldRecursivelyAction extends FoldingAction { }); } - invoke(foldingController: FoldingController, editor: editorCommon.ICommonCodeEditor): void { - foldingController.foldUnfoldRecursively(true); + invoke(foldingController: FoldingController, foldingModel: FoldingModel, editor: ICommonCodeEditor): void { + let selectedLines = this.getSelectedLines(editor); + setCollapseStateDown(foldingModel, true, selectedLines); + if (selectedLines.length > 0) { + foldingController.reveal(selectedLines[0]); + } + } } @@ -721,8 +520,8 @@ class FoldAllAction extends FoldingAction { }); } - invoke(foldingController: FoldingController, editor: editorCommon.ICommonCodeEditor): void { - foldingController.foldAll(); + invoke(foldingController: FoldingController, foldingModel: FoldingModel, editor: ICommonCodeEditor): void { + setCollapseStateDown(foldingModel, true); } } @@ -742,8 +541,8 @@ class UnfoldAllAction extends FoldingAction { }); } - invoke(foldingController: FoldingController, editor: editorCommon.ICommonCodeEditor): void { - foldingController.unfoldAll(); + invoke(foldingController: FoldingController, foldingModel: FoldingModel, editor: ICommonCodeEditor): void { + setCollapseStateDown(foldingModel, false); } } @@ -755,12 +554,8 @@ class FoldLevelAction extends FoldingAction { return parseInt(this.id.substr(FoldLevelAction.ID_PREFIX.length)); } - private getSelectedLines(editor: editorCommon.ICommonCodeEditor) { - return editor.getSelections().map(s => s.startLineNumber); - } - - invoke(foldingController: FoldingController, editor: editorCommon.ICommonCodeEditor): void { - foldingController.foldLevel(this.getFoldingLevel(), this.getSelectedLines(editor)); + invoke(foldingController: FoldingController, foldingModel: FoldingModel, editor: ICommonCodeEditor): void { + setCollapseStateAtLevel(foldingModel, this.getFoldingLevel(), true, this.getSelectedLines(editor)); } } diff --git a/src/vs/editor/contrib/folding/browser/foldingDecorations.ts b/src/vs/editor/contrib/folding/browser/foldingDecorations.ts new file mode 100644 index 00000000000..df965777690 --- /dev/null +++ b/src/vs/editor/contrib/folding/browser/foldingDecorations.ts @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { TrackedRangeStickiness } from 'vs/editor/common/editorCommon'; +import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations'; +import { FoldingRegion, IDecorationProvider } from 'vs/editor/contrib/folding/common/foldingModel'; + +export class FoldingDecorationProvider implements IDecorationProvider { + + private COLLAPSED_VISUAL_DECORATION = ModelDecorationOptions.register({ + stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + afterContentClassName: 'inline-folded', + linesDecorationsClassName: 'folding collapsed' + }); + + private EXPANDED_AUTO_HIDE_VISUAL_DECORATION = ModelDecorationOptions.register({ + stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + linesDecorationsClassName: 'folding autoHide' + }); + + private EXPANDED_VISUAL_DECORATION = ModelDecorationOptions.register({ + stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + linesDecorationsClassName: 'folding' + }); + + public autoHideFoldingControls: boolean = true; + + getDecorationOption(region: FoldingRegion): ModelDecorationOptions { + if (region.isCollapsed) { + return this.COLLAPSED_VISUAL_DECORATION; + } else if (this.autoHideFoldingControls) { + return this.EXPANDED_AUTO_HIDE_VISUAL_DECORATION; + } else { + return this.EXPANDED_VISUAL_DECORATION; + } + } +} diff --git a/src/vs/editor/contrib/folding/common/foldingModel.ts b/src/vs/editor/contrib/folding/common/foldingModel.ts index 16a1b1c8cdc..61429293e21 100644 --- a/src/vs/editor/contrib/folding/common/foldingModel.ts +++ b/src/vs/editor/contrib/folding/common/foldingModel.ts @@ -3,293 +3,354 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as editorCommon from 'vs/editor/common/editorCommon'; -import { Range } from 'vs/editor/common/core/range'; -import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations'; +import { IModel, IModelDecorationOptions } from 'vs/editor/common/editorCommon'; +import Event, { Emitter } from 'vs/base/common/event'; -export interface IFoldingRange { +export interface IFoldingRange extends ILineRange { + indent: number; +} + +export interface ILineRange { startLineNumber: number; endLineNumber: number; - indent: number; - isCollapsed?: boolean; } export function toString(range: IFoldingRange): string { - return (range ? range.startLineNumber + '/' + range.endLineNumber : 'null') + (range.isCollapsed ? ' (collapsed)' : '') + ' - ' + range.indent; + return (range ? range.startLineNumber + '/' + range.endLineNumber : 'null') + ' - ' + range.indent; } -export class CollapsibleRegion { +export interface IFoldingRangeProvider { + getFoldingRanges(textModel: IModel): Thenable; +} - private decorationIds: string[]; - private _isCollapsed: boolean; - private _indent: number; +export interface IDecorationProvider { + getDecorationOption(region: FoldingRegion): IModelDecorationOptions; +} - private _lastRange: IFoldingRange; +export interface FoldingModelChangeEvent { + model: FoldingModel; + collapseStateChanged?: FoldingRegion[]; +} - public constructor(range: IFoldingRange, model: editorCommon.IModel, changeAccessor: editorCommon.IModelDecorationsChangeAccessor) { - this.decorationIds = []; - this.update(range, model, changeAccessor); +export type CollapseMemento = ILineRange[]; + +export class FoldingModel { + private _textModel: IModel; + private _decorationProvider: IDecorationProvider; + + private _regions: FoldingRegion[] = []; + private _updateEventEmitter = new Emitter(); + + public get regions(): FoldingRegion[] { return this._regions; }; + public get onDidChange(): Event { return this._updateEventEmitter.event; }; + public get textModel() { return this._textModel; } + + constructor(textModel: IModel, decorationProvider: IDecorationProvider) { + this._textModel = textModel; + this._decorationProvider = decorationProvider; } - public get isCollapsed(): boolean { - return this._isCollapsed; - } - - public get isExpanded(): boolean { - return !this._isCollapsed; - } - - public get indent(): number { - return this._indent; - } - - public get foldingRange(): IFoldingRange { - return this._lastRange; - } - - public get startLineNumber(): number { - return this._lastRange ? this._lastRange.startLineNumber : void 0; - } - - public get endLineNumber(): number { - return this._lastRange ? this._lastRange.endLineNumber : void 0; - } - - public setCollapsed(isCollaped: boolean, changeAccessor: editorCommon.IModelDecorationsChangeAccessor): void { - this._isCollapsed = isCollaped; - if (this.decorationIds.length > 0) { - changeAccessor.changeDecorationOptions(this.decorationIds[0], this.getVisualDecorationOptions()); + public toggleCollapseState(regions: FoldingRegion[]) { + if (!regions.length) { + return; } + let processed = {}; + this._textModel.changeDecorations(accessor => { + for (let region of regions) { + if (!processed[region.editorDecorationId]) { + processed[region.editorDecorationId] = true; + region.isCollapsed = !region.isCollapsed; + accessor.changeDecorationOptions(region.editorDecorationId, this._decorationProvider.getDecorationOption(region)); + } + } + }); + this._updateEventEmitter.fire({ model: this, collapseStateChanged: regions }); } - public getDecorationRange(model: editorCommon.IModel): Range { - if (this.decorationIds.length > 0) { - return model.getDecorationRange(this.decorationIds[1]); + public update(newRanges: IFoldingRange[]): void { + let editorDecorationIds = []; + let newEditorDecorations = []; + + // remember the latest start line numbers of the collapsed regions + let collapsedStartLineNumbers: number[] = []; + for (let region of this._regions) { + if (region.isCollapsed) { + let decRange = this._textModel.getDecorationRange(region.editorDecorationId); + if (decRange) { + collapsedStartLineNumbers.push(decRange.startLineNumber); + } + } + if (region.editorDecorationId) { + editorDecorationIds.push(region.editorDecorationId); + } + } + + let recycleBin = this._regions; + let newRegions = []; + + let newRegion = (range: IFoldingRange, isCollapsed: boolean) => { + let region = recycleBin.length ? recycleBin.pop() : new FoldingRegion(); + region.init(range, isCollapsed); + newRegions.push(region); + + let startLineNumber = range.startLineNumber; + let maxColumn = this._textModel.getLineMaxColumn(startLineNumber); + let decorationRange = { + startLineNumber: startLineNumber, + startColumn: maxColumn, + endLineNumber: startLineNumber, + endColumn: maxColumn + }; + newEditorDecorations.push({ range: decorationRange, options: this._decorationProvider.getDecorationOption(region) }); + }; + + let k = 0, i = 0; + while (i < collapsedStartLineNumbers.length && k < newRanges.length) { + let collapsedStartLineNumber = collapsedStartLineNumbers[i]; + while (k < newRanges.length && collapsedStartLineNumber > newRanges[k].startLineNumber) { + newRegion(newRanges[k], false); + k++; + } + if (k < newRanges.length) { + let currRange = newRanges[k]; + if (collapsedStartLineNumber < currRange.startLineNumber) { + i++; + } else if (collapsedStartLineNumber === currRange.startLineNumber) { + newRegion(newRanges[k], true); + i++; + k++; + } + } + } + while (k < newRanges.length) { + newRegion(newRanges[k], false); + k++; + } + + let newEditorDecorationIds = this._textModel.deltaDecorations(editorDecorationIds, newEditorDecorations); + for (let i = 0; i < newEditorDecorations.length; i++) { + newRegions[i].editorDecorationId = newEditorDecorationIds[i]; + } + + this._regions = newRegions; + this._updateEventEmitter.fire({ model: this }); + } + + /** + * Collapse state memento, for persistence only + */ + public getMemento(): CollapseMemento { + let collapsedRanges: ILineRange[] = []; + for (let region of this._regions) { + if (region.isCollapsed && region.editorDecorationId) { + let range = this._textModel.getDecorationRange(region.editorDecorationId); + if (range) { + let startLineNumber = range.startLineNumber; + let endLineNumber = range.endLineNumber + region.range.endLineNumber - region.range.startLineNumber; + collapsedRanges.push({ startLineNumber, endLineNumber }); + } + } + } + if (collapsedRanges.length > 0) { + return collapsedRanges; } return null; } - private static _COLLAPSED_VISUAL_DECORATION = ModelDecorationOptions.register({ - stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, - afterContentClassName: 'inline-folded', - linesDecorationsClassName: 'folding collapsed' - }); - - private static _EXPANDED_VISUAL_DECORATION = ModelDecorationOptions.register({ - stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, - linesDecorationsClassName: 'folding' - }); - - private getVisualDecorationOptions(): ModelDecorationOptions { - if (this._isCollapsed) { - return CollapsibleRegion._COLLAPSED_VISUAL_DECORATION; - } else { - return CollapsibleRegion._EXPANDED_VISUAL_DECORATION; + /** + * Apply persisted state, for persistence only + */ + public applyMemento(state: CollapseMemento) { + if (!Array.isArray(state)) { + return; } - } - - private static _RANGE_DECORATION = ModelDecorationOptions.register({ - stickiness: editorCommon.TrackedRangeStickiness.GrowsOnlyWhenTypingBefore - }); - - private getRangeDecorationOptions(): ModelDecorationOptions { - return CollapsibleRegion._RANGE_DECORATION; - } - - public update(newRange: IFoldingRange, model: editorCommon.IModel, changeAccessor: editorCommon.IModelDecorationsChangeAccessor): void { - this._lastRange = newRange; - this._isCollapsed = !!newRange.isCollapsed; - this._indent = newRange.indent; - - let newDecorations: editorCommon.IModelDeltaDecoration[] = []; - - let maxColumn = model.getLineMaxColumn(newRange.startLineNumber); - let visualRng = { - startLineNumber: newRange.startLineNumber, - startColumn: maxColumn, - endLineNumber: newRange.startLineNumber, - endColumn: maxColumn - }; - newDecorations.push({ range: visualRng, options: this.getVisualDecorationOptions() }); - - let colRng = { - startLineNumber: newRange.startLineNumber, - startColumn: 1, - endLineNumber: newRange.endLineNumber, - endColumn: model.getLineMaxColumn(newRange.endLineNumber) - }; - newDecorations.push({ range: colRng, options: this.getRangeDecorationOptions() }); - - this.decorationIds = changeAccessor.deltaDecorations(this.decorationIds, newDecorations); - } - - - public dispose(changeAccessor: editorCommon.IModelDecorationsChangeAccessor): void { - this._lastRange = null; - this.decorationIds = changeAccessor.deltaDecorations(this.decorationIds, []); - } - - public toString(): string { - let str = this.isCollapsed ? 'collapsed ' : 'expanded '; - if (this._lastRange) { - str += (this._lastRange.startLineNumber + '/' + this._lastRange.endLineNumber); - } else { - str += 'no range'; - } - - return str; - } -} - -export function getCollapsibleRegionsToFoldAtLine(allRegions: CollapsibleRegion[], model: editorCommon.IModel, lineNumber: number, levels: number, up: boolean): CollapsibleRegion[] { - let surroundingRegion: CollapsibleRegion = getCollapsibleRegionAtLine(allRegions, model, lineNumber); - if (!surroundingRegion) { - return []; - } - if (levels === 1) { - return [surroundingRegion]; - } - let result = getCollapsibleRegionsFor(surroundingRegion, allRegions, model, levels, up); - return result.filter(collapsibleRegion => !collapsibleRegion.isCollapsed); -} - -export function getCollapsibleRegionsToUnfoldAtLine(allRegions: CollapsibleRegion[], model: editorCommon.IModel, lineNumber: number, levels: number): CollapsibleRegion[] { - let surroundingRegion: CollapsibleRegion = getCollapsibleRegionAtLine(allRegions, model, lineNumber); - if (!surroundingRegion) { - return []; - } - if (levels === 1) { - let regionToUnfold = surroundingRegion.isCollapsed ? surroundingRegion : getFoldedCollapsibleRegionAfterLine(allRegions, model, surroundingRegion, lineNumber); - return regionToUnfold ? [regionToUnfold] : []; - } - let result = getCollapsibleRegionsFor(surroundingRegion, allRegions, model, levels, false); - return result.filter(collapsibleRegion => collapsibleRegion.isCollapsed); -} - -function getCollapsibleRegionAtLine(allRegions: CollapsibleRegion[], model: editorCommon.IModel, lineNumber: number): CollapsibleRegion { - let collapsibleRegion: CollapsibleRegion = null; - for (let i = 0, len = allRegions.length; i < len; i++) { - let dec = allRegions[i]; - let decRange = dec.getDecorationRange(model); - if (decRange) { - if (doesLineBelongsToCollapsibleRegion(decRange, lineNumber)) { - collapsibleRegion = dec; + let toToogle: FoldingRegion[] = []; + for (let range of state) { + let region = this.getRegionAtLine(range.startLineNumber); + if (region && !region.isCollapsed) { + toToogle.push(region); } - if (doesCollapsibleRegionIsAfterLine(decRange, lineNumber)) { + } + this.toggleCollapseState(toToogle); + } + + public dispose() { + let editorDecorationIds = []; + for (let region of this._regions) { + if (region.editorDecorationId) { + editorDecorationIds.push(region.editorDecorationId); + } + } + this._textModel.deltaDecorations(editorDecorationIds, []); + } + + getAllRegionsAtLine(lineNumber: number, filter?: (r: FoldingRegion) => boolean): FoldingRegion[] { + let result: FoldingRegion[] = []; + for (let i = 0, len = this.regions.length; i < len; i++) { + let current = this.regions[i]; + if (current.range.endLineNumber < lineNumber) { + continue; + } + let startLineNumber = current.range.startLineNumber; + if (startLineNumber <= lineNumber) { + if (!filter || filter(current)) { + result.push(current); + } + } else { break; } } + return result; } - return collapsibleRegion; -} -function getFoldedCollapsibleRegionAfterLine(allRegions: CollapsibleRegion[], model: editorCommon.IModel, surroundingRegion: CollapsibleRegion, lineNumber: number): CollapsibleRegion { - let index = allRegions.indexOf(surroundingRegion); - for (let i = index + 1; i < allRegions.length; i++) { - let dec = allRegions[i]; - let decRange = dec.getDecorationRange(model); - if (decRange) { - if (doesCollapsibleRegionIsAfterLine(decRange, lineNumber)) { - if (!doesCollapsibleRegionContains(surroundingRegion.foldingRange, decRange)) { - return null; - } - if (dec.isCollapsed) { - return dec; - } + + getRegionAtLine(lineNumber: number): FoldingRegion { + let result: FoldingRegion; + for (let i = 0, len = this.regions.length; i < len; i++) { + let current = this.regions[i]; + if (current.range.endLineNumber < lineNumber) { + continue; } - } - } - return null; -} - -export function doesLineBelongsToCollapsibleRegion(range: IFoldingRange | Range, lineNumber: number): boolean { - return lineNumber >= range.startLineNumber && lineNumber <= range.endLineNumber; -} - -function doesCollapsibleRegionIsAfterLine(range: IFoldingRange | Range, lineNumber: number): boolean { - return lineNumber < range.startLineNumber; -} -function doesCollapsibleRegionIsBeforeLine(range: IFoldingRange | Range, lineNumber: number): boolean { - return lineNumber > range.endLineNumber; -} - -function doesCollapsibleRegionContains(range1: IFoldingRange | Range, range2: IFoldingRange | Range): boolean { - if (range1 instanceof Range && range2 instanceof Range) { - return range1.containsRange(range2); - } - return range1.startLineNumber <= range2.startLineNumber && range1.endLineNumber >= range2.endLineNumber; -} - -function getCollapsibleRegionsFor(surroundingRegion: CollapsibleRegion, allRegions: CollapsibleRegion[], model: editorCommon.IModel, levels: number, up: boolean): CollapsibleRegion[] { - let collapsibleRegionsHierarchy: CollapsibleRegionsHierarchy = up ? new CollapsibleRegionsParentHierarchy(surroundingRegion, allRegions, model) : new CollapsibleRegionsChildrenHierarchy(surroundingRegion, allRegions, model); - return collapsibleRegionsHierarchy.getRegionsTill(levels); -} - -interface CollapsibleRegionsHierarchy { - getRegionsTill(level: number): CollapsibleRegion[]; -} - -class CollapsibleRegionsChildrenHierarchy implements CollapsibleRegionsHierarchy { - - children: CollapsibleRegionsChildrenHierarchy[] = []; - lastChildIndex: number; - - constructor(private region: CollapsibleRegion, allRegions: CollapsibleRegion[], model: editorCommon.IModel) { - for (let index = allRegions.indexOf(region) + 1; index < allRegions.length; index++) { - let dec = allRegions[index]; - let decRange = dec.getDecorationRange(model); - if (decRange) { - if (doesCollapsibleRegionContains(region.foldingRange, decRange)) { - index = this.processChildRegion(dec, allRegions, model, index); - } - if (doesCollapsibleRegionIsAfterLine(decRange, region.foldingRange.endLineNumber)) { - break; - } + let startLineNumber = current.range.startLineNumber; + if (startLineNumber <= lineNumber) { + result = current; + } else { + break; } } - } - - private processChildRegion(dec: CollapsibleRegion, allRegions: CollapsibleRegion[], model: editorCommon.IModel, index: number): number { - let childRegion = new CollapsibleRegionsChildrenHierarchy(dec, allRegions, model); - this.children.push(childRegion); - this.lastChildIndex = index; - return childRegion.children.length > 0 ? childRegion.lastChildIndex : index; - } - - public getRegionsTill(level: number): CollapsibleRegion[] { - let result = [this.region]; - if (level > 1) { - this.children.forEach(region => result = result.concat(region.getRegionsTill(level - 1))); - } return result; } -} -class CollapsibleRegionsParentHierarchy implements CollapsibleRegionsHierarchy { - parent: CollapsibleRegionsParentHierarchy; - lastChildIndex: number; + getRegionsInside(range: ILineRange, filter?: (r: FoldingRegion, level?: number) => boolean): FoldingRegion[] { + let result = []; + let trackLevel = filter && filter.length === 2; + let levelStack: ILineRange[] = trackLevel ? [range] : null;; - constructor(private region: CollapsibleRegion, allRegions: CollapsibleRegion[], model: editorCommon.IModel) { - for (let index = allRegions.indexOf(region) - 1; index >= 0; index--) { - let dec = allRegions[index]; - let decRange = dec.getDecorationRange(model); - if (decRange) { - if (doesCollapsibleRegionContains(decRange, region.foldingRange)) { - this.parent = new CollapsibleRegionsParentHierarchy(dec, allRegions, model); - break; - } - if (doesCollapsibleRegionIsBeforeLine(decRange, region.foldingRange.endLineNumber)) { - break; - } + for (let i = 0, len = this.regions.length; i < len; i++) { + let current = this.regions[i]; + let endLineNumber = current.range.endLineNumber; + if (endLineNumber <= range.startLineNumber) { + continue; + } + let startLineNumber = current.range.startLineNumber; + if (startLineNumber < range.endLineNumber) { + if (endLineNumber <= range.endLineNumber && startLineNumber >= range.startLineNumber) { + if (trackLevel) { + while (!current.containedBy(levelStack[levelStack.length - 1])) { + levelStack.pop(); + } + levelStack.push(current.range); + if (filter(current, levelStack.length - 1)) { + result.push(current); + } + } else if (!filter || filter(current)) { + result.push(current); + } + } + } else { + break; } - } - } - - public getRegionsTill(level: number): CollapsibleRegion[] { - let result = [this.region]; - if (this.parent && level > 1) { - result = result.concat(this.parent.getRegionsTill(level - 1)); } return result; } + +} + +export class FoldingRegion { + + public editorDecorationId: string; + public isCollapsed: boolean; + public range: IFoldingRange; + + constructor() { + } + + public init(range: IFoldingRange, isCollapsed: boolean): void { + this.range = range; + this.isCollapsed = isCollapsed; + this.editorDecorationId = void 0; + } + isAfterLine(lineNumber: number): boolean { + return lineNumber < this.range.startLineNumber; + } + isBeforeLine(lineNumber: number): boolean { + return lineNumber > this.range.endLineNumber; + } + contains(range: ILineRange): boolean { + return this.range.startLineNumber <= range.startLineNumber && this.range.endLineNumber >= range.endLineNumber; + } + containedBy(range: ILineRange): boolean { + return range.startLineNumber <= this.range.startLineNumber && range.endLineNumber >= this.range.endLineNumber; + } + containsLine(lineNumber: number) { + return this.range.startLineNumber <= lineNumber && lineNumber <= this.range.endLineNumber; + } + hidesLine(lineNumber: number) { + return this.range.startLineNumber < lineNumber && lineNumber <= this.range.endLineNumber; + } +} + +export function setCollapseStateLevelsDown(foldingModel: FoldingModel, levels: number, doCollapse: boolean, lineNumbers?: number[]) { + let toToggle = []; + for (let lineNumber of lineNumbers) { + let region = foldingModel.getRegionAtLine(lineNumber); + if (region) { + if (levels === 1) { + if (region.isCollapsed !== doCollapse) { + toToggle.push(region); + } + } else { + let regionsInside = foldingModel.getRegionsInside(region.range, (r, level) => r.isCollapsed !== doCollapse && level <= levels); + toToggle.push(...regionsInside); + } + } + } + foldingModel.toggleCollapseState(toToggle); +} + +export function setCollapseStateLevelsUp(foldingModel: FoldingModel, levels: number, doCollapse: boolean, lineNumbers?: number[]) { + let toToggle = []; + for (let lineNumber of lineNumbers) { + let regions = foldingModel.getAllRegionsAtLine(lineNumber); + for (let i = 0; i < levels && regions.length > 0; i++) { + let region = regions.pop(); + if (region.isCollapsed !== doCollapse) { + toToggle.push(region); + } + } + } + foldingModel.toggleCollapseState(toToggle); +} + +/** + * Collapse or expand the regions at the given locations including all children. + * @param doCollapse Wheter to collase or expand + * @param lineNumbers the regions to fold, or if not set all regions in the model. + */ +export function setCollapseStateDown(foldingModel: FoldingModel, doCollapse: boolean, lineNumbers?: number[]): void { + let toToggle = []; + if (!lineNumbers) { + toToggle = foldingModel.regions.filter(region => region.isCollapsed !== doCollapse); + } else { + for (let lineNumber of lineNumbers) { + let region = foldingModel.getRegionAtLine(lineNumber); + if (region) { + let regionsInside = foldingModel.getRegionsInside(region.range, r => r.isCollapsed !== doCollapse); + toToggle.push(...regionsInside); + } + } + } + foldingModel.toggleCollapseState(toToggle); +} + +/** + * Folds or unfolds all regions that have a given level, except if they contain one of the blocked lines. + * @param foldLevel level. Level == 1 is the top level + * @param doCollapse Wheter to collase or expand +* @param blockedLineNumbers +*/ +export function setCollapseStateAtLevel(foldingModel: FoldingModel, foldLevel: number, doCollapse: boolean, blockedLineNumbers: number[]): void { + let filter = (region, level) => level === foldLevel && region.isCollapsed !== doCollapse && !blockedLineNumbers.some(line => region.containsLine(line)); + let toToggle = foldingModel.getRegionsInside({ startLineNumber: 1, endLineNumber: Number.MAX_VALUE }, filter); + foldingModel.toggleCollapseState(toToggle); } \ No newline at end of file diff --git a/src/vs/editor/contrib/folding/common/hiddenRangeModel.ts b/src/vs/editor/contrib/folding/common/hiddenRangeModel.ts new file mode 100644 index 00000000000..aee78bfc692 --- /dev/null +++ b/src/vs/editor/contrib/folding/common/hiddenRangeModel.ts @@ -0,0 +1,143 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import Event, { Emitter } from 'vs/base/common/event'; +import { Range, IRange } from 'vs/editor/common/core/range'; +import { FoldingRegion, FoldingModel, IFoldingRange, CollapseMemento } from 'vs/editor/contrib/folding/common/foldingModel'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { Selection } from 'vs/editor/common/core/selection'; +import { findFirst } from 'vs/base/common/arrays'; + +export class HiddenRangeModel { + private _foldingModel: FoldingModel; + private _hiddenRanges: IRange[] = []; + private _foldingModelListener: IDisposable; + private _updateEventEmitter = new Emitter(); + + public get onDidChange(): Event { return this._updateEventEmitter.event; }; + public get hiddenRanges() { return this._hiddenRanges; } + + public constructor(model: FoldingModel) { + this._foldingModel = model; + this._foldingModelListener = model.onDidChange(_ => this.updateHiddenRanges()); + if (model.regions.length) { + this.updateHiddenRanges(); + } + } + + private updateHiddenRanges(): void { + let updateHiddenAreas = false; + let newHiddenAreas: IRange[] = []; + let i = 0; // index into hidden + + let lastCollapsed: FoldingRegion = null; + + let regions = this._foldingModel.regions; + for (let region of regions) { + if (!region.isCollapsed || lastCollapsed && lastCollapsed.contains(region.range)) { + // ignore ranges contained in collapsed regions + continue; + } + lastCollapsed = region; + let range = region.range; + + if (!updateHiddenAreas && i < this._hiddenRanges.length && matchesHiddenRange(this._hiddenRanges[i], range)) { + newHiddenAreas.push(this._hiddenRanges[i]); + i++; + } else { + updateHiddenAreas = true; + newHiddenAreas.push(new Range(range.startLineNumber + 1, 1, range.endLineNumber, 1)); + } + }; + if (updateHiddenAreas || i < this._hiddenRanges.length) { + this.applyHiddenRanges(newHiddenAreas); + } + } + + public applyMemento(state: CollapseMemento): boolean { + if (!Array.isArray(state) || state.length === 0) { + return false; + } + let hiddenRanges = []; + for (let r of state) { + if (!r.startLineNumber || !r.endLineNumber) { + return false; + } + hiddenRanges.push(new Range(r.startLineNumber + 1, 1, r.endLineNumber, 1)); + } + this.applyHiddenRanges(hiddenRanges); + return true; + } + + private applyHiddenRanges(newHiddenAreas: IRange[]) { + this._hiddenRanges = newHiddenAreas; + this._updateEventEmitter.fire(newHiddenAreas); + } + + public hasRanges() { + return this._hiddenRanges.length > 0; + } + + public isHidden(line: number): boolean { + return findRange(this._hiddenRanges, line) !== null; + } + + public adjustSelections(selections: Selection[]): boolean { + let hasChanges = false; + let editorModel = this._foldingModel.textModel; + let lastRange = null; + + let adjustLine = (line: number) => { + if (!lastRange || !isInside(line, lastRange)) { + lastRange = findRange(this._hiddenRanges, line); + } + if (lastRange) { + return lastRange.startLineNumber - 1; + } + return null; + }; + for (let i = 0, len = selections.length; i < len; i++) { + let selection = selections[i]; + let adjustedStartLine = adjustLine(selection.startLineNumber); + if (adjustedStartLine) { + selection = selection.setStartPosition(adjustedStartLine, editorModel.getLineMaxColumn(adjustedStartLine)); + hasChanges = true; + } + let adjustedEndLine = adjustLine(selection.endLineNumber); + if (adjustedEndLine) { + selection = selection.setEndPosition(adjustedEndLine, editorModel.getLineMaxColumn(adjustedEndLine)); + hasChanges = true; + } + selections[i] = selection; + } + return hasChanges; + } + + + public dispose() { + if (this.hiddenRanges.length > 0) { + this._hiddenRanges = []; + this._updateEventEmitter.fire(this._hiddenRanges); + } + if (this._foldingModelListener) { + this._foldingModelListener.dispose(); + this._foldingModelListener = null; + } + } +} + +function matchesHiddenRange(hr: IRange, range: IFoldingRange) { + return hr.startLineNumber === range.startLineNumber + 1 && hr.endLineNumber === range.endLineNumber; +} +function isInside(line: number, range: IRange) { + return line >= range.startLineNumber && line <= range.endLineNumber; +} +function findRange(ranges: IRange[], line: number): IRange { + let i = findFirst(ranges, r => line < r.startLineNumber) - 1; + if (i >= 0 && ranges[i].endLineNumber >= line) { + return ranges[i]; + } + return null; +} \ No newline at end of file diff --git a/src/vs/editor/contrib/folding/test/foldingModel.test.ts b/src/vs/editor/contrib/folding/test/foldingModel.test.ts new file mode 100644 index 00000000000..f0c89473edf --- /dev/null +++ b/src/vs/editor/contrib/folding/test/foldingModel.test.ts @@ -0,0 +1,464 @@ +/*--------------------------------------------------------------------------------------------- + * 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 assert from 'assert'; +import { FoldingModel, FoldingRegion, setCollapseStateDown, setCollapseStateAtLevel, setCollapseStateLevelsDown, setCollapseStateLevelsUp } from 'vs/editor/contrib/folding/common/foldingModel'; +import { Model } from 'vs/editor/common/model/model'; +import { computeRanges } from 'vs/editor/common/model/indentRanges'; +import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations'; +import { TrackedRangeStickiness } from 'vs/editor/common/editorCommon'; +import { EditOperation } from 'vs/editor/common/core/editOperation'; +import { Position } from 'vs/editor/common/core/position'; + + +interface ExpectedRegion { + startLineNumber: number; + endLineNumber: number; + isCollapsed: boolean; +} + +export class TestDecorationProvider { + + private testDecorator = ModelDecorationOptions.register({ + stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + linesDecorationsClassName: 'folding' + }); + + getDecorationOption(region: FoldingRegion): ModelDecorationOptions { + return this.testDecorator; + } +} + +suite('Folding Model', () => { + function r(startLineNumber: number, endLineNumber: number, isCollapsed: boolean = false): ExpectedRegion { + return { startLineNumber, endLineNumber, isCollapsed }; + } + + function assertRegion(actual: FoldingRegion, expected: ExpectedRegion, message?: string) { + assert.equal(!!actual, !!expected, message); + if (actual) { + assert.equal(actual.range.startLineNumber, expected.startLineNumber, message); + assert.equal(actual.range.endLineNumber, expected.endLineNumber, message); + assert.equal(actual.isCollapsed, expected.isCollapsed, message); + } + } + + function assertFoldedRegions(foldingModel: FoldingModel, expectedRegions: ExpectedRegion[], message?: string) { + assert.deepEqual(foldingModel.regions.filter(r => r.isCollapsed).map(r => ({ startLineNumber: r.range.startLineNumber, endLineNumber: r.range.endLineNumber, isCollapsed: false })), expectedRegions, message); + } + + function assertRegions(actual: FoldingRegion[], expectedRegions: ExpectedRegion[], message?: string) { + assert.deepEqual(actual.map(r => ({ startLineNumber: r.range.startLineNumber, endLineNumber: r.range.endLineNumber, isCollapsed: r.isCollapsed })), expectedRegions, message); + } + + test('getRegionAtLine', () => { + let lines = [ + /* 1*/ '/**', + /* 2*/ ' * Comment', + /* 3*/ ' */', + /* 4*/ 'class A {', + /* 5*/ ' void foo() {', + /* 6*/ ' // comment {', + /* 7*/ ' }', + /* 8*/ '}']; + + let textModel = Model.createFromString(lines.join('\n')); + let foldingModel = new FoldingModel(textModel, new TestDecorationProvider()); + + let ranges = computeRanges(textModel, false, null); + foldingModel.update(ranges); + + let r1 = r(1, 3, false); + let r2 = r(4, 7, false); + let r3 = r(5, 6, false); + + assertRegions(foldingModel.regions, [r1, r2, r3]); + + assertRegion(foldingModel.getRegionAtLine(1), r1, '1'); + assertRegion(foldingModel.getRegionAtLine(2), r1, '2'); + assertRegion(foldingModel.getRegionAtLine(3), r1, '3'); + assertRegion(foldingModel.getRegionAtLine(4), r2, '4'); + assertRegion(foldingModel.getRegionAtLine(5), r3, '5'); + assertRegion(foldingModel.getRegionAtLine(6), r3, '5'); + assertRegion(foldingModel.getRegionAtLine(7), r2, '6'); + assertRegion(foldingModel.getRegionAtLine(8), null, '7'); + }); + + test('collapse', () => { + let lines = [ + /* 1*/ '/**', + /* 2*/ ' * Comment', + /* 3*/ ' */', + /* 4*/ 'class A {', + /* 5*/ ' void foo() {', + /* 6*/ ' // comment {', + /* 7*/ ' }', + /* 8*/ '}']; + + let textModel = Model.createFromString(lines.join('\n')); + let foldingModel = new FoldingModel(textModel, new TestDecorationProvider()); + + let ranges = computeRanges(textModel, false, null); + foldingModel.update(ranges); + + let r1 = r(1, 3, false); + let r2 = r(4, 7, false); + let r3 = r(5, 6, false); + + assertRegions(foldingModel.regions, [r1, r2, r3]); + + foldingModel.toggleCollapseState([foldingModel.getRegionAtLine(1)]); + foldingModel.update(ranges); + + assertRegions(foldingModel.regions, [r(1, 3, true), r2, r3]); + + foldingModel.toggleCollapseState([foldingModel.getRegionAtLine(5)]); + foldingModel.update(ranges); + + assertRegions(foldingModel.regions, [r(1, 3, true), r2, r(5, 6, true)]); + + foldingModel.toggleCollapseState([foldingModel.getRegionAtLine(7)]); + foldingModel.update(ranges); + + assertRegions(foldingModel.regions, [r(1, 3, true), r(4, 7, true), r(5, 6, true)]); + + }); + + test('update', () => { + let lines = [ + /* 1*/ '/**', + /* 2*/ ' * Comment', + /* 3*/ ' */', + /* 4*/ 'class A {', + /* 5*/ ' void foo() {', + /* 6*/ ' // comment {', + /* 7*/ ' }', + /* 8*/ '}']; + + let textModel = Model.createFromString(lines.join('\n')); + let foldingModel = new FoldingModel(textModel, new TestDecorationProvider()); + + let ranges = computeRanges(textModel, false, null); + foldingModel.update(ranges); + + let r1 = r(1, 3, false); + let r2 = r(4, 7, false); + let r3 = r(5, 6, false); + + assertRegions(foldingModel.regions, [r1, r2, r3]); + foldingModel.toggleCollapseState([foldingModel.getRegionAtLine(2), foldingModel.getRegionAtLine(5)]); + + textModel.applyEdits([EditOperation.insert(new Position(4, 1), '//hello\n')]); + + foldingModel.update(computeRanges(textModel, false, null)); + + assertRegions(foldingModel.regions, [r(1, 3, true), r(5, 8, false), r(6, 7, true)]); + }); + + test('getRegionsInside', () => { + let lines = [ + /* 1*/ '/**', + /* 2*/ ' * Comment', + /* 3*/ ' */', + /* 4*/ 'class A {', + /* 5*/ ' void foo() {', + /* 6*/ ' // comment {', + /* 7*/ ' }', + /* 8*/ '}']; + + let textModel = Model.createFromString(lines.join('\n')); + let foldingModel = new FoldingModel(textModel, new TestDecorationProvider()); + + let ranges = computeRanges(textModel, false, null); + foldingModel.update(ranges); + + let r1 = r(1, 3, false); + let r2 = r(4, 7, false); + let r3 = r(5, 6, false); + + assertRegions(foldingModel.regions, [r1, r2, r3]); + + assertRegions(foldingModel.getRegionsInside(r(1, 8)), [r1, r2, r3], '1'); + assertRegions(foldingModel.getRegionsInside(r(1, 3)), [r1], '2'); + assertRegions(foldingModel.getRegionsInside(r(4, 7)), [r2, r3], '3'); + assertRegions(foldingModel.getRegionsInside(r(4, 6)), [r3], '4'); + assertRegions(foldingModel.getRegionsInside(r(2, 5)), [], '5'); + assertRegions(foldingModel.getRegionsInside(r(6, 8)), [], '6'); + + }); + + test('getRegionsInsideWithLevel', () => { + let lines = [ + /* 1*/ '//#region', + /* 2*/ '//#endregion', + /* 3*/ 'class A {', + /* 4*/ ' void foo() {', + /* 5*/ ' if (true) {', + /* 6*/ ' return;', + /* 7*/ ' }', + /* 8*/ ' if (true) {', + /* 9*/ ' return;', + /* 10*/ ' }', + /* 11*/ ' }', + /* 12*/ '}']; + + let textModel = Model.createFromString(lines.join('\n')); + let foldingModel = new FoldingModel(textModel, new TestDecorationProvider()); + + let ranges = computeRanges(textModel, false, { start: /^\/\/#region$/, end: /^\/\/#endregion$/ }); + foldingModel.update(ranges); + + let r1 = r(1, 2, false); + let r2 = r(3, 11, false); + let r3 = r(4, 10, false); + let r4 = r(5, 6, false); + let r5 = r(8, 9, false); + + assertRegions(foldingModel.regions, [r1, r2, r3, r4, r5]); + + assertRegions(foldingModel.getRegionsInside(r(1, 12), (r, level) => level === 1), [r1, r2], '1'); + assertRegions(foldingModel.getRegionsInside(r(1, 12), (r, level) => level === 2), [r3], '2'); + assertRegions(foldingModel.getRegionsInside(r(1, 12), (r, level) => level === 3), [r4, r5], '3'); + + assertRegions(foldingModel.getRegionsInside(r(4, 11), (r, level) => level === 1), [r3], '4'); + assertRegions(foldingModel.getRegionsInside(r(4, 11), (r, level) => level === 2), [r4, r5], '5'); + + assertRegions(foldingModel.getRegionsInside(r(1, 10), (r) => !r.hidesLine(9)), [r1, r4], '6'); + + }); + + test('getRegionAtLine', () => { + let lines = [ + /* 1*/ '//#region', + /* 2*/ 'class A {', + /* 3*/ ' void foo() {', + /* 4*/ ' if (true) {', + /* 5*/ ' //hello', + /* 6*/ ' }', + /* 7*/ '', + /* 8*/ ' }', + /* 9*/ '}', + /* 10*/ '//#endregion', + /* 11*/ '']; + + let textModel = Model.createFromString(lines.join('\n')); + let foldingModel = new FoldingModel(textModel, new TestDecorationProvider()); + + let ranges = computeRanges(textModel, false, { start: /^\/\/#region$/, end: /^\/\/#endregion$/ }); + foldingModel.update(ranges); + + let r1 = r(1, 10, false); + let r2 = r(2, 8, false); + let r3 = r(3, 7, false); + let r4 = r(4, 5, false); + + assertRegions(foldingModel.regions, [r1, r2, r3, r4]); + + assertRegions(foldingModel.getAllRegionsAtLine(1), [r1], '1'); + assertRegions(foldingModel.getAllRegionsAtLine(2), [r1, r2], '2'); + assertRegions(foldingModel.getAllRegionsAtLine(3), [r1, r2, r3], '3'); + assertRegions(foldingModel.getAllRegionsAtLine(4), [r1, r2, r3, r4], '4'); + assertRegions(foldingModel.getAllRegionsAtLine(5), [r1, r2, r3, r4], '5'); + assertRegions(foldingModel.getAllRegionsAtLine(6), [r1, r2, r3], '6'); + assertRegions(foldingModel.getAllRegionsAtLine(7), [r1, r2, r3], '7'); + assertRegions(foldingModel.getAllRegionsAtLine(8), [r1, r2], '8'); + assertRegions(foldingModel.getAllRegionsAtLine(9), [r1], '9'); + assertRegions(foldingModel.getAllRegionsAtLine(10), [r1], '10'); + assertRegions(foldingModel.getAllRegionsAtLine(11), [], '10'); + }); + + test('setCollapseStateRecursivly', () => { + let lines = [ + /* 1*/ '//#region', + /* 2*/ '//#endregion', + /* 3*/ 'class A {', + /* 4*/ ' void foo() {', + /* 5*/ ' if (true) {', + /* 6*/ ' return;', + /* 7*/ ' }', + /* 8*/ '', + /* 9*/ ' if (true) {', + /* 10*/ ' return;', + /* 11*/ ' }', + /* 12*/ ' }', + /* 13*/ '}']; + + let textModel = Model.createFromString(lines.join('\n')); + let foldingModel = new FoldingModel(textModel, new TestDecorationProvider()); + + let ranges = computeRanges(textModel, false, { start: /^\/\/#region$/, end: /^\/\/#endregion$/ }); + foldingModel.update(ranges); + + let r1 = r(1, 2, false); + let r2 = r(3, 12, false); + let r3 = r(4, 11, false); + let r4 = r(5, 6, false); + let r5 = r(9, 10, false); + assertRegions(foldingModel.regions, [r1, r2, r3, r4, r5]); + + setCollapseStateDown(foldingModel, true, [4]); + assertFoldedRegions(foldingModel, [r3, r4, r5], '1'); + + setCollapseStateDown(foldingModel, false, [8]); + assertFoldedRegions(foldingModel, [], '2'); + + setCollapseStateDown(foldingModel, true, [12]); + assertFoldedRegions(foldingModel, [r2, r3, r4, r5], '1'); + + setCollapseStateDown(foldingModel, false, [7]); + assertFoldedRegions(foldingModel, [r2], '1'); + + setCollapseStateDown(foldingModel, false); + assertFoldedRegions(foldingModel, [], '1'); + + setCollapseStateDown(foldingModel, true); + assertFoldedRegions(foldingModel, [r1, r2, r3, r4, r5], '1'); + + }); + + test('setCollapseStateAtLevel', () => { + let lines = [ + /* 1*/ '//#region', + /* 2*/ '//#endregion', + /* 3*/ 'class A {', + /* 4*/ ' void foo() {', + /* 5*/ ' if (true) {', + /* 6*/ ' return;', + /* 7*/ ' }', + /* 8*/ '', + /* 9*/ ' if (true) {', + /* 10*/ ' return;', + /* 11*/ ' }', + /* 12*/ ' }', + /* 13*/ '}']; + + let textModel = Model.createFromString(lines.join('\n')); + let foldingModel = new FoldingModel(textModel, new TestDecorationProvider()); + + let ranges = computeRanges(textModel, false, { start: /^\/\/#region$/, end: /^\/\/#endregion$/ }); + foldingModel.update(ranges); + + let r1 = r(1, 2, false); + let r2 = r(3, 12, false); + let r3 = r(4, 11, false); + let r4 = r(5, 6, false); + let r5 = r(9, 10, false); + assertRegions(foldingModel.regions, [r1, r2, r3, r4, r5]); + + setCollapseStateAtLevel(foldingModel, 1, true, []); + assertFoldedRegions(foldingModel, [r1, r2], '1'); + + setCollapseStateAtLevel(foldingModel, 1, false, [5]); + assertFoldedRegions(foldingModel, [r2], '1'); + + setCollapseStateAtLevel(foldingModel, 1, false, [1]); + assertFoldedRegions(foldingModel, [], '1'); + + setCollapseStateAtLevel(foldingModel, 2, true, []); + assertFoldedRegions(foldingModel, [r3], '1'); + + setCollapseStateAtLevel(foldingModel, 3, true, [4, 9]); + assertFoldedRegions(foldingModel, [r3, r4], '1'); + + setCollapseStateAtLevel(foldingModel, 3, false, [4, 9]); + assertFoldedRegions(foldingModel, [r3], '1'); + + }); + + test('setCollapseStateLevelsDown', () => { + let lines = [ + /* 1*/ '//#region', + /* 2*/ '//#endregion', + /* 3*/ 'class A {', + /* 4*/ ' void foo() {', + /* 5*/ ' if (true) {', + /* 6*/ ' return;', + /* 7*/ ' }', + /* 8*/ '', + /* 9*/ ' if (true) {', + /* 10*/ ' return;', + /* 11*/ ' }', + /* 12*/ ' }', + /* 13*/ '}']; + + let textModel = Model.createFromString(lines.join('\n')); + let foldingModel = new FoldingModel(textModel, new TestDecorationProvider()); + + let ranges = computeRanges(textModel, false, { start: /^\/\/#region$/, end: /^\/\/#endregion$/ }); + foldingModel.update(ranges); + + let r1 = r(1, 2, false); + let r2 = r(3, 12, false); + let r3 = r(4, 11, false); + let r4 = r(5, 6, false); + let r5 = r(9, 10, false); + assertRegions(foldingModel.regions, [r1, r2, r3, r4, r5]); + + setCollapseStateLevelsDown(foldingModel, 1, true, [4]); + assertFoldedRegions(foldingModel, [r3], '1'); + + setCollapseStateLevelsDown(foldingModel, 2, true, [4]); + assertFoldedRegions(foldingModel, [r3, r4, r5], '2'); + + setCollapseStateLevelsDown(foldingModel, 2, false, [3]); + assertFoldedRegions(foldingModel, [r4, r5], '3'); + + setCollapseStateLevelsDown(foldingModel, 2, false, [2]); + assertFoldedRegions(foldingModel, [r4, r5], '4'); + + setCollapseStateLevelsDown(foldingModel, 4, true, [2]); + assertFoldedRegions(foldingModel, [r1, r4, r5], '5'); + + setCollapseStateLevelsDown(foldingModel, 4, false, [2, 3]); + assertFoldedRegions(foldingModel, [], '6'); + + }); + + test('setCollapseStateLevelsUp', () => { + let lines = [ + /* 1*/ '//#region', + /* 2*/ '//#endregion', + /* 3*/ 'class A {', + /* 4*/ ' void foo() {', + /* 5*/ ' if (true) {', + /* 6*/ ' return;', + /* 7*/ ' }', + /* 8*/ '', + /* 9*/ ' if (true) {', + /* 10*/ ' return;', + /* 11*/ ' }', + /* 12*/ ' }', + /* 13*/ '}']; + + let textModel = Model.createFromString(lines.join('\n')); + let foldingModel = new FoldingModel(textModel, new TestDecorationProvider()); + + let ranges = computeRanges(textModel, false, { start: /^\/\/#region$/, end: /^\/\/#endregion$/ }); + foldingModel.update(ranges); + + let r1 = r(1, 2, false); + let r2 = r(3, 12, false); + let r3 = r(4, 11, false); + let r4 = r(5, 6, false); + let r5 = r(9, 10, false); + assertRegions(foldingModel.regions, [r1, r2, r3, r4, r5]); + + setCollapseStateLevelsUp(foldingModel, 1, true, [4]); + assertFoldedRegions(foldingModel, [r3], '1'); + + setCollapseStateLevelsUp(foldingModel, 2, true, [4]); + assertFoldedRegions(foldingModel, [r2, r3], '2'); + + setCollapseStateLevelsUp(foldingModel, 4, false, [1, 3, 4]); + assertFoldedRegions(foldingModel, [], '3'); + + setCollapseStateLevelsUp(foldingModel, 2, true, [10]); + assertFoldedRegions(foldingModel, [r3, r5], '4'); + + + }); + +}); \ No newline at end of file diff --git a/src/vs/editor/contrib/folding/test/hiddenRangeModel.test.ts b/src/vs/editor/contrib/folding/test/hiddenRangeModel.test.ts new file mode 100644 index 00000000000..e8c0c409e75 --- /dev/null +++ b/src/vs/editor/contrib/folding/test/hiddenRangeModel.test.ts @@ -0,0 +1,99 @@ +/*--------------------------------------------------------------------------------------------- + * 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 assert from 'assert'; +import { FoldingModel } from 'vs/editor/contrib/folding/common/foldingModel'; +import { Model } from 'vs/editor/common/model/model'; +import { computeRanges } from 'vs/editor/common/model/indentRanges'; +import { TestDecorationProvider } from './foldingModel.test'; +import { HiddenRangeModel } from 'vs/editor/contrib/folding/common/hiddenRangeModel'; +import { IRange } from 'vs/editor/common/core/range'; + + +interface ExpectedRange { + startLineNumber: number; + endLineNumber: number; +} + +suite('Hidden Range Model', () => { + function r(startLineNumber: number, endLineNumber: number): ExpectedRange { + return { startLineNumber, endLineNumber }; + } + + function assertRanges(actual: IRange[], expectedRegions: ExpectedRange[], message?: string) { + assert.deepEqual(actual.map(r => ({ startLineNumber: r.startLineNumber, endLineNumber: r.endLineNumber })), expectedRegions, message); + } + + test('hasRanges', () => { + let lines = [ + /* 1*/ '/**', + /* 2*/ ' * Comment', + /* 3*/ ' */', + /* 4*/ 'class A {', + /* 5*/ ' void foo() {', + /* 6*/ ' if (true) {', + /* 7*/ ' //hello', + /* 8*/ ' }', + /* 9*/ ' }', + /* 10*/ '}']; + + let textModel = Model.createFromString(lines.join('\n')); + let foldingModel = new FoldingModel(textModel, new TestDecorationProvider()); + let hiddenRangeModel = new HiddenRangeModel(foldingModel); + + assert.equal(hiddenRangeModel.hasRanges(), false); + + let ranges = computeRanges(textModel, false, null); + foldingModel.update(ranges); + + foldingModel.toggleCollapseState([foldingModel.getRegionAtLine(1), foldingModel.getRegionAtLine(6)]); + assertRanges(hiddenRangeModel.hiddenRanges, [r(2, 3), r(7, 7)]); + + assert.equal(hiddenRangeModel.hasRanges(), true); + assert.equal(hiddenRangeModel.isHidden(1), false); + assert.equal(hiddenRangeModel.isHidden(2), true); + assert.equal(hiddenRangeModel.isHidden(3), true); + assert.equal(hiddenRangeModel.isHidden(4), false); + assert.equal(hiddenRangeModel.isHidden(5), false); + assert.equal(hiddenRangeModel.isHidden(6), false); + assert.equal(hiddenRangeModel.isHidden(7), true); + assert.equal(hiddenRangeModel.isHidden(8), false); + assert.equal(hiddenRangeModel.isHidden(9), false); + assert.equal(hiddenRangeModel.isHidden(10), false); + + foldingModel.toggleCollapseState([foldingModel.getRegionAtLine(4)]); + assertRanges(hiddenRangeModel.hiddenRanges, [r(2, 3), r(5, 9)]); + + assert.equal(hiddenRangeModel.hasRanges(), true); + assert.equal(hiddenRangeModel.isHidden(1), false); + assert.equal(hiddenRangeModel.isHidden(2), true); + assert.equal(hiddenRangeModel.isHidden(3), true); + assert.equal(hiddenRangeModel.isHidden(4), false); + assert.equal(hiddenRangeModel.isHidden(5), true); + assert.equal(hiddenRangeModel.isHidden(6), true); + assert.equal(hiddenRangeModel.isHidden(7), true); + assert.equal(hiddenRangeModel.isHidden(8), true); + assert.equal(hiddenRangeModel.isHidden(9), true); + assert.equal(hiddenRangeModel.isHidden(10), false); + + foldingModel.toggleCollapseState([foldingModel.getRegionAtLine(1), foldingModel.getRegionAtLine(6), foldingModel.getRegionAtLine(4)]); + assertRanges(hiddenRangeModel.hiddenRanges, []); + assert.equal(hiddenRangeModel.hasRanges(), false); + assert.equal(hiddenRangeModel.isHidden(1), false); + assert.equal(hiddenRangeModel.isHidden(2), false); + assert.equal(hiddenRangeModel.isHidden(3), false); + assert.equal(hiddenRangeModel.isHidden(4), false); + assert.equal(hiddenRangeModel.isHidden(5), false); + assert.equal(hiddenRangeModel.isHidden(6), false); + assert.equal(hiddenRangeModel.isHidden(7), false); + assert.equal(hiddenRangeModel.isHidden(8), false); + assert.equal(hiddenRangeModel.isHidden(9), false); + assert.equal(hiddenRangeModel.isHidden(10), false); + + }); + + +}); \ No newline at end of file diff --git a/src/vs/editor/contrib/format/browser/formatActions.ts b/src/vs/editor/contrib/format/browser/formatActions.ts index de620bc090b..62cd203a97b 100644 --- a/src/vs/editor/contrib/format/browser/formatActions.ts +++ b/src/vs/editor/contrib/format/browser/formatActions.ts @@ -13,7 +13,7 @@ import * as editorCommon from 'vs/editor/common/editorCommon'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { editorAction, ServicesAccessor, EditorAction, commonEditorContribution } from 'vs/editor/common/editorCommonExtensions'; import { OnTypeFormattingEditProviderRegistry, DocumentRangeFormattingEditProviderRegistry } from 'vs/editor/common/modes'; -import { getOnTypeFormattingEdits, getDocumentFormattingEdits, getDocumentRangeFormattingEdits } from '../common/format'; +import { getOnTypeFormattingEdits, getDocumentFormattingEdits, getDocumentRangeFormattingEdits, NoProviderError } from '../common/format'; import { EditOperationsCommand } from '../common/formatCommand'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { ICodeEditorService } from 'vs/editor/common/services/codeEditorService'; @@ -23,6 +23,7 @@ import { Range } from 'vs/editor/common/core/range'; import { alert } from 'vs/base/browser/ui/aria/aria'; import { EditorState, CodeEditorStateFlag } from 'vs/editor/common/core/editorState'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { IMessageService, Severity } from 'vs/platform/message/common/message'; function alertFormattingEdits(edits: editorCommon.ISingleEditOperation[]): void { @@ -263,6 +264,7 @@ export abstract class AbstractFormatAction extends EditorAction { public run(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor): TPromise { const workerService = accessor.get(IEditorWorkerService); + const messageService = accessor.get(IMessageService); const formattingPromise = this._getFormattingEdits(editor); if (!formattingPromise) { @@ -281,6 +283,15 @@ export abstract class AbstractFormatAction extends EditorAction { EditOperationsCommand.execute(editor, edits); alertFormattingEdits(edits); editor.focus(); + }, err => { + if (err instanceof Error && err.name === NoProviderError.Name) { + messageService.show( + Severity.Info, + nls.localize('no.provider', "Sorry, but there is no formatter for '{0}'-files installed.", editor.getModel().getLanguageIdentifier().language), + ); + } else { + throw err; + } }); } @@ -296,7 +307,7 @@ export class FormatDocumentAction extends AbstractFormatAction { id: 'editor.action.formatDocument', label: nls.localize('formatDocument.label', "Format Document"), alias: 'Format Document', - precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasDocumentFormattingProvider), + precondition: EditorContextKeys.writable, kbOpts: { kbExpr: EditorContextKeys.textFocus, primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_F, @@ -304,6 +315,7 @@ export class FormatDocumentAction extends AbstractFormatAction { linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_I } }, menuOpts: { + when: EditorContextKeys.hasDocumentFormattingProvider, group: '1_modification', order: 1.3 } @@ -325,12 +337,13 @@ export class FormatSelectionAction extends AbstractFormatAction { id: 'editor.action.formatSelection', label: nls.localize('formatSelection.label', "Format Selection"), alias: 'Format Code', - precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasDocumentSelectionFormattingProvider, EditorContextKeys.hasNonEmptySelection), + precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasNonEmptySelection), kbOpts: { kbExpr: EditorContextKeys.textFocus, primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_F) }, menuOpts: { + when: ContextKeyExpr.and(EditorContextKeys.hasDocumentSelectionFormattingProvider, EditorContextKeys.hasNonEmptySelection), group: '1_modification', order: 1.31 } diff --git a/src/vs/editor/contrib/format/common/format.ts b/src/vs/editor/contrib/format/common/format.ts index 0f8bb567719..37c0f0246fd 100644 --- a/src/vs/editor/contrib/format/common/format.ts +++ b/src/vs/editor/contrib/format/common/format.ts @@ -17,12 +17,23 @@ import { IModelService } from 'vs/editor/common/services/modelService'; import { asWinJsPromise, sequence } from 'vs/base/common/async'; import { Position } from 'vs/editor/common/core/position'; -export function getDocumentRangeFormattingEdits(model: IReadOnlyModel, range: Range, options: FormattingOptions): TPromise { +export class NoProviderError extends Error { + + static readonly Name = 'NOPRO'; + + constructor(message?: string) { + super(); + this.name = NoProviderError.Name; + this.message = message; + } +} + +export function getDocumentRangeFormattingEdits(model: IReadOnlyModel, range: Range, options: FormattingOptions): TPromise { const providers = DocumentRangeFormattingEditProviderRegistry.ordered(model); if (providers.length === 0) { - return TPromise.as(undefined); + return TPromise.wrapError(new NoProviderError()); } let result: TextEdit[]; diff --git a/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.ts b/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.ts index 0cede0a44db..5db1dae02f9 100644 --- a/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.ts +++ b/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationCommands.ts @@ -22,7 +22,7 @@ import { ReferencesController } from 'vs/editor/contrib/referenceSearch/browser/ import { ReferencesModel } from 'vs/editor/contrib/referenceSearch/browser/referencesModel'; import { PeekContext } from 'vs/editor/contrib/referenceSearch/browser/peekViewWidget'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { MessageController } from './messageController'; +import { MessageController } from 'vs/editor/contrib/message/messageController'; import * as corePosition from 'vs/editor/common/core/position'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { IProgressService } from 'vs/platform/progress/common/progress'; diff --git a/src/vs/editor/contrib/indentation/common/indentation.ts b/src/vs/editor/contrib/indentation/common/indentation.ts index cd9add8748f..41fb47db826 100644 --- a/src/vs/editor/contrib/indentation/common/indentation.ts +++ b/src/vs/editor/contrib/indentation/common/indentation.ts @@ -323,7 +323,9 @@ export class ReindentLinesAction extends EditorAction { } let edits = getReindentEditOperations(model, 1, model.getLineCount()); if (edits) { + editor.pushUndoStop(); editor.executeEdits(this.id, edits); + editor.pushUndoStop(); } } } diff --git a/src/vs/editor/contrib/linesOperations/common/linesOperations.ts b/src/vs/editor/contrib/linesOperations/common/linesOperations.ts index 3c5229de450..1b0ef8b7e2f 100644 --- a/src/vs/editor/contrib/linesOperations/common/linesOperations.ts +++ b/src/vs/editor/contrib/linesOperations/common/linesOperations.ts @@ -407,7 +407,9 @@ export abstract class AbstractDeleteAllToBoundaryAction extends EditorAction { return EditOperation.replace(range, ''); }); + editor.pushUndoStop(); editor.executeEdits(this.id, edits, endCursorState); + editor.pushUndoStop(); } /** @@ -673,8 +675,9 @@ export class JoinLinesAction extends EditorAction { } endCursorState.unshift(endPrimaryCursor); + editor.pushUndoStop(); editor.executeEdits(this.id, edits, endCursorState); - + editor.pushUndoStop(); } } diff --git a/src/vs/editor/contrib/linesOperations/test/common/linesOperations.test.ts b/src/vs/editor/contrib/linesOperations/test/common/linesOperations.test.ts index 64c67e2cb9a..3e7578ef9e8 100644 --- a/src/vs/editor/contrib/linesOperations/test/common/linesOperations.test.ts +++ b/src/vs/editor/contrib/linesOperations/test/common/linesOperations.test.ts @@ -73,6 +73,32 @@ suite('Editor Contrib - Line Operations', () => { assert.equal(model.getLineContent(5), 'horlworld', '005'); }); }); + + test('issue #36234: should push undo stop', () => { + withMockCodeEditor( + [ + 'one', + 'two', + 'three' + ], {}, (editor, cursor) => { + let model = editor.getModel(); + let deleteAllLeftAction = new DeleteAllLeftAction(); + + editor.setSelection(new Selection(1, 1, 1, 1)); + + editor.trigger('keyboard', Handler.Type, { text: 'Typing some text here on line ' }); + assert.equal(model.getLineContent(1), 'Typing some text here on line one'); + assert.deepEqual(editor.getSelection(), new Selection(1, 31, 1, 31)); + + deleteAllLeftAction.run(null, editor); + assert.equal(model.getLineContent(1), 'one'); + assert.deepEqual(editor.getSelection(), new Selection(1, 1, 1, 1)); + + editor.trigger('keyboard', Handler.Undo, {}); + assert.equal(model.getLineContent(1), 'Typing some text here on line one'); + assert.deepEqual(editor.getSelection(), new Selection(1, 31, 1, 31)); + }); + }); }); suite('JoinLinesAction', () => { @@ -164,6 +190,31 @@ suite('Editor Contrib - Line Operations', () => { assert.deepEqual(editor.getSelection().toString(), new Selection(3, 4, 3, 8).toString(), '003'); }); }); + + test('should push undo stop', function () { + withMockCodeEditor( + [ + 'hello', + 'world' + ], {}, (editor, cursor) => { + let model = editor.getModel(); + let joinLinesAction = new JoinLinesAction(); + + editor.setSelection(new Selection(1, 6, 1, 6)); + + editor.trigger('keyboard', Handler.Type, { text: ' my dear' }); + assert.equal(model.getLineContent(1), 'hello my dear'); + assert.deepEqual(editor.getSelection(), new Selection(1, 14, 1, 14)); + + joinLinesAction.run(null, editor); + assert.equal(model.getLineContent(1), 'hello my dear world'); + assert.deepEqual(editor.getSelection(), new Selection(1, 14, 1, 14)); + + editor.trigger('keyboard', Handler.Undo, {}); + assert.equal(model.getLineContent(1), 'hello my dear'); + assert.deepEqual(editor.getSelection(), new Selection(1, 14, 1, 14)); + }); + }); }); test('transpose', function () { diff --git a/src/vs/editor/contrib/linesOperations/test/common/sortLinesCommand.test.ts b/src/vs/editor/contrib/linesOperations/test/common/sortLinesCommand.test.ts index ec19e7bbe0d..58aa224ff6c 100644 --- a/src/vs/editor/contrib/linesOperations/test/common/sortLinesCommand.test.ts +++ b/src/vs/editor/contrib/linesOperations/test/common/sortLinesCommand.test.ts @@ -77,7 +77,7 @@ suite('Editor Contrib - Sort Lines Command', () => { 'third line', 'fifth' ], - new Selection(3, 3, 4, 2) + new Selection(3, 3, 4, 1) ); }); @@ -119,7 +119,7 @@ suite('Editor Contrib - Sort Lines Command', () => { 'second line', 'third line', ], - new Selection(1, 1, 5, 6) + new Selection(1, 1, 5, 11) ); }); diff --git a/src/vs/editor/contrib/goToDeclaration/browser/messageController.css b/src/vs/editor/contrib/message/messageController.css similarity index 100% rename from src/vs/editor/contrib/goToDeclaration/browser/messageController.css rename to src/vs/editor/contrib/message/messageController.css diff --git a/src/vs/editor/contrib/goToDeclaration/browser/messageController.ts b/src/vs/editor/contrib/message/messageController.ts similarity index 98% rename from src/vs/editor/contrib/goToDeclaration/browser/messageController.ts rename to src/vs/editor/contrib/message/messageController.ts index 693722e4c52..ce5d45af58c 100644 --- a/src/vs/editor/contrib/goToDeclaration/browser/messageController.ts +++ b/src/vs/editor/contrib/message/messageController.ts @@ -47,10 +47,14 @@ export class MessageController { this._visible = MessageController.CONTEXT_SNIPPET_MODE.bindTo(contextKeyService); } - dispose() { + dispose(): void { this._visible.reset(); } + isVisible() { + return this._visible.get(); + } + showMessage(message: string, position: IPosition): void { alert(message); diff --git a/src/vs/editor/contrib/multicursor/common/multicursor.ts b/src/vs/editor/contrib/multicursor/common/multicursor.ts index 0f6b2fdec41..a45a8226d0d 100644 --- a/src/vs/editor/contrib/multicursor/common/multicursor.ts +++ b/src/vs/editor/contrib/multicursor/common/multicursor.ts @@ -5,14 +5,24 @@ 'use strict'; import * as nls from 'vs/nls'; -import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { ICommonCodeEditor, ScrollType } from 'vs/editor/common/editorCommon'; +import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { KeyCode, KeyMod, KeyChord } from 'vs/base/common/keyCodes'; +import { RunOnceScheduler } from 'vs/base/common/async'; +import { ICommonCodeEditor, ScrollType, IEditorContribution, FindMatch, TrackedRangeStickiness, OverviewRulerLane, IModel } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { editorAction, ServicesAccessor, EditorAction } from 'vs/editor/common/editorCommonExtensions'; +import { editorAction, commonEditorContribution, ServicesAccessor, EditorAction } from 'vs/editor/common/editorCommonExtensions'; +import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; -import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents'; +import { CursorChangeReason, ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; import { CursorMoveCommands } from 'vs/editor/common/controller/cursorMoveCommands'; import { CursorState, RevealTarget } from 'vs/editor/common/controller/cursorCommon'; +import { Constants } from 'vs/editor/common/core/uint'; +import { DocumentHighlightProviderRegistry } from 'vs/editor/common/modes'; +import { CommonFindController } from 'vs/editor/contrib/find/common/findController'; +import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations'; +import { overviewRulerSelectionHighlightForeground } from 'vs/platform/theme/common/colorRegistry'; +import { themeColorFromId } from 'vs/platform/theme/common/themeService'; +import { INewFindReplaceState, FindOptionOverride } from 'vs/editor/contrib/find/common/findState'; @editorAction export class InsertCursorAbove extends EditorAction { @@ -139,3 +149,764 @@ class InsertCursorAtEndOfEachLineSelected extends EditorAction { } } } + +export class MultiCursorSessionResult { + constructor( + public readonly selections: Selection[], + public readonly revealRange: Range, + public readonly revealScrollType: ScrollType + ) { } +} + +export class MultiCursorSession { + + public static create(editor: ICommonCodeEditor, findController: CommonFindController): MultiCursorSession { + const findState = findController.getState(); + + // Find widget owns entirely what we search for if: + // - focus is not in the editor (i.e. it is in the find widget) + // - and the search widget is visible + // - and the search string is non-empty + if (!editor.isFocused() && findState.isRevealed && findState.searchString.length > 0) { + // Find widget owns what is searched for + return new MultiCursorSession(editor, findController, false, findState.searchString, findState.wholeWord, findState.matchCase, null); + } + + // Otherwise, the selection gives the search text, and the find widget gives the search settings + // The exception is the find state disassociation case: when beginning with a single, collapsed selection + let isDisconnectedFromFindController = false; + let wholeWord: boolean; + let matchCase: boolean; + const selections = editor.getSelections(); + if (selections.length === 1 && selections[0].isEmpty()) { + isDisconnectedFromFindController = true; + wholeWord = true; + matchCase = true; + } else { + wholeWord = findState.wholeWord; + matchCase = findState.matchCase; + } + + // Selection owns what is searched for + const s = editor.getSelection(); + + let searchText: string; + let currentMatch: Selection = null; + + if (s.isEmpty()) { + // selection is empty => expand to current word + const word = editor.getModel().getWordAtPosition(s.getStartPosition()); + if (!word) { + return null; + } + searchText = word.word; + currentMatch = new Selection(s.startLineNumber, word.startColumn, s.startLineNumber, word.endColumn); + } else { + searchText = editor.getModel().getValueInRange(s).replace(/\r\n/g, '\n'); + } + + return new MultiCursorSession(editor, findController, isDisconnectedFromFindController, searchText, wholeWord, matchCase, currentMatch); + } + + constructor( + private readonly _editor: ICommonCodeEditor, + public readonly findController: CommonFindController, + public readonly isDisconnectedFromFindController: boolean, + public readonly searchText: string, + public readonly wholeWord: boolean, + public readonly matchCase: boolean, + public currentMatch: Selection + ) { } + + public addSelectionToNextFindMatch(): MultiCursorSessionResult { + const nextMatch = this._getNextMatch(); + if (!nextMatch) { + return null; + } + + const allSelections = this._editor.getSelections(); + return new MultiCursorSessionResult(allSelections.concat(nextMatch), nextMatch, ScrollType.Smooth); + } + + public moveSelectionToNextFindMatch(): MultiCursorSessionResult { + const nextMatch = this._getNextMatch(); + if (!nextMatch) { + return null; + } + + const allSelections = this._editor.getSelections(); + return new MultiCursorSessionResult(allSelections.slice(0, allSelections.length - 1).concat(nextMatch), nextMatch, ScrollType.Smooth); + } + + private _getNextMatch(): Selection { + if (this.currentMatch) { + const result = this.currentMatch; + this.currentMatch = null; + return result; + } + + this.findController.highlightFindOptions(); + + const allSelections = this._editor.getSelections(); + const lastAddedSelection = allSelections[allSelections.length - 1]; + const nextMatch = this._editor.getModel().findNextMatch(this.searchText, lastAddedSelection.getEndPosition(), false, this.matchCase, this.wholeWord ? this._editor.getConfiguration().wordSeparators : null, false); + + if (!nextMatch) { + return null; + } + return new Selection(nextMatch.range.startLineNumber, nextMatch.range.startColumn, nextMatch.range.endLineNumber, nextMatch.range.endColumn); + } + + public addSelectionToPreviousFindMatch(): MultiCursorSessionResult { + const previousMatch = this._getPreviousMatch(); + if (!previousMatch) { + return null; + } + + const allSelections = this._editor.getSelections(); + return new MultiCursorSessionResult(allSelections.concat(previousMatch), previousMatch, ScrollType.Smooth); + } + + public moveSelectionToPreviousFindMatch(): MultiCursorSessionResult { + const previousMatch = this._getPreviousMatch(); + if (!previousMatch) { + return null; + } + + const allSelections = this._editor.getSelections(); + return new MultiCursorSessionResult(allSelections.slice(0, allSelections.length - 1).concat(previousMatch), previousMatch, ScrollType.Smooth); + } + + private _getPreviousMatch(): Selection { + if (this.currentMatch) { + const result = this.currentMatch; + this.currentMatch = null; + return result; + } + + this.findController.highlightFindOptions(); + + const allSelections = this._editor.getSelections(); + const lastAddedSelection = allSelections[allSelections.length - 1]; + const previousMatch = this._editor.getModel().findPreviousMatch(this.searchText, lastAddedSelection.getStartPosition(), false, this.matchCase, this.wholeWord ? this._editor.getConfiguration().wordSeparators : null, false); + + if (!previousMatch) { + return null; + } + return new Selection(previousMatch.range.startLineNumber, previousMatch.range.startColumn, previousMatch.range.endLineNumber, previousMatch.range.endColumn); + } + + public selectAll(): FindMatch[] { + this.findController.highlightFindOptions(); + + return this._editor.getModel().findMatches(this.searchText, true, false, this.matchCase, this.wholeWord ? this._editor.getConfiguration().wordSeparators : null, false, Constants.MAX_SAFE_SMALL_INTEGER); + } +} + +@commonEditorContribution +export class MultiCursorSelectionController extends Disposable implements IEditorContribution { + + private static ID = 'editor.contrib.multiCursorController'; + + private readonly _editor: ICommonCodeEditor; + private _ignoreSelectionChange: boolean; + private _session: MultiCursorSession; + private _sessionDispose: IDisposable[]; + + public static get(editor: ICommonCodeEditor): MultiCursorSelectionController { + return editor.getContribution(MultiCursorSelectionController.ID); + } + + constructor(editor: ICommonCodeEditor) { + super(); + this._editor = editor; + this._ignoreSelectionChange = false; + this._session = null; + this._sessionDispose = []; + } + + public dispose(): void { + this._endSession(); + super.dispose(); + } + + public getId(): string { + return MultiCursorSelectionController.ID; + } + + private _beginSessionIfNeeded(findController: CommonFindController): void { + if (!this._session) { + // Create a new session + const session = MultiCursorSession.create(this._editor, findController); + if (!session) { + return; + } + + this._session = session; + + const newState: INewFindReplaceState = { searchString: this._session.searchText }; + if (this._session.isDisconnectedFromFindController) { + newState.wholeWordOverride = FindOptionOverride.True; + newState.matchCaseOverride = FindOptionOverride.True; + newState.isRegexOverride = FindOptionOverride.False; + } + findController.getState().change(newState, false); + + this._sessionDispose = [ + this._editor.onDidChangeCursorSelection((e) => { + if (this._ignoreSelectionChange) { + return; + } + this._endSession(); + }), + this._editor.onDidBlurEditorText(() => { + this._endSession(); + }), + findController.getState().addChangeListener((e) => { + if (e.matchCase || e.wholeWord) { + this._endSession(); + } + }) + ]; + } + } + + private _endSession(): void { + this._sessionDispose = dispose(this._sessionDispose); + if (this._session && this._session.isDisconnectedFromFindController) { + const newState: INewFindReplaceState = { + wholeWordOverride: FindOptionOverride.NotSet, + matchCaseOverride: FindOptionOverride.NotSet, + isRegexOverride: FindOptionOverride.NotSet, + }; + this._session.findController.getState().change(newState, false); + } + this._session = null; + } + + private _setSelections(selections: Selection[]): void { + this._ignoreSelectionChange = true; + this._editor.setSelections(selections); + this._ignoreSelectionChange = false; + } + + private _expandEmptyToWord(model: IModel, selection: Selection): Selection { + if (!selection.isEmpty()) { + return selection; + } + const word = model.getWordAtPosition(selection.getStartPosition()); + if (!word) { + return selection; + } + return new Selection(selection.startLineNumber, word.startColumn, selection.startLineNumber, word.endColumn); + } + + private _applySessionResult(result: MultiCursorSessionResult): void { + if (!result) { + return; + } + this._setSelections(result.selections); + if (result.revealRange) { + this._editor.revealRangeInCenterIfOutsideViewport(result.revealRange, result.revealScrollType); + } + } + + public getSession(findController: CommonFindController): MultiCursorSession { + return this._session; + } + + public addSelectionToNextFindMatch(findController: CommonFindController): void { + if (!this._session) { + // If there are multiple cursors, handle the case where they do not all select the same text. + const allSelections = this._editor.getSelections(); + if (allSelections.length > 1) { + const findState = findController.getState(); + const matchCase = findState.matchCase; + const selectionsContainSameText = modelRangesContainSameText(this._editor.getModel(), allSelections, matchCase); + if (!selectionsContainSameText) { + const model = this._editor.getModel(); + let resultingSelections: Selection[] = []; + for (let i = 0, len = allSelections.length; i < len; i++) { + resultingSelections[i] = this._expandEmptyToWord(model, allSelections[i]); + } + this._editor.setSelections(resultingSelections); + return; + } + } + } + this._beginSessionIfNeeded(findController); + if (this._session) { + this._applySessionResult(this._session.addSelectionToNextFindMatch()); + } + } + + public addSelectionToPreviousFindMatch(findController: CommonFindController): void { + this._beginSessionIfNeeded(findController); + if (this._session) { + this._applySessionResult(this._session.addSelectionToPreviousFindMatch()); + } + } + + public moveSelectionToNextFindMatch(findController: CommonFindController): void { + this._beginSessionIfNeeded(findController); + if (this._session) { + this._applySessionResult(this._session.moveSelectionToNextFindMatch()); + } + } + + public moveSelectionToPreviousFindMatch(findController: CommonFindController): void { + this._beginSessionIfNeeded(findController); + if (this._session) { + this._applySessionResult(this._session.addSelectionToPreviousFindMatch()); + } + } + + public selectAll(findController: CommonFindController): void { + let matches: FindMatch[] = null; + + const findState = findController.getState(); + + // Special case: find widget owns entirely what we search for if: + // - focus is not in the editor (i.e. it is in the find widget) + // - and the search widget is visible + // - and the search string is non-empty + // - and we're searching for a regex + if (!this._editor.isFocused() && findState.isRevealed && findState.searchString.length > 0 && findState.isRegex) { + + matches = this._editor.getModel().findMatches(findState.searchString, true, findState.isRegex, findState.matchCase, findState.wholeWord ? this._editor.getConfiguration().wordSeparators : null, false, Constants.MAX_SAFE_SMALL_INTEGER); + + } else { + + this._beginSessionIfNeeded(findController); + if (!this._session) { + return; + } + + matches = this._session.selectAll(); + } + + if (matches.length > 0) { + const editorSelection = this._editor.getSelection(); + // Have the primary cursor remain the one where the action was invoked + for (let i = 0, len = matches.length; i < len; i++) { + const match = matches[i]; + const intersection = match.range.intersectRanges(editorSelection); + if (intersection) { + // bingo! + matches[i] = matches[0]; + matches[0] = match; + break; + } + } + + this._setSelections(matches.map(m => new Selection(m.range.startLineNumber, m.range.startColumn, m.range.endLineNumber, m.range.endColumn))); + } + } +} + +export abstract class MultiCursorSelectionControllerAction extends EditorAction { + + public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void { + const multiCursorController = MultiCursorSelectionController.get(editor); + if (!multiCursorController) { + return; + } + const findController = CommonFindController.get(editor); + if (!findController) { + return null; + } + this._run(multiCursorController, findController); + } + + protected abstract _run(multiCursorController: MultiCursorSelectionController, findController: CommonFindController): void; +} + +@editorAction +export class AddSelectionToNextFindMatchAction extends MultiCursorSelectionControllerAction { + constructor() { + super({ + id: 'editor.action.addSelectionToNextFindMatch', + label: nls.localize('addSelectionToNextFindMatch', "Add Selection To Next Find Match"), + alias: 'Add Selection To Next Find Match', + precondition: null, + kbOpts: { + kbExpr: EditorContextKeys.focus, + primary: KeyMod.CtrlCmd | KeyCode.KEY_D + } + }); + } + protected _run(multiCursorController: MultiCursorSelectionController, findController: CommonFindController): void { + multiCursorController.addSelectionToNextFindMatch(findController); + } +} + +@editorAction +export class AddSelectionToPreviousFindMatchAction extends MultiCursorSelectionControllerAction { + constructor() { + super({ + id: 'editor.action.addSelectionToPreviousFindMatch', + label: nls.localize('addSelectionToPreviousFindMatch', "Add Selection To Previous Find Match"), + alias: 'Add Selection To Previous Find Match', + precondition: null + }); + } + protected _run(multiCursorController: MultiCursorSelectionController, findController: CommonFindController): void { + multiCursorController.addSelectionToPreviousFindMatch(findController); + } +} + +@editorAction +export class MoveSelectionToNextFindMatchAction extends MultiCursorSelectionControllerAction { + constructor() { + super({ + id: 'editor.action.moveSelectionToNextFindMatch', + label: nls.localize('moveSelectionToNextFindMatch', "Move Last Selection To Next Find Match"), + alias: 'Move Last Selection To Next Find Match', + precondition: null, + kbOpts: { + kbExpr: EditorContextKeys.focus, + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_D) + } + }); + } + protected _run(multiCursorController: MultiCursorSelectionController, findController: CommonFindController): void { + multiCursorController.moveSelectionToNextFindMatch(findController); + } +} + +@editorAction +export class MoveSelectionToPreviousFindMatchAction extends MultiCursorSelectionControllerAction { + constructor() { + super({ + id: 'editor.action.moveSelectionToPreviousFindMatch', + label: nls.localize('moveSelectionToPreviousFindMatch', "Move Last Selection To Previous Find Match"), + alias: 'Move Last Selection To Previous Find Match', + precondition: null + }); + } + protected _run(multiCursorController: MultiCursorSelectionController, findController: CommonFindController): void { + multiCursorController.moveSelectionToPreviousFindMatch(findController); + } +} + +@editorAction +export class SelectHighlightsAction extends MultiCursorSelectionControllerAction { + constructor() { + super({ + id: 'editor.action.selectHighlights', + label: nls.localize('selectAllOccurrencesOfFindMatch', "Select All Occurrences of Find Match"), + alias: 'Select All Occurrences of Find Match', + precondition: null, + kbOpts: { + kbExpr: EditorContextKeys.focus, + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_L + } + }); + } + protected _run(multiCursorController: MultiCursorSelectionController, findController: CommonFindController): void { + multiCursorController.selectAll(findController); + } +} + +@editorAction +export class CompatChangeAll extends MultiCursorSelectionControllerAction { + constructor() { + super({ + id: 'editor.action.changeAll', + label: nls.localize('changeAll.label', "Change All Occurrences"), + alias: 'Change All Occurrences', + precondition: EditorContextKeys.writable, + kbOpts: { + kbExpr: EditorContextKeys.textFocus, + primary: KeyMod.CtrlCmd | KeyCode.F2 + }, + menuOpts: { + group: '1_modification', + order: 1.2 + } + }); + } + protected _run(multiCursorController: MultiCursorSelectionController, findController: CommonFindController): void { + multiCursorController.selectAll(findController); + } +} + +class SelectionHighlighterState { + public readonly lastWordUnderCursor: Selection; + public readonly searchText: string; + public readonly matchCase: boolean; + public readonly wordSeparators: string; + + constructor(lastWordUnderCursor: Selection, searchText: string, matchCase: boolean, wordSeparators: string) { + this.lastWordUnderCursor = lastWordUnderCursor; + this.searchText = searchText; + this.matchCase = matchCase; + this.wordSeparators = wordSeparators; + } + + /** + * Everything equals except for `lastWordUnderCursor` + */ + public static softEquals(a: SelectionHighlighterState, b: SelectionHighlighterState): boolean { + if (!a && !b) { + return true; + } + if (!a || !b) { + return false; + } + return ( + a.searchText === b.searchText + && a.matchCase === b.matchCase + && a.wordSeparators === b.wordSeparators + ); + } +} + +@commonEditorContribution +export class SelectionHighlighter extends Disposable implements IEditorContribution { + private static ID = 'editor.contrib.selectionHighlighter'; + + private editor: ICommonCodeEditor; + private _isEnabled: boolean; + private decorations: string[]; + private updateSoon: RunOnceScheduler; + private state: SelectionHighlighterState; + + constructor(editor: ICommonCodeEditor) { + super(); + this.editor = editor; + this._isEnabled = editor.getConfiguration().contribInfo.selectionHighlight; + this.decorations = []; + this.updateSoon = this._register(new RunOnceScheduler(() => this._update(), 300)); + this.state = null; + + this._register(editor.onDidChangeConfiguration((e) => { + this._isEnabled = editor.getConfiguration().contribInfo.selectionHighlight; + })); + this._register(editor.onDidChangeCursorSelection((e: ICursorSelectionChangedEvent) => { + + if (!this._isEnabled) { + // Early exit if nothing needs to be done! + // Leave some form of early exit check here if you wish to continue being a cursor position change listener ;) + return; + } + + if (e.selection.isEmpty()) { + if (e.reason === CursorChangeReason.Explicit) { + if (this.state && (!this.state.lastWordUnderCursor || !this.state.lastWordUnderCursor.containsPosition(e.selection.getStartPosition()))) { + // no longer valid + this._setState(null); + } + this.updateSoon.schedule(); + } else { + this._setState(null); + + } + } else { + this._update(); + } + })); + this._register(editor.onDidChangeModel((e) => { + this._setState(null); + })); + this._register(CommonFindController.get(editor).getState().addChangeListener((e) => { + this._update(); + })); + } + + public getId(): string { + return SelectionHighlighter.ID; + } + + private _update(): void { + this._setState(SelectionHighlighter._createState(this._isEnabled, this.editor)); + } + + private static _createState(isEnabled: boolean, editor: ICommonCodeEditor): SelectionHighlighterState { + if (!isEnabled) { + return null; + } + const model = editor.getModel(); + if (!model) { + return null; + } + const s = editor.getSelection(); + if (s.startLineNumber !== s.endLineNumber) { + // multiline forbidden for perf reasons + return null; + } + const multiCursorController = MultiCursorSelectionController.get(editor); + if (!multiCursorController) { + return null; + } + const findController = CommonFindController.get(editor); + if (!findController) { + return null; + } + let r = multiCursorController.getSession(findController); + if (!r) { + const allSelections = editor.getSelections(); + if (allSelections.length > 1) { + const findState = findController.getState(); + const matchCase = findState.matchCase; + const selectionsContainSameText = modelRangesContainSameText(editor.getModel(), allSelections, matchCase); + if (!selectionsContainSameText) { + return null; + } + } + + r = MultiCursorSession.create(editor, findController); + } + if (!r) { + return null; + } + + let lastWordUnderCursor: Selection = null; + const hasFindOccurrences = DocumentHighlightProviderRegistry.has(model); + if (r.currentMatch) { + // This is an empty selection + if (hasFindOccurrences) { + // Do not interfere with semantic word highlighting in the no selection case + return null; + } + + const config = editor.getConfiguration(); + if (!config.contribInfo.occurrencesHighlight) { + return null; + } + + lastWordUnderCursor = r.currentMatch; + } + if (/^[ \t]+$/.test(r.searchText)) { + // whitespace only selection + return null; + } + if (r.searchText.length > 200) { + // very long selection + return null; + } + + // TODO: better handling of this case + const findState = findController.getState(); + const caseSensitive = findState.matchCase; + + // Return early if the find widget shows the exact same matches + if (findState.isRevealed) { + let findStateSearchString = findState.searchString; + if (!caseSensitive) { + findStateSearchString = findStateSearchString.toLowerCase(); + } + + let mySearchString = r.searchText; + if (!caseSensitive) { + mySearchString = mySearchString.toLowerCase(); + } + + if (findStateSearchString === mySearchString && r.matchCase === findState.matchCase && r.wholeWord === findState.wholeWord && !findState.isRegex) { + return null; + } + } + + return new SelectionHighlighterState(lastWordUnderCursor, r.searchText, r.matchCase, r.wholeWord ? editor.getConfiguration().wordSeparators : null); + } + + private _setState(state: SelectionHighlighterState): void { + if (SelectionHighlighterState.softEquals(this.state, state)) { + this.state = state; + return; + } + this.state = state; + + if (!this.state) { + if (this.decorations.length > 0) { + this.decorations = this.editor.deltaDecorations(this.decorations, []); + } + return; + } + + const model = this.editor.getModel(); + const hasFindOccurrences = DocumentHighlightProviderRegistry.has(model); + + let allMatches = model.findMatches(this.state.searchText, true, false, this.state.matchCase, this.state.wordSeparators, false).map(m => m.range); + allMatches.sort(Range.compareRangesUsingStarts); + + let selections = this.editor.getSelections(); + selections.sort(Range.compareRangesUsingStarts); + + // do not overlap with selection (issue #64 and #512) + let matches: Range[] = []; + for (let i = 0, j = 0, len = allMatches.length, lenJ = selections.length; i < len;) { + const match = allMatches[i]; + + if (j >= lenJ) { + // finished all editor selections + matches.push(match); + i++; + } else { + const cmp = Range.compareRangesUsingStarts(match, selections[j]); + if (cmp < 0) { + // match is before sel + matches.push(match); + i++; + } else if (cmp > 0) { + // sel is before match + j++; + } else { + // sel is equal to match + i++; + j++; + } + } + } + + const decorations = matches.map(r => { + return { + range: r, + // Show in overviewRuler only if model has no semantic highlighting + options: (hasFindOccurrences ? SelectionHighlighter._SELECTION_HIGHLIGHT : SelectionHighlighter._SELECTION_HIGHLIGHT_OVERVIEW) + }; + }); + + this.decorations = this.editor.deltaDecorations(this.decorations, decorations); + } + + private static _SELECTION_HIGHLIGHT_OVERVIEW = ModelDecorationOptions.register({ + stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + className: 'selectionHighlight', + overviewRuler: { + color: themeColorFromId(overviewRulerSelectionHighlightForeground), + darkColor: themeColorFromId(overviewRulerSelectionHighlightForeground), + position: OverviewRulerLane.Center + } + }); + + private static _SELECTION_HIGHLIGHT = ModelDecorationOptions.register({ + stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + className: 'selectionHighlight', + }); + + public dispose(): void { + this._setState(null); + super.dispose(); + } +} + +function modelRangesContainSameText(model: IModel, ranges: Range[], matchCase: boolean): boolean { + const selectedText = getValueInRange(model, ranges[0], !matchCase); + for (let i = 1, len = ranges.length; i < len; i++) { + const range = ranges[i]; + if (range.isEmpty()) { + return false; + } + const thisSelectedText = getValueInRange(model, range, !matchCase); + if (selectedText !== thisSelectedText) { + return false; + } + } + return true; +} + +function getValueInRange(model: IModel, range: Range, toLowerCase: boolean): string { + const text = model.getValueInRange(range); + return (toLowerCase ? text.toLowerCase() : text); +} diff --git a/src/vs/editor/contrib/multicursor/test/common/multicursor.test.ts b/src/vs/editor/contrib/multicursor/test/common/multicursor.test.ts index 7824a0df71e..93c8377b9d0 100644 --- a/src/vs/editor/contrib/multicursor/test/common/multicursor.test.ts +++ b/src/vs/editor/contrib/multicursor/test/common/multicursor.test.ts @@ -5,11 +5,14 @@ 'use strict'; import * as assert from 'assert'; -import { withMockCodeEditor } from 'vs/editor/test/common/mocks/mockCodeEditor'; +import { withMockCodeEditor, MockCodeEditor } from 'vs/editor/test/common/mocks/mockCodeEditor'; import { Selection } from 'vs/editor/common/core/selection'; -import { InsertCursorAbove, InsertCursorBelow } from 'vs/editor/contrib/multicursor/common/multicursor'; -import { Handler } from 'vs/editor/common/editorCommon'; - +import { Range } from 'vs/editor/common/core/range'; +import { InsertCursorAbove, InsertCursorBelow, MultiCursorSelectionController, SelectHighlightsAction, AddSelectionToNextFindMatchAction } from 'vs/editor/contrib/multicursor/common/multicursor'; +import { Handler, EndOfLineSequence } from 'vs/editor/common/editorCommon'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { CommonFindController } from 'vs/editor/contrib/find/common/findController'; suite('Multicursor', () => { @@ -42,3 +45,525 @@ suite('Multicursor', () => { }); }); + +function fromRange(rng: Range): number[] { + return [rng.startLineNumber, rng.startColumn, rng.endLineNumber, rng.endColumn]; +} + +suite('Multicursor selection', () => { + let queryState: { [key: string]: any; } = {}; + let serviceCollection = new ServiceCollection(); + serviceCollection.set(IStorageService, { + get: (key: string) => queryState[key], + getBoolean: (key: string) => !!queryState[key], + store: (key: string, value: any) => { queryState[key] = value; } + }); + + test('issue #8817: Cursor position changes when you cancel multicursor', () => { + withMockCodeEditor([ + 'var x = (3 * 5)', + 'var y = (3 * 5)', + 'var z = (3 * 5)', + ], { serviceCollection: serviceCollection }, (editor, cursor) => { + + let findController = editor.registerAndInstantiateContribution(CommonFindController); + let multiCursorSelectController = editor.registerAndInstantiateContribution(MultiCursorSelectionController); + let selectHighlightsAction = new SelectHighlightsAction(); + + editor.setSelection(new Selection(2, 9, 2, 16)); + + selectHighlightsAction.run(null, editor); + assert.deepEqual(editor.getSelections().map(fromRange), [ + [2, 9, 2, 16], + [1, 9, 1, 16], + [3, 9, 3, 16], + ]); + + editor.trigger('test', 'removeSecondaryCursors', null); + + assert.deepEqual(fromRange(editor.getSelection()), [2, 9, 2, 16]); + + multiCursorSelectController.dispose(); + findController.dispose(); + }); + }); + + test('issue #5400: "Select All Occurrences of Find Match" does not select all if find uses regex', () => { + withMockCodeEditor([ + 'something', + 'someething', + 'someeething', + 'nothing' + ], { serviceCollection: serviceCollection }, (editor, cursor) => { + + let findController = editor.registerAndInstantiateContribution(CommonFindController); + let multiCursorSelectController = editor.registerAndInstantiateContribution(MultiCursorSelectionController); + let selectHighlightsAction = new SelectHighlightsAction(); + + editor._isFocused = false; + + editor.setSelection(new Selection(1, 1, 1, 1)); + findController.getState().change({ searchString: 'some+thing', isRegex: true, isRevealed: true }, false); + + selectHighlightsAction.run(null, editor); + assert.deepEqual(editor.getSelections().map(fromRange), [ + [1, 1, 1, 10], + [2, 1, 2, 11], + [3, 1, 3, 12], + ]); + + assert.equal(findController.getState().searchString, 'some+thing'); + + multiCursorSelectController.dispose(); + findController.dispose(); + }); + }); + + test('AddSelectionToNextFindMatchAction can work with multiline', () => { + withMockCodeEditor([ + '', + 'qwe', + 'rty', + '', + 'qwe', + '', + 'rty', + 'qwe', + 'rty' + ], { serviceCollection: serviceCollection }, (editor, cursor) => { + + let findController = editor.registerAndInstantiateContribution(CommonFindController); + let multiCursorSelectController = editor.registerAndInstantiateContribution(MultiCursorSelectionController); + let addSelectionToNextFindMatch = new AddSelectionToNextFindMatchAction(); + + editor.setSelection(new Selection(2, 1, 3, 4)); + + addSelectionToNextFindMatch.run(null, editor); + assert.deepEqual(editor.getSelections().map(fromRange), [ + [2, 1, 3, 4], + [8, 1, 9, 4] + ]); + + editor.trigger('test', 'removeSecondaryCursors', null); + + assert.deepEqual(fromRange(editor.getSelection()), [2, 1, 3, 4]); + + multiCursorSelectController.dispose(); + findController.dispose(); + }); + }); + + test('issue #6661: AddSelectionToNextFindMatchAction can work with touching ranges', () => { + withMockCodeEditor([ + 'abcabc', + 'abc', + 'abcabc', + ], { serviceCollection: serviceCollection }, (editor, cursor) => { + + let findController = editor.registerAndInstantiateContribution(CommonFindController); + let multiCursorSelectController = editor.registerAndInstantiateContribution(MultiCursorSelectionController); + let addSelectionToNextFindMatch = new AddSelectionToNextFindMatchAction(); + + editor.setSelection(new Selection(1, 1, 1, 4)); + + addSelectionToNextFindMatch.run(null, editor); + assert.deepEqual(editor.getSelections().map(fromRange), [ + [1, 1, 1, 4], + [1, 4, 1, 7] + ]); + + addSelectionToNextFindMatch.run(null, editor); + addSelectionToNextFindMatch.run(null, editor); + addSelectionToNextFindMatch.run(null, editor); + assert.deepEqual(editor.getSelections().map(fromRange), [ + [1, 1, 1, 4], + [1, 4, 1, 7], + [2, 1, 2, 4], + [3, 1, 3, 4], + [3, 4, 3, 7] + ]); + + editor.trigger('test', Handler.Type, { text: 'z' }); + assert.deepEqual(editor.getSelections().map(fromRange), [ + [1, 2, 1, 2], + [1, 3, 1, 3], + [2, 2, 2, 2], + [3, 2, 3, 2], + [3, 3, 3, 3] + ]); + assert.equal(editor.getValue(), [ + 'zz', + 'z', + 'zz', + ].join('\n')); + + multiCursorSelectController.dispose(); + findController.dispose(); + }); + }); + + test('issue #23541: Multiline Ctrl+D does not work in CRLF files', () => { + withMockCodeEditor([ + '', + 'qwe', + 'rty', + '', + 'qwe', + '', + 'rty', + 'qwe', + 'rty' + ], { serviceCollection: serviceCollection }, (editor, cursor) => { + + editor.getModel().setEOL(EndOfLineSequence.CRLF); + + let findController = editor.registerAndInstantiateContribution(CommonFindController); + let multiCursorSelectController = editor.registerAndInstantiateContribution(MultiCursorSelectionController); + let addSelectionToNextFindMatch = new AddSelectionToNextFindMatchAction(); + + editor.setSelection(new Selection(2, 1, 3, 4)); + + addSelectionToNextFindMatch.run(null, editor); + assert.deepEqual(editor.getSelections().map(fromRange), [ + [2, 1, 3, 4], + [8, 1, 9, 4] + ]); + + editor.trigger('test', 'removeSecondaryCursors', null); + + assert.deepEqual(fromRange(editor.getSelection()), [2, 1, 3, 4]); + + multiCursorSelectController.dispose(); + findController.dispose(); + }); + }); + + function testMulticursor(text: string[], callback: (editor: MockCodeEditor, findController: CommonFindController) => void): void { + withMockCodeEditor(text, { serviceCollection: serviceCollection }, (editor, cursor) => { + let findController = editor.registerAndInstantiateContribution(CommonFindController); + let multiCursorSelectController = editor.registerAndInstantiateContribution(MultiCursorSelectionController); + + callback(editor, findController); + + multiCursorSelectController.dispose(); + findController.dispose(); + }); + } + + function testAddSelectionToNextFindMatchAction(text: string[], callback: (editor: MockCodeEditor, action: AddSelectionToNextFindMatchAction, findController: CommonFindController) => void): void { + testMulticursor(text, (editor, findController) => { + let action = new AddSelectionToNextFindMatchAction(); + callback(editor, action, findController); + }); + } + + test('AddSelectionToNextFindMatchAction starting with single collapsed selection', () => { + const text = [ + 'abc pizza', + 'abc house', + 'abc bar' + ]; + testAddSelectionToNextFindMatchAction(text, (editor, action, findController) => { + editor.setSelections([ + new Selection(1, 2, 1, 2), + ]); + + action.run(null, editor); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 1, 1, 4), + ]); + + action.run(null, editor); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 1, 1, 4), + new Selection(2, 1, 2, 4), + ]); + + action.run(null, editor); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 1, 1, 4), + new Selection(2, 1, 2, 4), + new Selection(3, 1, 3, 4), + ]); + + action.run(null, editor); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 1, 1, 4), + new Selection(2, 1, 2, 4), + new Selection(3, 1, 3, 4), + ]); + }); + }); + + test('AddSelectionToNextFindMatchAction starting with two selections, one being collapsed 1)', () => { + const text = [ + 'abc pizza', + 'abc house', + 'abc bar' + ]; + testAddSelectionToNextFindMatchAction(text, (editor, action, findController) => { + editor.setSelections([ + new Selection(1, 1, 1, 4), + new Selection(2, 2, 2, 2), + ]); + + action.run(null, editor); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 1, 1, 4), + new Selection(2, 1, 2, 4), + ]); + + action.run(null, editor); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 1, 1, 4), + new Selection(2, 1, 2, 4), + new Selection(3, 1, 3, 4), + ]); + + action.run(null, editor); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 1, 1, 4), + new Selection(2, 1, 2, 4), + new Selection(3, 1, 3, 4), + ]); + }); + }); + + test('AddSelectionToNextFindMatchAction starting with two selections, one being collapsed 2)', () => { + const text = [ + 'abc pizza', + 'abc house', + 'abc bar' + ]; + testAddSelectionToNextFindMatchAction(text, (editor, action, findController) => { + editor.setSelections([ + new Selection(1, 2, 1, 2), + new Selection(2, 1, 2, 4), + ]); + + action.run(null, editor); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 1, 1, 4), + new Selection(2, 1, 2, 4), + ]); + + action.run(null, editor); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 1, 1, 4), + new Selection(2, 1, 2, 4), + new Selection(3, 1, 3, 4), + ]); + + action.run(null, editor); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 1, 1, 4), + new Selection(2, 1, 2, 4), + new Selection(3, 1, 3, 4), + ]); + }); + }); + + test('AddSelectionToNextFindMatchAction starting with all collapsed selections', () => { + const text = [ + 'abc pizza', + 'abc house', + 'abc bar' + ]; + testAddSelectionToNextFindMatchAction(text, (editor, action, findController) => { + editor.setSelections([ + new Selection(1, 2, 1, 2), + new Selection(2, 2, 2, 2), + new Selection(3, 1, 3, 1), + ]); + + action.run(null, editor); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 1, 1, 4), + new Selection(2, 1, 2, 4), + new Selection(3, 1, 3, 4), + ]); + + action.run(null, editor); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 1, 1, 4), + new Selection(2, 1, 2, 4), + new Selection(3, 1, 3, 4), + ]); + }); + }); + + test('AddSelectionToNextFindMatchAction starting with all collapsed selections on different words', () => { + const text = [ + 'abc pizza', + 'abc house', + 'abc bar' + ]; + testAddSelectionToNextFindMatchAction(text, (editor, action, findController) => { + editor.setSelections([ + new Selection(1, 6, 1, 6), + new Selection(2, 6, 2, 6), + new Selection(3, 6, 3, 6), + ]); + + action.run(null, editor); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 5, 1, 10), + new Selection(2, 5, 2, 10), + new Selection(3, 5, 3, 8), + ]); + + action.run(null, editor); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 5, 1, 10), + new Selection(2, 5, 2, 10), + new Selection(3, 5, 3, 8), + ]); + }); + }); + + test('issue #20651: AddSelectionToNextFindMatchAction case insensitive', () => { + const text = [ + 'test', + 'testte', + 'Test', + 'testte', + 'test' + ]; + testAddSelectionToNextFindMatchAction(text, (editor, action, findController) => { + editor.setSelections([ + new Selection(1, 1, 1, 5), + ]); + + action.run(null, editor); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 1, 1, 5), + new Selection(2, 1, 2, 5), + ]); + + action.run(null, editor); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 1, 1, 5), + new Selection(2, 1, 2, 5), + new Selection(3, 1, 3, 5), + ]); + + action.run(null, editor); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 1, 1, 5), + new Selection(2, 1, 2, 5), + new Selection(3, 1, 3, 5), + new Selection(4, 1, 4, 5), + ]); + + action.run(null, editor); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 1, 1, 5), + new Selection(2, 1, 2, 5), + new Selection(3, 1, 3, 5), + new Selection(4, 1, 4, 5), + new Selection(5, 1, 5, 5), + ]); + + action.run(null, editor); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 1, 1, 5), + new Selection(2, 1, 2, 5), + new Selection(3, 1, 3, 5), + new Selection(4, 1, 4, 5), + new Selection(5, 1, 5, 5), + ]); + }); + }); + + suite('Find state disassociation', () => { + + const text = [ + 'app', + 'apples', + 'whatsapp', + 'app', + 'App', + ' app' + ]; + + test('enters mode', () => { + testAddSelectionToNextFindMatchAction(text, (editor, action, findController) => { + editor.setSelections([ + new Selection(1, 2, 1, 2), + ]); + + action.run(null, editor); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 1, 1, 4), + ]); + + action.run(null, editor); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 1, 1, 4), + new Selection(4, 1, 4, 4), + ]); + + action.run(null, editor); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 1, 1, 4), + new Selection(4, 1, 4, 4), + new Selection(6, 2, 6, 5), + ]); + }); + }); + + test('leaves mode when selection changes', () => { + testAddSelectionToNextFindMatchAction(text, (editor, action, findController) => { + editor.setSelections([ + new Selection(1, 2, 1, 2), + ]); + + action.run(null, editor); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 1, 1, 4), + ]); + + action.run(null, editor); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 1, 1, 4), + new Selection(4, 1, 4, 4), + ]); + + // change selection + editor.setSelections([ + new Selection(1, 1, 1, 4), + ]); + + action.run(null, editor); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 1, 1, 4), + new Selection(2, 1, 2, 4), + ]); + }); + }); + + test('Select Highlights respects mode ', () => { + testMulticursor(text, (editor, findController) => { + let action = new SelectHighlightsAction(); + editor.setSelections([ + new Selection(1, 2, 1, 2), + ]); + + action.run(null, editor); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 1, 1, 4), + new Selection(4, 1, 4, 4), + new Selection(6, 2, 6, 5), + ]); + + action.run(null, editor); + assert.deepEqual(editor.getSelections(), [ + new Selection(1, 1, 1, 4), + new Selection(4, 1, 4, 4), + new Selection(6, 2, 6, 5), + ]); + }); + }); + + }); +}); diff --git a/src/vs/editor/contrib/quickFix/browser/lightBulbWidget.ts b/src/vs/editor/contrib/quickFix/browser/lightBulbWidget.ts index 2b76c9e2540..3ab3d31ffe2 100644 --- a/src/vs/editor/contrib/quickFix/browser/lightBulbWidget.ts +++ b/src/vs/editor/contrib/quickFix/browser/lightBulbWidget.ts @@ -35,6 +35,8 @@ export class LightBulbWidget implements IDisposable, IContentWidget { this._editor = editor; this._editor.addContentWidget(this); + this._disposables.push(this._editor.onDidChangeModel(_ => this._futureFixes.cancel())); + this._disposables.push(this._editor.onDidChangeModelLanguage(_ => this._futureFixes.cancel())); this._disposables.push(dom.addStandardDisposableListener(this._domNode, 'click', e => { // a bit of extra work to make sure the menu // doesn't cover the line-text diff --git a/src/vs/editor/contrib/referenceSearch/browser/referencesController.ts b/src/vs/editor/contrib/referenceSearch/browser/referencesController.ts index d268a392aff..7678766238c 100644 --- a/src/vs/editor/contrib/referenceSearch/browser/referencesController.ts +++ b/src/vs/editor/contrib/referenceSearch/browser/referencesController.ts @@ -123,7 +123,7 @@ export class ReferencesController implements editorCommon.IEditorContribution { switch (kind) { case 'open': if (event.source === 'editor' - && this._configurationService.lookup('editor.stablePeek').value) { + && this._configurationService.getValue('editor.stablePeek')) { // when stable peek is configured we don't close // the peek window on selecting the editor diff --git a/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.ts b/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.ts index 0d299ca85af..ce7638670eb 100644 --- a/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.ts +++ b/src/vs/editor/contrib/referenceSearch/browser/referencesWidget.ts @@ -38,7 +38,6 @@ import { ITextModelService, ITextEditorModel } from 'vs/editor/common/services/r import { registerColor, activeContrastBorder, contrastBorder } from 'vs/platform/theme/common/colorRegistry'; import { registerThemingParticipant, ITheme, IThemeService } from 'vs/platform/theme/common/themeService'; import { attachListStyler, attachBadgeStyler } from 'vs/platform/theme/common/styler'; -import { IModelDecorationsChangedEvent } from 'vs/editor/common/model/textModelEvents'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations'; @@ -81,7 +80,7 @@ class DecorationsManager implements IDisposable { } private _addDecorations(reference: FileReferences): void { - this._callOnModelChange.push(this._editor.getModel().onDidChangeDecorations((event) => this._onDecorationChanged(event))); + this._callOnModelChange.push(this._editor.getModel().onDidChangeDecorations((event) => this._onDecorationChanged())); this._editor.changeDecorations(accessor => { @@ -107,21 +106,20 @@ class DecorationsManager implements IDisposable { }); } - private _onDecorationChanged(event: IModelDecorationsChangedEvent): void { - const changedDecorations = event.changedDecorations, - toRemove: string[] = []; + private _onDecorationChanged(): void { + const toRemove: string[] = []; - for (let i = 0, len = changedDecorations.length; i < len; i++) { - let reference = this._decorations.get(changedDecorations[i]); - if (!reference) { - continue; + this._decorations.forEach((reference, decorationId) => { + const newRange = this._editor.getModel().getDecorationRange(decorationId); + + if (!newRange) { + return; } - const newRange = this._editor.getModel().getDecorationRange(changedDecorations[i]); let ignore = false; if (Range.equalsRange(newRange, reference.range)) { - continue; + return; } else if (Range.spansMultipleLines(newRange)) { ignore = true; @@ -137,11 +135,11 @@ class DecorationsManager implements IDisposable { if (ignore) { this._decorationIgnoreSet.add(reference.id); - toRemove.push(changedDecorations[i]); + toRemove.push(decorationId); } else { reference.range = newRange; } - } + }); this._editor.changeDecorations((accessor) => { for (let i = 0, len = toRemove.length; i < len; i++) { diff --git a/src/vs/editor/contrib/snippet/test/browser/snippetSession.test.ts b/src/vs/editor/contrib/snippet/test/browser/snippetSession.test.ts index 2bb0c11006d..05d96ea0b30 100644 --- a/src/vs/editor/contrib/snippet/test/browser/snippetSession.test.ts +++ b/src/vs/editor/contrib/snippet/test/browser/snippetSession.test.ts @@ -234,9 +234,9 @@ suite('SnippetSession', function () { assertSelections(editor, new Selection(1, 10, 1, 10), new Selection(2, 14, 2, 14)); session.prev(); - assertSelections(editor, new Selection(1, 7, 1, 7), new Selection(2, 11, 2, 11)); + assertSelections(editor, new Selection(1, 7, 1, 10), new Selection(2, 11, 2, 14)); session.prev(); - assertSelections(editor, new Selection(1, 4, 1, 4), new Selection(2, 8, 2, 8)); + assertSelections(editor, new Selection(1, 4, 1, 7), new Selection(2, 8, 2, 11)); session.prev(); assertSelections(editor, new Selection(1, 1, 1, 4), new Selection(2, 5, 2, 8)); }); diff --git a/src/vs/editor/contrib/snippet/test/browser/snippetVariables.test.ts b/src/vs/editor/contrib/snippet/test/browser/snippetVariables.test.ts index e5de27daa03..aad0d69ba7a 100644 --- a/src/vs/editor/contrib/snippet/test/browser/snippetVariables.test.ts +++ b/src/vs/editor/contrib/snippet/test/browser/snippetVariables.test.ts @@ -164,4 +164,12 @@ suite('Snippet Variables Resolver', function () { assertVariableResolve2('${foobarfoobar/(foo)/${2:+FAR}/g}', 'barbar'); // bad group reference }); + + // test('Snippet transforms do not handle regex with alternatives or optional matches, #36089', function () { + // assertVariableResolve2( + // '${TM_FILENAME/^(.)|(?:-(.))|(\\.js)/${1:+/upcase}${2:+/upcase}/g}', + // 'MyClass', + // 'my-class.js' + // ); + // }); }); diff --git a/src/vs/editor/contrib/zoneWidget/browser/zoneWidget.ts b/src/vs/editor/contrib/zoneWidget/browser/zoneWidget.ts index 24acb8e2eba..da7e44b07dd 100644 --- a/src/vs/editor/contrib/zoneWidget/browser/zoneWidget.ts +++ b/src/vs/editor/contrib/zoneWidget/browser/zoneWidget.ts @@ -395,7 +395,11 @@ export abstract class ZoneWidget implements IHorizontalSashLayoutProvider { // Reveal the line above or below the zone widget, to get the zone widget in the viewport const revealLineNumber = Math.min(this.editor.getModel().getLineCount(), Math.max(1, where.endLineNumber + 1)); - this.editor.revealLineInCenterIfOutsideViewport(revealLineNumber, ScrollType.Smooth); + this.revealLine(revealLineNumber); + } + + protected revealLine(lineNumber: number) { + this.editor.revealLine(lineNumber, ScrollType.Smooth); } protected setCssClass(className: string, classToReplace?: string): void { diff --git a/src/vs/editor/standalone/browser/simpleServices.ts b/src/vs/editor/standalone/browser/simpleServices.ts index 98464fac20a..e1d06c1220c 100644 --- a/src/vs/editor/standalone/browser/simpleServices.ts +++ b/src/vs/editor/standalone/browser/simpleServices.ts @@ -8,7 +8,7 @@ import { Schemas } from 'vs/base/common/network'; import Severity from 'vs/base/common/severity'; import URI from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; -import { IConfigurationService, IConfigurationServiceEvent, IConfigurationValue, IConfigurationKeys, IConfigurationValues, Configuration, IConfigurationData, ConfigurationModel, IConfigurationOverrides } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationService, IConfigurationChangeEvent, IConfigurationOverrides } from 'vs/platform/configuration/common/configuration'; import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { IEditor, IEditorInput, IEditorOptions, IEditorService, IResourceInput, Position } from 'vs/platform/editor/common/editor'; import { ICommandService, ICommand, ICommandEvent, ICommandHandler, CommandsRegistry } from 'vs/platform/commands/common/commands'; @@ -23,7 +23,7 @@ import * as editorCommon from 'vs/editor/common/editorCommon'; import { ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser'; import { Selection } from 'vs/editor/common/core/selection'; import Event, { Emitter } from 'vs/base/common/event'; -import { DefaultConfigurationModel } from 'vs/platform/configuration/common/model'; +import { Configuration, ConfigurationModel, DefaultConfigurationModel } from 'vs/platform/configuration/common/configurationModels'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IProgressService, IProgressRunner } from 'vs/platform/progress/common/progress'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration'; @@ -434,45 +434,68 @@ export class StandaloneKeybindingService extends AbstractKeybindingService { } } +function isConfigurationOverrides(thing: any): thing is IConfigurationOverrides { + return thing + && typeof thing === 'object' + && (!thing.overrideIdentifier || typeof thing.overrideIdentifier === 'string') + && (!thing.resource || thing.resource instanceof URI); +} + export class SimpleConfigurationService implements IConfigurationService { _serviceBrand: any; - private _onDidUpdateConfiguration = new Emitter(); - public onDidUpdateConfiguration: Event = this._onDidUpdateConfiguration.event; + private _onDidChangeConfiguration = new Emitter(); + public onDidChangeConfiguration: Event = this._onDidChangeConfiguration.event; - private _configuration: Configuration; + private _configuration: Configuration; constructor() { this._configuration = new Configuration(new DefaultConfigurationModel(), new ConfigurationModel()); } - private configuration(): Configuration { + private configuration(): Configuration { return this._configuration; } - public reloadConfiguration(section?: string): TPromise { - return TPromise.as(this.getConfiguration(section)); + getConfiguration(): T + getConfiguration(section: string): T + getConfiguration(overrides: IConfigurationOverrides): T + getConfiguration(section: string, overrides: IConfigurationOverrides): T + getConfiguration(arg1?: any, arg2?: any): any { + const section = typeof arg1 === 'string' ? arg1 : void 0; + const overrides = isConfigurationOverrides(arg1) ? arg1 : isConfigurationOverrides(arg2) ? arg2 : {}; + return this.configuration().getSection(section, overrides, null); } - public getConfiguration(section?: string, options?: IConfigurationOverrides): C { - return this.configuration().getValue(section, options); + public getValue(key: string, options: IConfigurationOverrides = {}): C { + return this.configuration().getValue(key, options, null); } - public lookup(key: string, options?: IConfigurationOverrides): IConfigurationValue { - return this.configuration().lookup(key, options); + public updateValue(key: string, value: any, arg3?: any, arg4?: any): TPromise { + return TPromise.as(null); } - public keys(): IConfigurationKeys { - return this.configuration().keys(); + public inspect(key: string, options: IConfigurationOverrides = {}): { + default: C, + user: C, + workspace: C, + workspaceFolder: C + value: C, + } { + return this.configuration().lookup(key, options, null); } - public values(): IConfigurationValues { - return this._configuration.values(); + public keys() { + return this.configuration().keys(null); } - public getConfigurationData(): IConfigurationData { - return this.configuration().toData(); + public reloadConfiguration(): TPromise { + return TPromise.as(null); + } + + public getConfigurationData() { + return null; } } @@ -480,12 +503,12 @@ export class SimpleResourceConfigurationService implements ITextResourceConfigur _serviceBrand: any; - public readonly onDidUpdateConfiguration: Event; - private readonly _onDidUpdateConfigurationEmitter = new Emitter(); + public readonly onDidChangeConfiguration: Event; + private readonly _onDidChangeConfigurationEmitter = new Emitter(); constructor(private configurationService: SimpleConfigurationService) { - this.configurationService.onDidUpdateConfiguration(() => { - this._onDidUpdateConfigurationEmitter.fire(); + this.configurationService.onDidChangeConfiguration((e) => { + this._onDidChangeConfigurationEmitter.fire(e); }); } @@ -575,4 +598,12 @@ export class SimpleWorkspaceContextService implements IWorkspaceContextService { public isCurrentWorkspace(workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): boolean { return true; } + + public addFolders(foldersToAdd: URI[]): TPromise { + return TPromise.as(void 0); + } + + public removeFolders(foldersToRemove: URI[]): TPromise { + return TPromise.as(void 0); + } } diff --git a/src/vs/editor/test/common/commands/sideEditing.test.ts b/src/vs/editor/test/common/commands/sideEditing.test.ts index 08fe4e0c5e7..ceaa7df8265 100644 --- a/src/vs/editor/test/common/commands/sideEditing.test.ts +++ b/src/vs/editor/test/common/commands/sideEditing.test.ts @@ -10,10 +10,11 @@ import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import { IIdentifiedSingleEditOperation } from 'vs/editor/common/editorCommon'; -import { ILineEdit, ModelLine, LineMarker, MarkersTracker } from 'vs/editor/common/model/modelLine'; import { withMockCodeEditor } from 'vs/editor/test/common/mocks/mockCodeEditor'; - -const NO_TAB_SIZE = 0; +import { Model } from 'vs/editor/common/model/model'; +import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration'; +import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl'; +import { Cursor } from 'vs/editor/common/controller/cursor'; function testCommand(lines: string[], selections: Selection[], edits: IIdentifiedSingleEditOperation[], expectedLines: string[], expectedSelections: Selection[]): void { withMockCodeEditor(lines, {}, (editor, cursor) => { @@ -31,15 +32,6 @@ function testCommand(lines: string[], selections: Selection[], edits: IIdentifie }); } -function testLineEditMarker(text: string, column: number, stickToPreviousCharacter: boolean, edit: ILineEdit, expectedColumn: number): void { - var line = new ModelLine(text, NO_TAB_SIZE); - line.addMarker(new LineMarker('1', 0, new Position(0, column), stickToPreviousCharacter)); - - line.applyEdits(new MarkersTracker(), [edit], NO_TAB_SIZE); - - assert.equal(line.getMarkers()[0].position.column, expectedColumn); -} - suite('Editor Side Editing - collapsed selection', () => { test('replace at selection', () => { @@ -86,14 +78,6 @@ suite('Editor Side Editing - collapsed selection', () => { ); }); - test('ModelLine.applyEdits uses `isReplace`', () => { - testLineEditMarker('something', 1, true, { startColumn: 1, endColumn: 1, text: 'asd', forceMoveMarkers: false }, 1); - testLineEditMarker('something', 1, true, { startColumn: 1, endColumn: 1, text: 'asd', forceMoveMarkers: true }, 4); - - testLineEditMarker('something', 1, false, { startColumn: 1, endColumn: 1, text: 'asd', forceMoveMarkers: false }, 4); - testLineEditMarker('something', 1, false, { startColumn: 1, endColumn: 1, text: 'asd', forceMoveMarkers: true }, 4); - }); - test('insert at selection', () => { testCommand( [ @@ -204,5 +188,699 @@ suite('Editor Side Editing - collapsed selection', () => { [new Selection(1, 1, 1, 1), new Selection(1, 3, 1, 3)] ); }); +}); +suite('SideEditing', () => { + + const LINES = [ + 'My First Line', + 'My Second Line', + 'Third Line' + ]; + + function _runTest(selection: Selection, editRange: Range, editText: string, editForceMoveMarkers: boolean, expected: Selection, msg: string): void { + const model = Model.createFromString(LINES.join('\n')); + const config = new TestConfiguration(null); + const viewModel = new ViewModel(0, config, model, null); + const cursor = new Cursor(config, model, viewModel); + + cursor.setSelections('tests', [selection]); + model.applyEdits([{ range: editRange, text: editText, forceMoveMarkers: editForceMoveMarkers, identifier: null }]); + const actual = cursor.getSelection(); + assert.deepEqual(actual.toString(), expected.toString(), msg); + + cursor.dispose(); + viewModel.dispose(); + config.dispose(); + model.dispose(); + } + + function runTest(selection: Range, editRange: Range, editText: string, expected: Selection[][]): void { + const sel1 = new Selection(selection.startLineNumber, selection.startColumn, selection.endLineNumber, selection.endColumn); + _runTest(sel1, editRange, editText, false, expected[0][0], '0-0-regular-no-force'); + _runTest(sel1, editRange, editText, true, expected[1][0], '1-0-regular-force'); + + // RTL selection + const sel2 = new Selection(selection.endLineNumber, selection.endColumn, selection.startLineNumber, selection.startColumn); + _runTest(sel2, editRange, editText, false, expected[0][1], '0-1-inverse-no-force'); + _runTest(sel2, editRange, editText, true, expected[1][1], '1-1-inverse-force'); + } + + suite('insert', () => { + suite('collapsed sel', () => { + test('before', () => { + runTest( + new Range(1, 4, 1, 4), + new Range(1, 3, 1, 3), 'xx', + [ + [new Selection(1, 6, 1, 6), new Selection(1, 6, 1, 6)], + [new Selection(1, 6, 1, 6), new Selection(1, 6, 1, 6)], + ] + ); + }); + test('equal', () => { + runTest( + new Range(1, 4, 1, 4), + new Range(1, 4, 1, 4), 'xx', + [ + [new Selection(1, 4, 1, 6), new Selection(1, 4, 1, 6)], + [new Selection(1, 6, 1, 6), new Selection(1, 6, 1, 6)], + ] + ); + }); + test('after', () => { + runTest( + new Range(1, 4, 1, 4), + new Range(1, 5, 1, 5), 'xx', + [ + [new Selection(1, 4, 1, 4), new Selection(1, 4, 1, 4)], + [new Selection(1, 4, 1, 4), new Selection(1, 4, 1, 4)], + ] + ); + }); + }); + suite('non-collapsed dec', () => { + test('before', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 3, 1, 3), 'xx', + [ + [new Selection(1, 6, 1, 11), new Selection(1, 11, 1, 6)], + [new Selection(1, 6, 1, 11), new Selection(1, 11, 1, 6)], + ] + ); + }); + test('start', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 4, 1, 4), 'xx', + [ + [new Selection(1, 4, 1, 11), new Selection(1, 11, 1, 4)], + [new Selection(1, 6, 1, 11), new Selection(1, 11, 1, 6)], + ] + ); + }); + test('inside', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 5, 1, 5), 'xx', + [ + [new Selection(1, 4, 1, 11), new Selection(1, 11, 1, 4)], + [new Selection(1, 4, 1, 11), new Selection(1, 11, 1, 4)], + ] + ); + }); + test('end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 9, 1, 9), 'xx', + [ + [new Selection(1, 4, 1, 11), new Selection(1, 11, 1, 4)], + [new Selection(1, 4, 1, 11), new Selection(1, 11, 1, 4)], + ] + ); + }); + test('after', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 10, 1, 10), 'xx', + [ + [new Selection(1, 4, 1, 9), new Selection(1, 9, 1, 4)], + [new Selection(1, 4, 1, 9), new Selection(1, 9, 1, 4)], + ] + ); + }); + }); + }); + + suite('delete', () => { + suite('collapsed dec', () => { + test('edit.end < range.start', () => { + runTest( + new Range(1, 4, 1, 4), + new Range(1, 1, 1, 3), '', + [ + [new Selection(1, 2, 1, 2), new Selection(1, 2, 1, 2)], + [new Selection(1, 2, 1, 2), new Selection(1, 2, 1, 2)], + ] + ); + }); + test('edit.end <= range.start', () => { + runTest( + new Range(1, 4, 1, 4), + new Range(1, 2, 1, 4), '', + [ + [new Selection(1, 2, 1, 2), new Selection(1, 2, 1, 2)], + [new Selection(1, 2, 1, 2), new Selection(1, 2, 1, 2)], + ] + ); + }); + test('edit.start < range.start && edit.end > range.end', () => { + runTest( + new Range(1, 4, 1, 4), + new Range(1, 3, 1, 5), '', + [ + [new Selection(1, 3, 1, 3), new Selection(1, 3, 1, 3)], + [new Selection(1, 3, 1, 3), new Selection(1, 3, 1, 3)], + ] + ); + }); + test('edit.start >= range.end', () => { + runTest( + new Range(1, 4, 1, 4), + new Range(1, 4, 1, 6), '', + [ + [new Selection(1, 4, 1, 4), new Selection(1, 4, 1, 4)], + [new Selection(1, 4, 1, 4), new Selection(1, 4, 1, 4)], + ] + ); + }); + test('edit.start > range.end', () => { + runTest( + new Range(1, 4, 1, 4), + new Range(1, 5, 1, 7), '', + [ + [new Selection(1, 4, 1, 4), new Selection(1, 4, 1, 4)], + [new Selection(1, 4, 1, 4), new Selection(1, 4, 1, 4)], + ] + ); + }); + }); + suite('non-collapsed dec', () => { + test('edit.end < range.start', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 1, 1, 3), '', + [ + [new Selection(1, 2, 1, 7), new Selection(1, 7, 1, 2)], + [new Selection(1, 2, 1, 7), new Selection(1, 7, 1, 2)], + ] + ); + }); + test('edit.end <= range.start', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 2, 1, 4), '', + [ + [new Selection(1, 2, 1, 7), new Selection(1, 7, 1, 2)], + [new Selection(1, 2, 1, 7), new Selection(1, 7, 1, 2)], + ] + ); + }); + test('edit.start < range.start && edit.end < range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 3, 1, 5), '', + [ + [new Selection(1, 3, 1, 7), new Selection(1, 7, 1, 3)], + [new Selection(1, 3, 1, 7), new Selection(1, 7, 1, 3)], + ] + ); + }); + + test('edit.start < range.start && edit.end == range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 3, 1, 9), '', + [ + [new Selection(1, 3, 1, 3), new Selection(1, 3, 1, 3)], + [new Selection(1, 3, 1, 3), new Selection(1, 3, 1, 3)], + ] + ); + }); + + test('edit.start < range.start && edit.end > range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 3, 1, 10), '', + [ + [new Selection(1, 3, 1, 3), new Selection(1, 3, 1, 3)], + [new Selection(1, 3, 1, 3), new Selection(1, 3, 1, 3)], + ] + ); + }); + + test('edit.start == range.start && edit.end < range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 4, 1, 6), '', + [ + [new Selection(1, 4, 1, 7), new Selection(1, 7, 1, 4)], + [new Selection(1, 4, 1, 7), new Selection(1, 7, 1, 4)], + ] + ); + }); + + test('edit.start == range.start && edit.end == range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 4, 1, 9), '', + [ + [new Selection(1, 4, 1, 4), new Selection(1, 4, 1, 4)], + [new Selection(1, 4, 1, 4), new Selection(1, 4, 1, 4)], + ] + ); + }); + + test('edit.start == range.start && edit.end > range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 4, 1, 10), '', + [ + [new Selection(1, 4, 1, 4), new Selection(1, 4, 1, 4)], + [new Selection(1, 4, 1, 4), new Selection(1, 4, 1, 4)], + ] + ); + }); + + test('edit.start > range.start && edit.start < range.end && edit.end < range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 5, 1, 7), '', + [ + [new Selection(1, 4, 1, 7), new Selection(1, 7, 1, 4)], + [new Selection(1, 4, 1, 7), new Selection(1, 7, 1, 4)], + ] + ); + }); + + test('edit.start > range.start && edit.start < range.end && edit.end == range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 5, 1, 9), '', + [ + [new Selection(1, 4, 1, 5), new Selection(1, 5, 1, 4)], + [new Selection(1, 4, 1, 5), new Selection(1, 5, 1, 4)], + ] + ); + }); + + test('edit.start > range.start && edit.start < range.end && edit.end > range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 5, 1, 10), '', + [ + [new Selection(1, 4, 1, 5), new Selection(1, 5, 1, 4)], + [new Selection(1, 4, 1, 5), new Selection(1, 5, 1, 4)], + ] + ); + }); + + test('edit.start == range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 9, 1, 11), '', + [ + [new Selection(1, 4, 1, 9), new Selection(1, 9, 1, 4)], + [new Selection(1, 4, 1, 9), new Selection(1, 9, 1, 4)], + ] + ); + }); + + test('edit.start > range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 10, 1, 11), '', + [ + [new Selection(1, 4, 1, 9), new Selection(1, 9, 1, 4)], + [new Selection(1, 4, 1, 9), new Selection(1, 9, 1, 4)], + ] + ); + }); + }); + }); + + suite('replace short', () => { + suite('collapsed dec', () => { + test('edit.end < range.start', () => { + runTest( + new Range(1, 4, 1, 4), + new Range(1, 1, 1, 3), 'c', + [ + [new Selection(1, 3, 1, 3), new Selection(1, 3, 1, 3)], + [new Selection(1, 3, 1, 3), new Selection(1, 3, 1, 3)], + ] + ); + }); + test('edit.end <= range.start', () => { + runTest( + new Range(1, 4, 1, 4), + new Range(1, 2, 1, 4), 'c', + [ + [new Selection(1, 3, 1, 3), new Selection(1, 3, 1, 3)], + [new Selection(1, 3, 1, 3), new Selection(1, 3, 1, 3)], + ] + ); + }); + test('edit.start < range.start && edit.end > range.end', () => { + runTest( + new Range(1, 4, 1, 4), + new Range(1, 3, 1, 5), 'c', + [ + [new Selection(1, 4, 1, 4), new Selection(1, 4, 1, 4)], + [new Selection(1, 4, 1, 4), new Selection(1, 4, 1, 4)], + ] + ); + }); + test('edit.start >= range.end', () => { + runTest( + new Range(1, 4, 1, 4), + new Range(1, 4, 1, 6), 'c', + [ + [new Selection(1, 4, 1, 4), new Selection(1, 4, 1, 4)], + [new Selection(1, 5, 1, 5), new Selection(1, 5, 1, 5)], + ] + ); + }); + test('edit.start > range.end', () => { + runTest( + new Range(1, 4, 1, 4), + new Range(1, 5, 1, 7), 'c', + [ + [new Selection(1, 4, 1, 4), new Selection(1, 4, 1, 4)], + [new Selection(1, 4, 1, 4), new Selection(1, 4, 1, 4)], + ] + ); + }); + }); + suite('non-collapsed dec', () => { + test('edit.end < range.start', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 1, 1, 3), 'c', + [ + [new Selection(1, 3, 1, 8), new Selection(1, 8, 1, 3)], + [new Selection(1, 3, 1, 8), new Selection(1, 8, 1, 3)], + ] + ); + }); + test('edit.end <= range.start', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 2, 1, 4), 'c', + [ + [new Selection(1, 3, 1, 8), new Selection(1, 8, 1, 3)], + [new Selection(1, 3, 1, 8), new Selection(1, 8, 1, 3)], + ] + ); + }); + test('edit.start < range.start && edit.end < range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 3, 1, 5), 'c', + [ + [new Selection(1, 4, 1, 8), new Selection(1, 8, 1, 4)], + [new Selection(1, 4, 1, 8), new Selection(1, 8, 1, 4)], + ] + ); + }); + test('edit.start < range.start && edit.end == range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 3, 1, 9), 'c', + [ + [new Selection(1, 4, 1, 4), new Selection(1, 4, 1, 4)], + [new Selection(1, 4, 1, 4), new Selection(1, 4, 1, 4)], + ] + ); + }); + test('edit.start < range.start && edit.end > range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 3, 1, 10), 'c', + [ + [new Selection(1, 4, 1, 4), new Selection(1, 4, 1, 4)], + [new Selection(1, 4, 1, 4), new Selection(1, 4, 1, 4)], + ] + ); + }); + test('edit.start == range.start && edit.end < range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 4, 1, 6), 'c', + [ + [new Selection(1, 4, 1, 8), new Selection(1, 8, 1, 4)], + [new Selection(1, 5, 1, 8), new Selection(1, 8, 1, 5)], + ] + ); + }); + test('edit.start == range.start && edit.end == range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 4, 1, 9), 'c', + [ + [new Selection(1, 4, 1, 5), new Selection(1, 5, 1, 4)], + [new Selection(1, 5, 1, 5), new Selection(1, 5, 1, 5)], + ] + ); + }); + test('edit.start == range.start && edit.end > range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 4, 1, 10), 'c', + [ + [new Selection(1, 4, 1, 5), new Selection(1, 5, 1, 4)], + [new Selection(1, 5, 1, 5), new Selection(1, 5, 1, 5)], + ] + ); + }); + test('edit.start > range.start && edit.start < range.end && edit.end < range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 5, 1, 7), 'c', + [ + [new Selection(1, 4, 1, 8), new Selection(1, 8, 1, 4)], + [new Selection(1, 4, 1, 8), new Selection(1, 8, 1, 4)], + ] + ); + }); + test('edit.start > range.start && edit.start < range.end && edit.end == range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 5, 1, 9), 'c', + [ + [new Selection(1, 4, 1, 6), new Selection(1, 6, 1, 4)], + [new Selection(1, 4, 1, 6), new Selection(1, 6, 1, 4)], + ] + ); + }); + test('edit.start > range.start && edit.start < range.end && edit.end > range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 5, 1, 10), 'c', + [ + [new Selection(1, 4, 1, 6), new Selection(1, 6, 1, 4)], + [new Selection(1, 4, 1, 6), new Selection(1, 6, 1, 4)], + ] + ); + }); + test('edit.start == range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 9, 1, 11), 'c', + [ + [new Selection(1, 4, 1, 9), new Selection(1, 9, 1, 4)], + [new Selection(1, 4, 1, 10), new Selection(1, 10, 1, 4)], + ] + ); + }); + test('edit.start > range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 10, 1, 11), 'c', + [ + [new Selection(1, 4, 1, 9), new Selection(1, 9, 1, 4)], + [new Selection(1, 4, 1, 9), new Selection(1, 9, 1, 4)], + ] + ); + }); + }); + }); + + suite('replace long', () => { + suite('collapsed dec', () => { + test('edit.end < range.start', () => { + runTest( + new Range(1, 4, 1, 4), + new Range(1, 1, 1, 3), 'cccc', + [ + [new Selection(1, 6, 1, 6), new Selection(1, 6, 1, 6)], + [new Selection(1, 6, 1, 6), new Selection(1, 6, 1, 6)], + ] + ); + }); + test('edit.end <= range.start', () => { + runTest( + new Range(1, 4, 1, 4), + new Range(1, 2, 1, 4), 'cccc', + [ + [new Selection(1, 4, 1, 6), new Selection(1, 4, 1, 6)], + [new Selection(1, 6, 1, 6), new Selection(1, 6, 1, 6)], + ] + ); + }); + test('edit.start < range.start && edit.end > range.end', () => { + runTest( + new Range(1, 4, 1, 4), + new Range(1, 3, 1, 5), 'cccc', + [ + [new Selection(1, 4, 1, 4), new Selection(1, 4, 1, 4)], + [new Selection(1, 7, 1, 7), new Selection(1, 7, 1, 7)], + ] + ); + }); + test('edit.start >= range.end', () => { + runTest( + new Range(1, 4, 1, 4), + new Range(1, 4, 1, 6), 'cccc', + [ + [new Selection(1, 4, 1, 4), new Selection(1, 4, 1, 4)], + [new Selection(1, 8, 1, 8), new Selection(1, 8, 1, 8)], + ] + ); + }); + test('edit.start > range.end', () => { + runTest( + new Range(1, 4, 1, 4), + new Range(1, 5, 1, 7), 'cccc', + [ + [new Selection(1, 4, 1, 4), new Selection(1, 4, 1, 4)], + [new Selection(1, 4, 1, 4), new Selection(1, 4, 1, 4)], + ] + ); + }); + }); + suite('non-collapsed dec', () => { + test('edit.end < range.start', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 1, 1, 3), 'cccc', + [ + [new Selection(1, 6, 1, 11), new Selection(1, 11, 1, 6)], + [new Selection(1, 6, 1, 11), new Selection(1, 11, 1, 6)], + ] + ); + }); + test('edit.end <= range.start', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 2, 1, 4), 'cccc', + [ + [new Selection(1, 4, 1, 11), new Selection(1, 11, 1, 4)], + [new Selection(1, 6, 1, 11), new Selection(1, 11, 1, 6)], + ] + ); + }); + test('edit.start < range.start && edit.end < range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 3, 1, 5), 'cccc', + [ + [new Selection(1, 4, 1, 11), new Selection(1, 11, 1, 4)], + [new Selection(1, 7, 1, 11), new Selection(1, 11, 1, 7)], + ] + ); + }); + test('edit.start < range.start && edit.end == range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 3, 1, 9), 'cccc', + [ + [new Selection(1, 4, 1, 7), new Selection(1, 7, 1, 4)], + [new Selection(1, 7, 1, 7), new Selection(1, 7, 1, 7)], + ] + ); + }); + test('edit.start < range.start && edit.end > range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 3, 1, 10), 'cccc', + [ + [new Selection(1, 4, 1, 7), new Selection(1, 7, 1, 4)], + [new Selection(1, 7, 1, 7), new Selection(1, 7, 1, 7)], + ] + ); + }); + test('edit.start == range.start && edit.end < range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 4, 1, 6), 'cccc', + [ + [new Selection(1, 4, 1, 11), new Selection(1, 11, 1, 4)], + [new Selection(1, 8, 1, 11), new Selection(1, 11, 1, 8)], + ] + ); + }); + test('edit.start == range.start && edit.end == range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 4, 1, 9), 'cccc', + [ + [new Selection(1, 4, 1, 8), new Selection(1, 8, 1, 4)], + [new Selection(1, 8, 1, 8), new Selection(1, 8, 1, 8)], + ] + ); + }); + test('edit.start == range.start && edit.end > range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 4, 1, 10), 'cccc', + [ + [new Selection(1, 4, 1, 8), new Selection(1, 8, 1, 4)], + [new Selection(1, 8, 1, 8), new Selection(1, 8, 1, 8)], + ] + ); + }); + test('edit.start > range.start && edit.start < range.end && edit.end < range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 5, 1, 7), 'cccc', + [ + [new Selection(1, 4, 1, 11), new Selection(1, 11, 1, 4)], + [new Selection(1, 4, 1, 11), new Selection(1, 11, 1, 4)], + ] + ); + }); + test('edit.start > range.start && edit.start < range.end && edit.end == range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 5, 1, 9), 'cccc', + [ + [new Selection(1, 4, 1, 9), new Selection(1, 9, 1, 4)], + [new Selection(1, 4, 1, 9), new Selection(1, 9, 1, 4)], + ] + ); + }); + test('edit.start > range.start && edit.start < range.end && edit.end > range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 5, 1, 10), 'cccc', + [ + [new Selection(1, 4, 1, 9), new Selection(1, 9, 1, 4)], + [new Selection(1, 4, 1, 9), new Selection(1, 9, 1, 4)], + ] + ); + }); + test('edit.start == range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 9, 1, 11), 'cccc', + [ + [new Selection(1, 4, 1, 9), new Selection(1, 9, 1, 4)], + [new Selection(1, 4, 1, 13), new Selection(1, 13, 1, 4)], + ] + ); + }); + test('edit.start > range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 10, 1, 11), 'cccc', + [ + [new Selection(1, 4, 1, 9), new Selection(1, 9, 1, 4)], + [new Selection(1, 4, 1, 9), new Selection(1, 9, 1, 4)], + ] + ); + }); + }); + }); }); \ No newline at end of file diff --git a/src/vs/editor/test/common/controller/cursor.test.ts b/src/vs/editor/test/common/controller/cursor.test.ts index e21b52a667a..98b3810dec4 100644 --- a/src/vs/editor/test/common/controller/cursor.test.ts +++ b/src/vs/editor/test/common/controller/cursor.test.ts @@ -1453,9 +1453,9 @@ suite('Editor Controller - Regression tests', () => { cursorCommand(cursor, H.Undo); assert.equal(model.getValue(), [ - 'some lines', - 'and more lines', - 'just some text', + ' some lines', + ' and more lines', + ' just some text', ].join('\n'), '002'); cursorCommand(cursor, H.Undo); @@ -1464,6 +1464,13 @@ suite('Editor Controller - Regression tests', () => { 'and more lines', 'just some text', ].join('\n'), '003'); + + cursorCommand(cursor, H.Undo); + assert.equal(model.getValue(), [ + 'some lines', + 'and more lines', + 'just some text', + ].join('\n'), '004'); }); model.dispose(); @@ -1622,6 +1629,24 @@ suite('Editor Controller - Regression tests', () => { }); }); + test('issue #33788: Wrong cursor position when double click to select a word', () => { + let model = Model.createFromString( + [ + 'Just some text' + ].join('\n') + ); + + withMockCodeEditor(null, { model: model }, (editor, cursor) => { + CoreNavigationCommands.WordSelect.runCoreEditorCommand(cursor, { position: new Position(1, 8) }); + assert.deepEqual(cursor.getSelection(), new Selection(1, 6, 1, 10)); + + CoreNavigationCommands.WordSelectDrag.runCoreEditorCommand(cursor, { position: new Position(1, 8) }); + assert.deepEqual(cursor.getSelection(), new Selection(1, 6, 1, 10)); + }); + + model.dispose(); + }); + test('issue #9675: Undo/Redo adds a stop in between CHN Characters', () => { usingCursor({ text: [ @@ -1651,7 +1676,8 @@ suite('Editor Controller - Regression tests', () => { }); }); - test('issue #23913: Greater than 1000+ multi cursor typing replacement text appears inverted, lines begin to drop off selection', () => { + test('issue #23913: Greater than 1000+ multi cursor typing replacement text appears inverted, lines begin to drop off selection', function () { + this.timeout(10000); const LINE_CNT = 2000; let text = []; @@ -3105,6 +3131,93 @@ suite('Editor Controller - Indentation Rules', () => { assert.equal(model.getLineContent(3), '}'); }); }); + + test('issue #36090: JS: editor.autoIndent seems to be broken', () => { + class JSMode extends MockMode { + private static _id = new LanguageIdentifier('indentRulesMode', 4); + constructor() { + super(JSMode._id); + this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), { + brackets: [ + ['{', '}'], + ['[', ']'], + ['(', ')'] + ], + indentationRules: { + // ^(.*\*/)?\s*\}.*$ + decreaseIndentPattern: /^((?!.*?\/\*).*\*\/)?\s*[\}\]\)].*$/, + // ^.*\{[^}"']*$ + increaseIndentPattern: /^((?!\/\/).)*(\{[^}"'`]*|\([^)"'`]*|\[[^\]"'`]*)$/ + }, + onEnterRules: [ + { + // e.g. /** | */ + beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/, + afterText: /^\s*\*\/$/, + action: { indentAction: IndentAction.IndentOutdent, appendText: ' * ' } + }, { + // e.g. /** ...| + beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/, + action: { indentAction: IndentAction.None, appendText: ' * ' } + }, { + // e.g. * ...| + beforeText: /^(\t|(\ \ ))*\ \*(\ ([^\*]|\*(?!\/))*)?$/, + action: { indentAction: IndentAction.None, appendText: '* ' } + }, { + // e.g. */| + beforeText: /^(\t|(\ \ ))*\ \*\/\s*$/, + action: { indentAction: IndentAction.None, removeText: 1 } + }, + { + // e.g. *-----*/| + beforeText: /^(\t|(\ \ ))*\ \*[^/]*\*\/\s*$/, + action: { indentAction: IndentAction.None, removeText: 1 } + } + ] + })); + } + } + + let mode = new JSMode(); + let model = Model.createFromString( + [ + 'class ItemCtrl {', + ' getPropertiesByItemId(id) {', + ' return this.fetchItem(id)', + ' .then(item => {', + ' return this.getPropertiesOfItem(item);', + ' });', + ' }', + '}', + ].join('\n'), + undefined, + mode.getLanguageIdentifier() + ); + + withMockCodeEditor(null, { model: model, autoIndent: false }, (editor, cursor) => { + moveTo(cursor, 7, 6, false); + assertCursor(cursor, new Selection(7, 6, 7, 6)); + + cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard'); + assert.equal(model.getValue(), + [ + 'class ItemCtrl {', + ' getPropertiesByItemId(id) {', + ' return this.fetchItem(id)', + ' .then(item => {', + ' return this.getPropertiesOfItem(item);', + ' });', + ' }', + ' ', + '}', + ].join('\n') + ); + assertCursor(cursor, new Selection(8, 5, 8, 5)); + }); + + model.dispose(); + mode.dispose(); + }); }); interface ICursorOpts { @@ -3727,3 +3840,232 @@ suite('autoClosingPairs', () => { }); }); }); + +suite('Undo stops', () => { + + test('there is an undo stop between typing and deleting left', () => { + let model = Model.createFromString( + [ + 'A line', + 'Another line', + ].join('\n') + ); + + withMockCodeEditor(null, { model: model }, (editor, cursor) => { + cursor.setSelections('test', [new Selection(1, 3, 1, 3)]); + cursorCommand(cursor, H.Type, { text: 'first' }, 'keyboard'); + assert.equal(model.getLineContent(1), 'A first line'); + assertCursor(cursor, new Selection(1, 8, 1, 8)); + + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + assert.equal(model.getLineContent(1), 'A fir line'); + assertCursor(cursor, new Selection(1, 6, 1, 6)); + + cursorCommand(cursor, H.Undo, {}); + assert.equal(model.getLineContent(1), 'A first line'); + assertCursor(cursor, new Selection(1, 8, 1, 8)); + + cursorCommand(cursor, H.Undo, {}); + assert.equal(model.getLineContent(1), 'A line'); + assertCursor(cursor, new Selection(1, 3, 1, 3)); + }); + }); + + test('there is an undo stop between typing and deleting right', () => { + let model = Model.createFromString( + [ + 'A line', + 'Another line', + ].join('\n') + ); + + withMockCodeEditor(null, { model: model }, (editor, cursor) => { + cursor.setSelections('test', [new Selection(1, 3, 1, 3)]); + cursorCommand(cursor, H.Type, { text: 'first' }, 'keyboard'); + assert.equal(model.getLineContent(1), 'A first line'); + assertCursor(cursor, new Selection(1, 8, 1, 8)); + + CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null); + CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null); + assert.equal(model.getLineContent(1), 'A firstine'); + assertCursor(cursor, new Selection(1, 8, 1, 8)); + + cursorCommand(cursor, H.Undo, {}); + assert.equal(model.getLineContent(1), 'A first line'); + assertCursor(cursor, new Selection(1, 8, 1, 8)); + + cursorCommand(cursor, H.Undo, {}); + assert.equal(model.getLineContent(1), 'A line'); + assertCursor(cursor, new Selection(1, 3, 1, 3)); + }); + }); + + test('there is an undo stop between deleting left and typing', () => { + let model = Model.createFromString( + [ + 'A line', + 'Another line', + ].join('\n') + ); + + withMockCodeEditor(null, { model: model }, (editor, cursor) => { + cursor.setSelections('test', [new Selection(2, 8, 2, 8)]); + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + assert.equal(model.getLineContent(2), ' line'); + assertCursor(cursor, new Selection(2, 1, 2, 1)); + + cursorCommand(cursor, H.Type, { text: 'Second' }, 'keyboard'); + assert.equal(model.getLineContent(2), 'Second line'); + assertCursor(cursor, new Selection(2, 7, 2, 7)); + + cursorCommand(cursor, H.Undo, {}); + assert.equal(model.getLineContent(2), ' line'); + assertCursor(cursor, new Selection(2, 1, 2, 1)); + + cursorCommand(cursor, H.Undo, {}); + assert.equal(model.getLineContent(2), 'Another line'); + assertCursor(cursor, new Selection(2, 8, 2, 8)); + }); + }); + + test('there is an undo stop between deleting left and deleting right', () => { + let model = Model.createFromString( + [ + 'A line', + 'Another line', + ].join('\n') + ); + + withMockCodeEditor(null, { model: model }, (editor, cursor) => { + cursor.setSelections('test', [new Selection(2, 8, 2, 8)]); + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + assert.equal(model.getLineContent(2), ' line'); + assertCursor(cursor, new Selection(2, 1, 2, 1)); + + CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null); + CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null); + CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null); + CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null); + CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null); + assert.equal(model.getLineContent(2), ''); + assertCursor(cursor, new Selection(2, 1, 2, 1)); + + cursorCommand(cursor, H.Undo, {}); + assert.equal(model.getLineContent(2), ' line'); + assertCursor(cursor, new Selection(2, 1, 2, 1)); + + cursorCommand(cursor, H.Undo, {}); + assert.equal(model.getLineContent(2), 'Another line'); + assertCursor(cursor, new Selection(2, 8, 2, 8)); + }); + }); + + test('there is an undo stop between deleting right and typing', () => { + let model = Model.createFromString( + [ + 'A line', + 'Another line', + ].join('\n') + ); + + withMockCodeEditor(null, { model: model }, (editor, cursor) => { + cursor.setSelections('test', [new Selection(2, 9, 2, 9)]); + CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null); + CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null); + CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null); + CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null); + assert.equal(model.getLineContent(2), 'Another '); + assertCursor(cursor, new Selection(2, 9, 2, 9)); + + cursorCommand(cursor, H.Type, { text: 'text' }, 'keyboard'); + assert.equal(model.getLineContent(2), 'Another text'); + assertCursor(cursor, new Selection(2, 13, 2, 13)); + + cursorCommand(cursor, H.Undo, {}); + assert.equal(model.getLineContent(2), 'Another '); + assertCursor(cursor, new Selection(2, 9, 2, 9)); + + cursorCommand(cursor, H.Undo, {}); + assert.equal(model.getLineContent(2), 'Another line'); + assertCursor(cursor, new Selection(2, 9, 2, 9)); + }); + }); + + test('there is an undo stop between deleting right and deleting left', () => { + let model = Model.createFromString( + [ + 'A line', + 'Another line', + ].join('\n') + ); + + withMockCodeEditor(null, { model: model }, (editor, cursor) => { + cursor.setSelections('test', [new Selection(2, 9, 2, 9)]); + CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null); + CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null); + CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null); + CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null); + assert.equal(model.getLineContent(2), 'Another '); + assertCursor(cursor, new Selection(2, 9, 2, 9)); + + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + assert.equal(model.getLineContent(2), 'An'); + assertCursor(cursor, new Selection(2, 3, 2, 3)); + + cursorCommand(cursor, H.Undo, {}); + assert.equal(model.getLineContent(2), 'Another '); + assertCursor(cursor, new Selection(2, 9, 2, 9)); + + cursorCommand(cursor, H.Undo, {}); + assert.equal(model.getLineContent(2), 'Another line'); + assertCursor(cursor, new Selection(2, 9, 2, 9)); + }); + }); + + test('inserts undo stop when typing space', () => { + let model = Model.createFromString( + [ + 'A line', + 'Another line', + ].join('\n') + ); + + withMockCodeEditor(null, { model: model }, (editor, cursor) => { + cursor.setSelections('test', [new Selection(1, 3, 1, 3)]); + cursorCommand(cursor, H.Type, { text: 'first and interesting' }, 'keyboard'); + assert.equal(model.getLineContent(1), 'A first and interesting line'); + assertCursor(cursor, new Selection(1, 24, 1, 24)); + + cursorCommand(cursor, H.Undo, {}); + assert.equal(model.getLineContent(1), 'A first and line'); + assertCursor(cursor, new Selection(1, 12, 1, 12)); + + cursorCommand(cursor, H.Undo, {}); + assert.equal(model.getLineContent(1), 'A first line'); + assertCursor(cursor, new Selection(1, 8, 1, 8)); + + cursorCommand(cursor, H.Undo, {}); + assert.equal(model.getLineContent(1), 'A line'); + assertCursor(cursor, new Selection(1, 3, 1, 3)); + }); + }); + +}); diff --git a/src/vs/editor/test/common/mocks/mockCodeEditor.ts b/src/vs/editor/test/common/mocks/mockCodeEditor.ts index afb99c73774..33621c3d209 100644 --- a/src/vs/editor/test/common/mocks/mockCodeEditor.ts +++ b/src/vs/editor/test/common/mocks/mockCodeEditor.ts @@ -18,6 +18,9 @@ import * as editorOptions from 'vs/editor/common/config/editorOptions'; import { IDisposable } from 'vs/base/common/lifecycle'; export class MockCodeEditor extends CommonCodeEditor { + + public _isFocused = true; + protected _createConfiguration(options: editorOptions.IEditorOptions): CommonEditorConfiguration { return new TestConfiguration(options); } @@ -25,7 +28,7 @@ export class MockCodeEditor extends CommonCodeEditor { public layout(dimension?: editorCommon.IDimension): void { } public focus(): void { } - public isFocused(): boolean { return true; } + public isFocused(): boolean { return this._isFocused; } public hasWidgetFocus(): boolean { return true; }; protected _enableEmptySelectionClipboard(): boolean { return false; } diff --git a/src/vs/editor/test/common/model/editableTextModel.test.ts b/src/vs/editor/test/common/model/editableTextModel.test.ts index 6166f3eb8e5..d04e5269cad 100644 --- a/src/vs/editor/test/common/model/editableTextModel.test.ts +++ b/src/vs/editor/test/common/model/editableTextModel.test.ts @@ -7,7 +7,7 @@ import * as assert from 'assert'; import { Range } from 'vs/editor/common/core/range'; -import { EndOfLinePreference, EndOfLineSequence, IIdentifiedSingleEditOperation } from 'vs/editor/common/editorCommon'; +import { EndOfLineSequence, IIdentifiedSingleEditOperation } from 'vs/editor/common/editorCommon'; import { EditableTextModel, IValidatedEditOperation } from 'vs/editor/common/model/editableTextModel'; import { MirrorModel } from 'vs/editor/common/model/mirrorModel'; import { assertSyncedModels, testApplyEditsWithSyncedModels } from 'vs/editor/test/common/model/editableTextModelTestUtils'; @@ -15,12 +15,13 @@ import { IModelContentChangedEvent } from 'vs/editor/common/model/textModelEvent suite('EditorModel - EditableTextModel._getInverseEdits', () => { - function editOp(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, rangeLength: number, text: string[]): IValidatedEditOperation { + function editOp(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, text: string[]): IValidatedEditOperation { return { sortIndex: 0, identifier: null, range: new Range(startLineNumber, startColumn, endLineNumber, endColumn), - rangeLength: rangeLength, + rangeOffset: 0, + rangeLength: 0, lines: text, forceMoveMarkers: false, isAutoWhitespaceEdit: false @@ -39,7 +40,7 @@ suite('EditorModel - EditableTextModel._getInverseEdits', () => { test('single insert', () => { assertInverseEdits( [ - editOp(1, 1, 1, 1, 0, ['hello']) + editOp(1, 1, 1, 1, ['hello']) ], [ inverseEditOp(1, 1, 1, 6) @@ -50,8 +51,8 @@ suite('EditorModel - EditableTextModel._getInverseEdits', () => { test('Bug 19872: Undo is funky', () => { assertInverseEdits( [ - editOp(2, 1, 2, 2, 0, ['']), - editOp(3, 1, 4, 2, 0, ['']) + editOp(2, 1, 2, 2, ['']), + editOp(3, 1, 4, 2, ['']) ], [ inverseEditOp(2, 1, 2, 1), @@ -63,8 +64,8 @@ suite('EditorModel - EditableTextModel._getInverseEdits', () => { test('two single unrelated inserts', () => { assertInverseEdits( [ - editOp(1, 1, 1, 1, 0, ['hello']), - editOp(2, 1, 2, 1, 0, ['world']) + editOp(1, 1, 1, 1, ['hello']), + editOp(2, 1, 2, 1, ['world']) ], [ inverseEditOp(1, 1, 1, 6), @@ -76,8 +77,8 @@ suite('EditorModel - EditableTextModel._getInverseEdits', () => { test('two single inserts 1', () => { assertInverseEdits( [ - editOp(1, 1, 1, 1, 0, ['hello']), - editOp(1, 2, 1, 2, 0, ['world']) + editOp(1, 1, 1, 1, ['hello']), + editOp(1, 2, 1, 2, ['world']) ], [ inverseEditOp(1, 1, 1, 6), @@ -89,8 +90,8 @@ suite('EditorModel - EditableTextModel._getInverseEdits', () => { test('two single inserts 2', () => { assertInverseEdits( [ - editOp(1, 1, 1, 1, 0, ['hello']), - editOp(1, 4, 1, 4, 0, ['world']) + editOp(1, 1, 1, 1, ['hello']), + editOp(1, 4, 1, 4, ['world']) ], [ inverseEditOp(1, 1, 1, 6), @@ -102,7 +103,7 @@ suite('EditorModel - EditableTextModel._getInverseEdits', () => { test('multiline insert', () => { assertInverseEdits( [ - editOp(1, 1, 1, 1, 0, ['hello', 'world']) + editOp(1, 1, 1, 1, ['hello', 'world']) ], [ inverseEditOp(1, 1, 2, 6) @@ -113,8 +114,8 @@ suite('EditorModel - EditableTextModel._getInverseEdits', () => { test('two unrelated multiline inserts', () => { assertInverseEdits( [ - editOp(1, 1, 1, 1, 0, ['hello', 'world']), - editOp(2, 1, 2, 1, 0, ['how', 'are', 'you?']), + editOp(1, 1, 1, 1, ['hello', 'world']), + editOp(2, 1, 2, 1, ['how', 'are', 'you?']), ], [ inverseEditOp(1, 1, 2, 6), @@ -126,8 +127,8 @@ suite('EditorModel - EditableTextModel._getInverseEdits', () => { test('two multiline inserts 1', () => { assertInverseEdits( [ - editOp(1, 1, 1, 1, 0, ['hello', 'world']), - editOp(1, 2, 1, 2, 0, ['how', 'are', 'you?']), + editOp(1, 1, 1, 1, ['hello', 'world']), + editOp(1, 2, 1, 2, ['how', 'are', 'you?']), ], [ inverseEditOp(1, 1, 2, 6), @@ -139,7 +140,7 @@ suite('EditorModel - EditableTextModel._getInverseEdits', () => { test('single delete', () => { assertInverseEdits( [ - editOp(1, 1, 1, 6, 0, null) + editOp(1, 1, 1, 6, null) ], [ inverseEditOp(1, 1, 1, 1) @@ -150,8 +151,8 @@ suite('EditorModel - EditableTextModel._getInverseEdits', () => { test('two single unrelated deletes', () => { assertInverseEdits( [ - editOp(1, 1, 1, 6, 0, null), - editOp(2, 1, 2, 6, 0, null) + editOp(1, 1, 1, 6, null), + editOp(2, 1, 2, 6, null) ], [ inverseEditOp(1, 1, 1, 1), @@ -163,8 +164,8 @@ suite('EditorModel - EditableTextModel._getInverseEdits', () => { test('two single deletes 1', () => { assertInverseEdits( [ - editOp(1, 1, 1, 6, 0, null), - editOp(1, 7, 1, 12, 0, null) + editOp(1, 1, 1, 6, null), + editOp(1, 7, 1, 12, null) ], [ inverseEditOp(1, 1, 1, 1), @@ -176,8 +177,8 @@ suite('EditorModel - EditableTextModel._getInverseEdits', () => { test('two single deletes 2', () => { assertInverseEdits( [ - editOp(1, 1, 1, 6, 0, null), - editOp(1, 9, 1, 14, 0, null) + editOp(1, 1, 1, 6, null), + editOp(1, 9, 1, 14, null) ], [ inverseEditOp(1, 1, 1, 1), @@ -189,7 +190,7 @@ suite('EditorModel - EditableTextModel._getInverseEdits', () => { test('multiline delete', () => { assertInverseEdits( [ - editOp(1, 1, 2, 6, 0, null) + editOp(1, 1, 2, 6, null) ], [ inverseEditOp(1, 1, 1, 1) @@ -200,8 +201,8 @@ suite('EditorModel - EditableTextModel._getInverseEdits', () => { test('two unrelated multiline deletes', () => { assertInverseEdits( [ - editOp(1, 1, 2, 6, 0, null), - editOp(3, 1, 5, 5, 0, null), + editOp(1, 1, 2, 6, null), + editOp(3, 1, 5, 5, null), ], [ inverseEditOp(1, 1, 1, 1), @@ -213,8 +214,8 @@ suite('EditorModel - EditableTextModel._getInverseEdits', () => { test('two multiline deletes 1', () => { assertInverseEdits( [ - editOp(1, 1, 2, 6, 0, null), - editOp(2, 7, 4, 5, 0, null), + editOp(1, 1, 2, 6, null), + editOp(2, 7, 4, 5, null), ], [ inverseEditOp(1, 1, 1, 1), @@ -226,7 +227,7 @@ suite('EditorModel - EditableTextModel._getInverseEdits', () => { test('single replace', () => { assertInverseEdits( [ - editOp(1, 1, 1, 6, 0, ['Hello world']) + editOp(1, 1, 1, 6, ['Hello world']) ], [ inverseEditOp(1, 1, 1, 12) @@ -237,8 +238,8 @@ suite('EditorModel - EditableTextModel._getInverseEdits', () => { test('two replaces', () => { assertInverseEdits( [ - editOp(1, 1, 1, 6, 0, ['Hello world']), - editOp(1, 7, 1, 8, 0, ['How are you?']), + editOp(1, 1, 1, 6, ['Hello world']), + editOp(1, 7, 1, 8, ['How are you?']), ], [ inverseEditOp(1, 1, 1, 12), @@ -250,9 +251,9 @@ suite('EditorModel - EditableTextModel._getInverseEdits', () => { test('many edits', () => { assertInverseEdits( [ - editOp(1, 2, 1, 2, 0, ['', ' ']), - editOp(1, 5, 1, 6, 0, ['']), - editOp(1, 9, 1, 9, 0, ['', '']) + editOp(1, 2, 1, 2, ['', ' ']), + editOp(1, 5, 1, 6, ['']), + editOp(1, 9, 1, 9, ['', '']) ], [ inverseEditOp(1, 2, 2, 3), @@ -265,11 +266,12 @@ suite('EditorModel - EditableTextModel._getInverseEdits', () => { suite('EditorModel - EditableTextModel._toSingleEditOperation', () => { - function editOp(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, rangeLength: number, text: string[]): IValidatedEditOperation { + function editOp(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, rangeOffset: number, rangeLength: number, text: string[]): IValidatedEditOperation { return { sortIndex: 0, identifier: null, range: new Range(startLineNumber, startColumn, endLineNumber, endColumn), + rangeOffset: rangeOffset, rangeLength: rangeLength, lines: text, forceMoveMarkers: false, @@ -297,9 +299,9 @@ suite('EditorModel - EditableTextModel._toSingleEditOperation', () => { '1' ], [ - editOp(1, 3, 1, 3, 0, [' new line', 'No longer']) + editOp(1, 3, 1, 3, 2, 0, [' new line', 'No longer']) ], - editOp(1, 3, 1, 3, 0, [' new line', 'No longer']) + editOp(1, 3, 1, 3, 2, 0, [' new line', 'No longer']) ); }); @@ -311,11 +313,11 @@ suite('EditorModel - EditableTextModel._toSingleEditOperation', () => { '', '1' ], [ - editOp(1, 1, 1, 3, 0, ['Your']), - editOp(1, 4, 1, 4, 0, ['Interesting ']), - editOp(2, 3, 2, 6, 0, null) + editOp(1, 1, 1, 3, 0, 2, ['Your']), + editOp(1, 4, 1, 4, 3, 0, ['Interesting ']), + editOp(2, 3, 2, 6, 16, 3, null) ], - editOp(1, 1, 2, 6, 19, [ + editOp(1, 1, 2, 6, 0, 19, [ 'Your Interesting First Line', '\t\t' ])); @@ -331,10 +333,10 @@ suite('EditorModel - EditableTextModel._toSingleEditOperation', () => { '1' ], [ - editOp(1, 3, 1, 3, 0, ['', '', '', '', '']), - editOp(3, 15, 3, 15, 0, ['a', 'b']) + editOp(1, 3, 1, 3, 2, 0, ['', '', '', '', '']), + editOp(3, 15, 3, 15, 45, 0, ['a', 'b']) ], - editOp(1, 3, 3, 15, 43, [ + editOp(1, 3, 3, 15, 2, 43, [ '', '', '', @@ -357,9 +359,9 @@ suite('EditorModel - EditableTextModel._toSingleEditOperation', () => { '1' ], [ - editOp(1, 1, 1, 1, 0, ['']) + editOp(1, 1, 1, 1, 0, 0, ['']) ], - editOp(1, 1, 1, 1, 0, ['']) + editOp(1, 1, 1, 1, 0, 0, ['']) ); }); @@ -373,10 +375,10 @@ suite('EditorModel - EditableTextModel._toSingleEditOperation', () => { '123' ], [ - editOp(2, 1, 2, 3, 0, ['\t']), - editOp(3, 1, 3, 5, 0, ['']) + editOp(2, 1, 2, 3, 14, 2, ['\t']), + editOp(3, 1, 3, 5, 31, 4, ['']) ], - editOp(2, 1, 3, 5, 21, ['\tMy Second Line', '']) + editOp(2, 1, 3, 5, 14, 21, ['\tMy Second Line', '']) ); }); @@ -386,11 +388,11 @@ suite('EditorModel - EditableTextModel._toSingleEditOperation', () => { '{"x" : 1}' ], [ - editOp(1, 2, 1, 2, 0, ['\n ']), - editOp(1, 5, 1, 6, 0, ['']), - editOp(1, 9, 1, 9, 0, ['\n']) + editOp(1, 2, 1, 2, 1, 0, ['\n ']), + editOp(1, 5, 1, 6, 4, 1, ['']), + editOp(1, 9, 1, 9, 8, 0, ['\n']) ], - editOp(1, 2, 1, 9, 7, [ + editOp(1, 2, 1, 9, 1, 7, [ '', ' "x": 1', '' @@ -406,11 +408,11 @@ suite('EditorModel - EditableTextModel._toSingleEditOperation', () => { '}' ], [ - editOp(1, 2, 2, 3, 0, ['']), - editOp(2, 6, 2, 6, 0, [' ']), - editOp(2, 9, 3, 1, 0, ['']) + editOp(1, 2, 2, 3, 1, 3, ['']), + editOp(2, 6, 2, 6, 7, 0, [' ']), + editOp(2, 9, 3, 1, 10, 1, ['']) ], - editOp(1, 2, 3, 1, 10, ['"x" : 1']) + editOp(1, 2, 3, 1, 1, 10, ['"x" : 1']) ); }); @@ -424,10 +426,10 @@ suite('EditorModel - EditableTextModel._toSingleEditOperation', () => { '}' ], [ - editOp(1, 2, 2, 1, 0, ['', '\t']), - editOp(2, 11, 4, 1, 0, ['', '\t']) + editOp(1, 2, 2, 1, 1, 1, ['', '\t']), + editOp(2, 11, 4, 1, 12, 2, ['', '\t']) ], - editOp(1, 2, 4, 1, 13, [ + editOp(1, 2, 4, 1, 1, 13, [ '', '\t"a": true,', '\t' @@ -446,12 +448,12 @@ suite('EditorModel - EditableTextModel._toSingleEditOperation', () => { 'and the last line' ], [ - editOp(1, 5, 3, 1, 0, [' text', 'some more text', 'some more text']), - editOp(3, 2, 4, 1, 0, ['o more lines', 'asd', 'asd', 'asd']), - editOp(5, 1, 5, 6, 0, ['zzzzzzzz']), - editOp(5, 11, 6, 16, 0, ['1', '2', '3', '4']) + editOp(1, 5, 3, 1, 4, 21, [' text', 'some more text', 'some more text']), + editOp(3, 2, 4, 1, 26, 23, ['o more lines', 'asd', 'asd', 'asd']), + editOp(5, 1, 5, 6, 50, 5, ['zzzzzzzz']), + editOp(5, 11, 6, 16, 60, 22, ['1', '2', '3', '4']) ], - editOp(1, 5, 6, 16, 78, [ + editOp(1, 5, 6, 16, 4, 78, [ ' text', 'some more text', 'some more textno more lines', @@ -475,17 +477,17 @@ suite('EditorModel - EditableTextModel._toSingleEditOperation', () => { ' ,"e": /*comment*/ [null] }', ], [ - editOp(1, 1, 1, 2, 0, ['']), - editOp(1, 3, 1, 10, 0, ['', ' ']), - editOp(1, 16, 2, 14, 0, ['', ' ']), - editOp(2, 18, 3, 9, 0, ['', ' ']), - editOp(3, 22, 4, 9, 0, ['']), - editOp(4, 10, 4, 10, 0, ['', ' ']), - editOp(4, 28, 4, 28, 0, ['', ' ']), - editOp(4, 32, 4, 32, 0, ['', ' ']), - editOp(4, 33, 4, 34, 0, ['', '']) + editOp(1, 1, 1, 2, 0, 1, ['']), + editOp(1, 3, 1, 10, 2, 7, ['', ' ']), + editOp(1, 16, 2, 14, 15, 14, ['', ' ']), + editOp(2, 18, 3, 9, 33, 9, ['', ' ']), + editOp(3, 22, 4, 9, 55, 9, ['']), + editOp(4, 10, 4, 10, 65, 0, ['', ' ']), + editOp(4, 28, 4, 28, 83, 0, ['', ' ']), + editOp(4, 32, 4, 32, 87, 0, ['', ' ']), + editOp(4, 33, 4, 34, 88, 1, ['', '']) ], - editOp(1, 1, 4, 34, 89, [ + editOp(1, 1, 4, 34, 0, 89, [ '{', ' "d": [', ' null', @@ -505,11 +507,11 @@ suite('EditorModel - EditableTextModel._toSingleEditOperation', () => { ' ,def' ], [ - editOp(1, 1, 1, 4, 0, ['']), - editOp(1, 7, 2, 2, 0, ['']), - editOp(2, 3, 2, 3, 0, ['', '']) + editOp(1, 1, 1, 4, 0, 3, ['']), + editOp(1, 7, 2, 2, 6, 2, ['']), + editOp(2, 3, 2, 3, 9, 0, ['', '']) ], - editOp(1, 1, 2, 3, 9, [ + editOp(1, 1, 2, 3, 0, 9, [ 'abc,', '' ]) @@ -1567,7 +1569,6 @@ suite('EditorModel - EditableTextModel.applyEdits', () => { }); let assertMirrorModels = () => { - model._assertLineNumbersOK(); assert.equal(mirrorModel2.getText(), model.getValue(), 'mirror model 2 text OK'); assert.equal(mirrorModel2.version, model.getVersionId(), 'mirror model 2 version OK'); }; @@ -1579,257 +1580,3 @@ suite('EditorModel - EditableTextModel.applyEdits', () => { mirrorModel2.dispose(); }); }); - -interface ILightWeightMarker { - id: string; - lineNumber: number; - column: number; - stickToPreviousCharacter: boolean; -} - -suite('EditorModel - EditableTextModel.applyEdits & markers', () => { - - function editOp(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, text: string[]): IIdentifiedSingleEditOperation { - return { - identifier: null, - range: new Range(startLineNumber, startColumn, endLineNumber, endColumn), - text: text.join('\n'), - forceMoveMarkers: false - }; - } - - function marker(id: string, lineNumber: number, column: number, stickToPreviousCharacter: boolean): ILightWeightMarker { - return { - id: id, - lineNumber: lineNumber, - column: column, - stickToPreviousCharacter: stickToPreviousCharacter - }; - } - - function toMarkersMap(markers: ILightWeightMarker[]): { [markerId: string]: ILightWeightMarker } { - var result: { [markerId: string]: ILightWeightMarker } = {}; - markers.forEach(m => { - result[m.id] = m; - }); - return result; - } - - function testApplyEditsAndMarkers(text: string[], markers: ILightWeightMarker[], edits: IIdentifiedSingleEditOperation[], changedMarkers: string[], expectedText: string[], expectedMarkers: ILightWeightMarker[]): void { - var textStr = text.join('\n'); - var expectedTextStr = expectedText.join('\n'); - var markersMap = toMarkersMap(markers); - // var expectedMarkersMap = toMarkersMap(expectedMarkers); - var markerId2ModelMarkerId = Object.create(null); - - var model = EditableTextModel.createFromString(textStr); - model.setEOL(EndOfLineSequence.LF); - - // Add markers - markers.forEach((m) => { - let modelMarkerId = model._addMarker(0, m.lineNumber, m.column, m.stickToPreviousCharacter); - markerId2ModelMarkerId[m.id] = modelMarkerId; - }); - - // Apply edits & collect inverse edits - model.applyEdits(edits); - model._assertLineNumbersOK(); - - // Assert edits produced expected result - assert.deepEqual(model.getValue(EndOfLinePreference.LF), expectedTextStr); - - let actualChangedMarkers: string[] = []; - for (let i = 0, len = expectedMarkers.length; i < len; i++) { - let expectedMarker = expectedMarkers[i]; - let initialMarker = markersMap[expectedMarker.id]; - let expectedMarkerModelMarkerId = markerId2ModelMarkerId[expectedMarker.id]; - let actualMarker = model._getMarker(expectedMarkerModelMarkerId); - - if (actualMarker.lineNumber !== initialMarker.lineNumber || actualMarker.column !== initialMarker.column) { - actualChangedMarkers.push(initialMarker.id); - } - - assert.equal(actualMarker.lineNumber, expectedMarker.lineNumber, 'marker lineNumber of marker ' + expectedMarker.id); - assert.equal(actualMarker.column, expectedMarker.column, 'marker column of marker ' + expectedMarker.id); - } - - changedMarkers.sort(); - actualChangedMarkers.sort(); - assert.deepEqual(actualChangedMarkers, changedMarkers, 'changed markers'); - - model.dispose(); - } - - test('no markers changed', () => { - testApplyEditsAndMarkers( - [ - 'Hello world,', - 'this is a short text', - 'that is used in testing' - ], - [ - marker('a', 1, 1, true), - marker('b', 1, 1, false), - marker('c', 1, 7, false), - marker('d', 1, 12, true), - marker('e', 2, 1, false), - marker('f', 2, 16, true), - marker('g', 2, 21, true), - marker('h', 3, 24, false) - ], - [ - editOp(1, 13, 1, 13, [' how are you?']) - ], - [], - [ - 'Hello world, how are you?', - 'this is a short text', - 'that is used in testing' - ], - [ - marker('a', 1, 1, true), - marker('b', 1, 1, false), - marker('c', 1, 7, false), - marker('d', 1, 12, true), - marker('e', 2, 1, false), - marker('f', 2, 16, true), - marker('g', 2, 21, true), - marker('h', 3, 24, false) - ] - ); - }); - - test('first line changes', () => { - testApplyEditsAndMarkers( - [ - 'Hello world,', - 'this is a short text', - 'that is used in testing' - ], - [ - marker('a', 1, 1, true), - marker('b', 1, 1, false), - marker('c', 1, 7, false), - marker('d', 1, 12, true), - marker('e', 2, 1, false), - marker('f', 2, 16, true), - marker('g', 2, 21, true), - marker('h', 3, 24, false) - ], - [ - editOp(1, 7, 1, 12, ['friends']) - ], - [], - [ - 'Hello friends,', - 'this is a short text', - 'that is used in testing' - ], - [ - marker('a', 1, 1, true), - marker('b', 1, 1, false), - marker('c', 1, 7, false), - marker('d', 1, 12, true), - marker('e', 2, 1, false), - marker('f', 2, 16, true), - marker('g', 2, 21, true), - marker('h', 3, 24, false) - ] - ); - }); - - test('inserting lines', () => { - testApplyEditsAndMarkers( - [ - 'Hello world,', - 'this is a short text', - 'that is used in testing' - ], - [ - marker('a', 1, 1, true), - marker('b', 1, 1, false), - marker('c', 1, 7, false), - marker('d', 1, 12, true), - marker('e', 2, 1, false), - marker('f', 2, 16, true), - marker('g', 2, 21, true), - marker('h', 3, 24, false) - ], - [ - editOp(1, 7, 1, 12, ['friends']), - editOp(1, 13, 1, 13, ['', 'this is an inserted line', 'and another one. By the way,']) - ], - ['e', 'f', 'g', 'h'], - [ - 'Hello friends,', - 'this is an inserted line', - 'and another one. By the way,', - 'this is a short text', - 'that is used in testing' - ], - [ - marker('a', 1, 1, true), - marker('b', 1, 1, false), - marker('c', 1, 7, false), - marker('d', 1, 12, true), - marker('e', 4, 1, false), - marker('f', 4, 16, true), - marker('g', 4, 21, true), - marker('h', 5, 24, false) - ] - ); - }); - - test('replacing a lot', () => { - testApplyEditsAndMarkers( - [ - 'Hello world,', - 'this is a short text', - 'that is used in testing', - 'more lines...', - 'more lines...', - 'more lines...', - 'more lines...' - ], - [ - marker('a', 1, 1, true), - marker('b', 1, 1, false), - marker('c', 1, 7, false), - marker('d', 1, 12, true), - marker('e', 2, 1, false), - marker('f', 2, 16, true), - marker('g', 2, 21, true), - marker('h', 3, 24, false), - marker('i', 5, 1, false), - marker('j', 6, 1, false), - marker('k', 7, 14, false), - ], - [ - editOp(1, 7, 1, 12, ['friends']), - editOp(1, 13, 1, 13, ['', 'this is an inserted line', 'and another one. By the way,', 'This is another line']), - editOp(2, 1, 7, 14, ['Some new text here']) - ], - ['e', 'f', 'g', 'h', 'i', 'j', 'k'], - [ - 'Hello friends,', - 'this is an inserted line', - 'and another one. By the way,', - 'This is another line', - 'Some new text here' - ], - [ - marker('a', 1, 1, true), - marker('b', 1, 1, false), - marker('c', 1, 7, false), - marker('d', 1, 12, true), - marker('e', 5, 1, false), - marker('f', 5, 16, true), - marker('g', 5, 19, true), - marker('h', 5, 19, false), - marker('i', 5, 19, false), - marker('j', 5, 19, false), - marker('k', 5, 19, false), - ] - ); - }); -}); diff --git a/src/vs/editor/test/common/model/editableTextModelTestUtils.ts b/src/vs/editor/test/common/model/editableTextModelTestUtils.ts index a901d29fcd3..b0b18723062 100644 --- a/src/vs/editor/test/common/model/editableTextModelTestUtils.ts +++ b/src/vs/editor/test/common/model/editableTextModelTestUtils.ts @@ -105,7 +105,6 @@ export function assertSyncedModels(text: string, callback: (model: EditableTextM var assertMirrorModels = () => { assertLineMapping(model, 'model'); - model._assertLineNumbersOK(); assert.equal(mirrorModel2.getText(), model.getValue(), 'mirror model 2 text OK'); assert.equal(mirrorModel2.version, model.getVersionId(), 'mirror model 2 version OK'); }; diff --git a/src/vs/editor/test/common/model/intervalTree.test.ts b/src/vs/editor/test/common/model/intervalTree.test.ts new file mode 100644 index 00000000000..29a5978432e --- /dev/null +++ b/src/vs/editor/test/common/model/intervalTree.test.ts @@ -0,0 +1,555 @@ +/*--------------------------------------------------------------------------------------------- + * 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 assert from 'assert'; +import { IntervalTree, IntervalNode } from 'vs/editor/common/model/intervalTree'; + +const GENERATE_TESTS = false; +let TEST_COUNT = GENERATE_TESTS ? 10000 : 0; +let PRINT_TREE = false; +const MIN_INTERVAL_START = 1; +const MAX_INTERVAL_END = 100; +const MIN_INSERTS = 1; +const MAX_INSERTS = 30; +const MIN_CHANGE_CNT = 10; +const MAX_CHANGE_CNT = 20; + +suite('IntervalTree', () => { + + class Interval { + _intervalBrand: void; + + public start: number; + public end: number; + + constructor(start: number, end: number) { + this.start = start; + this.end = end; + } + } + + class Oracle { + public intervals: Interval[]; + + constructor() { + this.intervals = []; + } + + public insert(interval: Interval): Interval { + this.intervals.push(interval); + this.intervals.sort((a, b) => { + if (a.start === b.start) { + return a.end - b.end; + } + return a.start - b.start; + }); + return interval; + } + + public delete(interval: Interval): void { + for (let i = 0, len = this.intervals.length; i < len; i++) { + if (this.intervals[i] === interval) { + this.intervals.splice(i, 1); + return; + } + } + } + + public search(interval: Interval): Interval[] { + let result: Interval[] = []; + for (let i = 0, len = this.intervals.length; i < len; i++) { + let int = this.intervals[i]; + if (int.start <= interval.end && int.end >= interval.start) { + result.push(int); + } + } + return result; + } + } + + class TestState { + private _oracle: Oracle = new Oracle(); + private _tree: IntervalTree = new IntervalTree(); + private _lastNodeId = -1; + private _treeNodes: IntervalNode[] = []; + private _oracleNodes: Interval[] = []; + + public acceptOp(op: IOperation): void { + + if (op.type === 'insert') { + if (PRINT_TREE) { + console.log(`insert: {${JSON.stringify(new Interval(op.begin, op.end))}}`); + } + let nodeId = (++this._lastNodeId); + this._treeNodes[nodeId] = new IntervalNode(null, op.begin, op.end); + this._tree.insert(this._treeNodes[nodeId]); + this._oracleNodes[nodeId] = this._oracle.insert(new Interval(op.begin, op.end)); + } else if (op.type === 'delete') { + if (PRINT_TREE) { + console.log(`delete: {${JSON.stringify(this._oracleNodes[op.id])}}`); + } + this._tree.delete(this._treeNodes[op.id]); + this._oracle.delete(this._oracleNodes[op.id]); + + this._treeNodes[op.id] = null; + this._oracleNodes[op.id] = null; + } else if (op.type === 'change') { + + this._tree.delete(this._treeNodes[op.id]); + this._treeNodes[op.id].reset(0, op.begin, op.end, null); + this._tree.insert(this._treeNodes[op.id]); + + this._oracle.delete(this._oracleNodes[op.id]); + this._oracleNodes[op.id].start = op.begin; + this._oracleNodes[op.id].end = op.end; + this._oracle.insert(this._oracleNodes[op.id]); + + } else { + let actualNodes = this._tree.intervalSearch(op.begin, op.end, 0, false, 0); + let actual = actualNodes.map(n => new Interval(n.cachedAbsoluteStart, n.cachedAbsoluteEnd)); + let expected = this._oracle.search(new Interval(op.begin, op.end)); + assert.deepEqual(actual, expected); + return; + } + + if (PRINT_TREE) { + this._tree.print(); + } + + this._tree.assertInvariants(); + + let actual = this._tree.getAllInOrder().map(n => new Interval(n.cachedAbsoluteStart, n.cachedAbsoluteEnd)); + let expected = this._oracle.intervals; + assert.deepEqual(actual, expected); + } + + public getExistingNodeId(index: number): number { + let currIndex = -1; + for (let i = 0; i < this._treeNodes.length; i++) { + if (this._treeNodes[i] === null) { + continue; + } + currIndex++; + if (currIndex === index) { + return i; + } + } + throw new Error('unexpected'); + } + } + + interface IInsertOperation { + type: 'insert'; + begin: number; + end: number; + } + + interface IDeleteOperation { + type: 'delete'; + id: number; + } + + interface IChangeOperation { + type: 'change'; + id: number; + begin: number; + end: number; + } + + interface ISearchOperation { + type: 'search'; + begin: number; + end: number; + } + + type IOperation = IInsertOperation | IDeleteOperation | IChangeOperation | ISearchOperation; + + function testIntervalTree(ops: IOperation[]): void { + let state = new TestState(); + for (let i = 0; i < ops.length; i++) { + state.acceptOp(ops[i]); + } + } + + function getRandomInt(min: number, max: number): number { + return Math.floor(Math.random() * (max - min + 1)) + min; + } + + function getRandomRange(min: number, max: number): [number, number] { + let begin = getRandomInt(min, max); + let length: number; + if (getRandomInt(1, 10) <= 2) { + // large range + length = getRandomInt(0, max - begin); + } else { + // small range + length = getRandomInt(0, Math.min(max - begin, 10)); + } + return [begin, begin + length]; + } + + class AutoTest { + private _ops: IOperation[] = []; + private _state: TestState = new TestState(); + private _insertCnt: number; + private _deleteCnt: number; + private _changeCnt: number; + + constructor() { + this._insertCnt = getRandomInt(MIN_INSERTS, MAX_INSERTS); + this._changeCnt = getRandomInt(MIN_CHANGE_CNT, MAX_CHANGE_CNT); + this._deleteCnt = 0; + } + + private _doRandomInsert(): void { + let range = getRandomRange(MIN_INTERVAL_START, MAX_INTERVAL_END); + this._run({ + type: 'insert', + begin: range[0], + end: range[1] + }); + } + + private _doRandomDelete(): void { + let idx = getRandomInt(Math.floor(this._deleteCnt / 2), this._deleteCnt - 1); + this._run({ + type: 'delete', + id: this._state.getExistingNodeId(idx) + }); + } + + private _doRandomChange(): void { + let idx = getRandomInt(0, this._deleteCnt - 1); + let range = getRandomRange(MIN_INTERVAL_START, MAX_INTERVAL_END); + this._run({ + type: 'change', + id: this._state.getExistingNodeId(idx), + begin: range[0], + end: range[1] + }); + } + + public run() { + while (this._insertCnt > 0 || this._deleteCnt > 0 || this._changeCnt > 0) { + if (this._insertCnt > 0) { + this._doRandomInsert(); + this._insertCnt--; + this._deleteCnt++; + } else if (this._changeCnt > 0) { + this._doRandomChange(); + this._changeCnt--; + } else { + this._doRandomDelete(); + this._deleteCnt--; + } + + // Let's also search for something... + let searchRange = getRandomRange(MIN_INTERVAL_START, MAX_INTERVAL_END); + this._run({ + type: 'search', + begin: searchRange[0], + end: searchRange[1] + }); + } + } + + private _run(op: IOperation): void { + this._ops.push(op); + this._state.acceptOp(op); + } + + public print(): void { + console.log(`testIntervalTree(${JSON.stringify(this._ops)})`); + } + + } + + suite('generated', () => { + test('gen01', () => { + testIntervalTree([ + { type: 'insert', begin: 28, end: 35 }, + { type: 'insert', begin: 52, end: 54 }, + { type: 'insert', begin: 63, end: 69 } + ]); + }); + + test('gen02', () => { + testIntervalTree([ + { type: 'insert', begin: 80, end: 89 }, + { type: 'insert', begin: 92, end: 100 }, + { type: 'insert', begin: 99, end: 99 } + ]); + }); + + test('gen03', () => { + testIntervalTree([ + { type: 'insert', begin: 89, end: 96 }, + { type: 'insert', begin: 71, end: 74 }, + { type: 'delete', id: 1 } + ]); + }); + + test('gen04', () => { + testIntervalTree([ + { type: 'insert', begin: 44, end: 46 }, + { type: 'insert', begin: 85, end: 88 }, + { type: 'delete', id: 0 } + ]); + }); + + test('gen05', () => { + testIntervalTree([ + { type: 'insert', begin: 82, end: 90 }, + { type: 'insert', begin: 69, end: 73 }, + { type: 'delete', id: 0 }, + { type: 'delete', id: 1 } + ]); + }); + + test('gen06', () => { + testIntervalTree([ + { type: 'insert', begin: 41, end: 63 }, + { type: 'insert', begin: 98, end: 98 }, + { type: 'insert', begin: 47, end: 51 }, + { type: 'delete', id: 2 } + ]); + }); + + test('gen07', () => { + testIntervalTree([ + { type: 'insert', begin: 24, end: 26 }, + { type: 'insert', begin: 11, end: 28 }, + { type: 'insert', begin: 27, end: 30 }, + { type: 'insert', begin: 80, end: 85 }, + { type: 'delete', id: 1 } + ]); + }); + + test('gen08', () => { + testIntervalTree([ + { type: 'insert', begin: 100, end: 100 }, + { type: 'insert', begin: 100, end: 100 } + ]); + }); + + test('gen09', () => { + testIntervalTree([ + { type: 'insert', begin: 58, end: 65 }, + { type: 'insert', begin: 82, end: 96 }, + { type: 'insert', begin: 58, end: 65 } + ]); + }); + + test('gen10', () => { + testIntervalTree([ + { type: 'insert', begin: 32, end: 40 }, + { type: 'insert', begin: 25, end: 29 }, + { type: 'insert', begin: 24, end: 32 } + ]); + }); + + test('gen11', () => { + testIntervalTree([ + { type: 'insert', begin: 25, end: 70 }, + { type: 'insert', begin: 99, end: 100 }, + { type: 'insert', begin: 46, end: 51 }, + { type: 'insert', begin: 57, end: 57 }, + { type: 'delete', id: 2 } + ]); + }); + + test('gen12', () => { + testIntervalTree([ + { type: 'insert', begin: 20, end: 26 }, + { type: 'insert', begin: 10, end: 18 }, + { type: 'insert', begin: 99, end: 99 }, + { type: 'insert', begin: 37, end: 59 }, + { type: 'delete', id: 2 } + ]); + }); + + test('gen13', () => { + testIntervalTree([ + { type: 'insert', begin: 3, end: 91 }, + { type: 'insert', begin: 57, end: 57 }, + { type: 'insert', begin: 35, end: 44 }, + { type: 'insert', begin: 72, end: 81 }, + { type: 'delete', id: 2 } + ]); + }); + + test('gen14', () => { + testIntervalTree([ + { type: 'insert', begin: 58, end: 61 }, + { type: 'insert', begin: 34, end: 35 }, + { type: 'insert', begin: 56, end: 62 }, + { type: 'insert', begin: 69, end: 78 }, + { type: 'delete', id: 0 } + ]); + }); + + test('gen15', () => { + testIntervalTree([ + { type: 'insert', begin: 63, end: 69 }, + { type: 'insert', begin: 17, end: 24 }, + { type: 'insert', begin: 3, end: 13 }, + { type: 'insert', begin: 84, end: 94 }, + { type: 'insert', begin: 18, end: 23 }, + { type: 'insert', begin: 96, end: 98 }, + { type: 'delete', id: 1 } + ]); + }); + + test('gen16', () => { + testIntervalTree([ + { type: 'insert', begin: 27, end: 27 }, + { type: 'insert', begin: 42, end: 87 }, + { type: 'insert', begin: 42, end: 49 }, + { type: 'insert', begin: 69, end: 71 }, + { type: 'insert', begin: 20, end: 27 }, + { type: 'insert', begin: 8, end: 9 }, + { type: 'insert', begin: 42, end: 49 }, + { type: 'delete', id: 1 } + ]); + }); + + test('gen17', () => { + testIntervalTree([ + { type: 'insert', begin: 21, end: 23 }, + { type: 'insert', begin: 83, end: 87 }, + { type: 'insert', begin: 56, end: 58 }, + { type: 'insert', begin: 1, end: 55 }, + { type: 'insert', begin: 56, end: 59 }, + { type: 'insert', begin: 58, end: 60 }, + { type: 'insert', begin: 56, end: 65 }, + { type: 'delete', id: 1 }, + { type: 'delete', id: 0 }, + { type: 'delete', id: 6 } + ]); + }); + + test('gen18', () => { + testIntervalTree([ + { type: 'insert', begin: 25, end: 25 }, + { type: 'insert', begin: 67, end: 79 }, + { type: 'delete', id: 0 }, + { type: 'search', begin: 65, end: 75 } + ]); + }); + + test('force delta overflow', () => { + // Search the IntervalNode ctor for FORCE_OVERFLOWING_TEST + // to force that this test leads to a delta normalization + testIntervalTree([ + { type: 'insert', begin: 686081138593427, end: 733009856502260 }, + { type: 'insert', begin: 591031326181669, end: 591031326181672 }, + { type: 'insert', begin: 940037682731896, end: 940037682731903 }, + { type: 'insert', begin: 598413641151120, end: 598413641151128 }, + { type: 'insert', begin: 800564156553344, end: 800564156553351 }, + { type: 'insert', begin: 894198957565481, end: 894198957565491 } + ]); + }); + }); + + // TEST_COUNT = 0; + // PRINT_TREE = true; + + for (let i = 0; i < TEST_COUNT; i++) { + if (i % 100 === 0) { + console.log(`TEST ${i + 1}/${TEST_COUNT}`); + } + let test = new AutoTest(); + + try { + test.run(); + } catch (err) { + console.log(err); + test.print(); + return; + } + } + + suite('searching', () => { + + function createCormenTree(): IntervalTree { + let r = new IntervalTree(); + let data: [number, number][] = [ + [16, 21], + [8, 9], + [25, 30], + [5, 8], + [15, 23], + [17, 19], + [26, 26], + [0, 3], + [6, 10], + [19, 20] + ]; + data.forEach((int) => { + let node = new IntervalNode(null, int[0], int[1]); + r.insert(node); + }); + return r; + } + + const T = createCormenTree(); + + function assertIntervalSearch(start: number, end: number, expected: [number, number][]): void { + let actualNodes = T.intervalSearch(start, end, 0, false, 0); + let actual = actualNodes.map((n) => <[number, number]>[n.cachedAbsoluteStart, n.cachedAbsoluteEnd]); + assert.deepEqual(actual, expected); + } + + test('cormen 1->2', () => { + assertIntervalSearch( + 1, 2, + [ + [0, 3], + ] + ); + }); + + test('cormen 4->8', () => { + assertIntervalSearch( + 4, 8, + [ + [5, 8], + [6, 10], + [8, 9], + ] + ); + }); + + test('cormen 10->15', () => { + assertIntervalSearch( + 10, 15, + [ + [6, 10], + [15, 23], + ] + ); + }); + + test('cormen 21->25', () => { + assertIntervalSearch( + 21, 25, + [ + [15, 23], + [16, 21], + [25, 30], + ] + ); + }); + + test('cormen 24->24', () => { + assertIntervalSearch( + 24, 24, + [ + ] + ); + }); + }); +}); diff --git a/src/vs/editor/test/common/model/model.line.test.ts b/src/vs/editor/test/common/model/model.line.test.ts index 81dbba468d5..a2e75ba4ae9 100644 --- a/src/vs/editor/test/common/model/model.line.test.ts +++ b/src/vs/editor/test/common/model/model.line.test.ts @@ -6,9 +6,8 @@ import * as assert from 'assert'; import { LineTokens } from 'vs/editor/common/core/lineTokens'; -import { ModelLine, ILineEdit, LineMarker, MarkersTracker } from 'vs/editor/common/model/modelLine'; +import { ModelLine, ILineEdit } from 'vs/editor/common/model/modelLine'; import { MetadataConsts } from 'vs/editor/common/modes'; -import { Position } from 'vs/editor/common/core/position'; import { ViewLineToken, ViewLineTokenFactory } from 'vs/editor/common/core/viewLineToken'; function assertLineTokens(_actual: LineTokens, _expected: TestToken[]): void { @@ -54,7 +53,7 @@ suite('Editor Model - modelLine.applyEdits text', () => { function testEdits(initial: string, edits: ILineEdit[], expected: string): void { var line = new ModelLine(initial, NO_TAB_SIZE); - line.applyEdits(new MarkersTracker(), edits, NO_TAB_SIZE); + line.applyEdits(edits, NO_TAB_SIZE); assert.equal(line.text, expected); } @@ -62,8 +61,7 @@ suite('Editor Model - modelLine.applyEdits text', () => { return { startColumn: startColumn, endColumn: endColumn, - text: text, - forceMoveMarkers: false + text: text }; } @@ -201,7 +199,7 @@ suite('Editor Model - modelLine.split text', () => { function testLineSplit(initial: string, splitColumn: number, expected1: string, expected2: string): void { var line = new ModelLine(initial, NO_TAB_SIZE); - var newLine = line.split(new MarkersTracker(), splitColumn, false, NO_TAB_SIZE); + var newLine = line.split(splitColumn, NO_TAB_SIZE); assert.equal(line.text, expected1); assert.equal(newLine.text, expected2); } @@ -239,7 +237,7 @@ suite('Editor Model - modelLine.append text', () => { function testLineAppend(a: string, b: string, expected: string): void { var line1 = new ModelLine(a, NO_TAB_SIZE); var line2 = new ModelLine(b, NO_TAB_SIZE); - line1.append(new MarkersTracker(), 1, line2, NO_TAB_SIZE); + line1.append(line2, NO_TAB_SIZE); assert.equal(line1.text, expected); } @@ -301,7 +299,7 @@ suite('Editor Model - modelLine.applyEdits text & tokens', () => { let line = new ModelLine(initialText, NO_TAB_SIZE); line.setTokens(0, TestToken.toTokens(initialTokens)); - line.applyEdits(new MarkersTracker(), edits, NO_TAB_SIZE); + line.applyEdits(edits, NO_TAB_SIZE); assert.equal(line.text, expectedText); assertLineTokens(line.getTokens(0), expectedTokens); @@ -311,10 +309,10 @@ suite('Editor Model - modelLine.applyEdits text & tokens', () => { let line = new ModelLine('some text', NO_TAB_SIZE); line.setTokens(0, TestToken.toTokens([new TestToken(0, 1)])); - line.applyEdits(new MarkersTracker(), [{ startColumn: 1, endColumn: 10, text: '', forceMoveMarkers: false }], NO_TAB_SIZE); + line.applyEdits([{ startColumn: 1, endColumn: 10, text: '' }], NO_TAB_SIZE); line.setTokens(0, new Uint32Array(0)); - line.applyEdits(new MarkersTracker(), [{ startColumn: 1, endColumn: 1, text: 'a', forceMoveMarkers: false }], NO_TAB_SIZE); + line.applyEdits([{ startColumn: 1, endColumn: 1, text: 'a' }], NO_TAB_SIZE); assertLineTokens(line.getTokens(0), [new TestToken(0, 1)]); }); @@ -330,7 +328,6 @@ suite('Editor Model - modelLine.applyEdits text & tokens', () => { startColumn: 1, endColumn: 1, text: 'a', - forceMoveMarkers: false }], 'aabcd efgh', [ @@ -353,7 +350,6 @@ suite('Editor Model - modelLine.applyEdits text & tokens', () => { startColumn: 2, endColumn: 2, text: 'x', - forceMoveMarkers: false }], 'axabcd efgh', [ @@ -376,7 +372,6 @@ suite('Editor Model - modelLine.applyEdits text & tokens', () => { startColumn: 3, endColumn: 3, text: 'stu', - forceMoveMarkers: false }], 'axstuabcd efgh', [ @@ -399,7 +394,6 @@ suite('Editor Model - modelLine.applyEdits text & tokens', () => { startColumn: 10, endColumn: 10, text: '\t', - forceMoveMarkers: false }], 'axstuabcd\t efgh', [ @@ -422,7 +416,6 @@ suite('Editor Model - modelLine.applyEdits text & tokens', () => { startColumn: 12, endColumn: 12, text: 'dd', - forceMoveMarkers: false }], 'axstuabcd\t ddefgh', [ @@ -445,7 +438,6 @@ suite('Editor Model - modelLine.applyEdits text & tokens', () => { startColumn: 18, endColumn: 18, text: 'xyz', - forceMoveMarkers: false }], 'axstuabcd\t ddefghxyz', [ @@ -468,7 +460,6 @@ suite('Editor Model - modelLine.applyEdits text & tokens', () => { startColumn: 1, endColumn: 1, text: 'x', - forceMoveMarkers: false }], 'xaxstuabcd\t ddefghxyz', [ @@ -491,7 +482,6 @@ suite('Editor Model - modelLine.applyEdits text & tokens', () => { startColumn: 22, endColumn: 22, text: 'x', - forceMoveMarkers: false }], 'xaxstuabcd\t ddefghxyzx', [ @@ -514,7 +504,6 @@ suite('Editor Model - modelLine.applyEdits text & tokens', () => { startColumn: 2, endColumn: 2, text: '', - forceMoveMarkers: false }], 'xaxstuabcd\t ddefghxyzx', [ @@ -533,7 +522,6 @@ suite('Editor Model - modelLine.applyEdits text & tokens', () => { startColumn: 1, endColumn: 1, text: 'a', - forceMoveMarkers: false }], 'a', [ @@ -554,7 +542,6 @@ suite('Editor Model - modelLine.applyEdits text & tokens', () => { startColumn: 4, endColumn: 7, text: '', - forceMoveMarkers: false }], 'abcghij', [ @@ -576,7 +563,6 @@ suite('Editor Model - modelLine.applyEdits text & tokens', () => { startColumn: 4, endColumn: 4, text: 'hello', - forceMoveMarkers: false }], 'abchellodefghij', [ @@ -599,7 +585,6 @@ suite('Editor Model - modelLine.applyEdits text & tokens', () => { startColumn: 1, endColumn: 2, text: '', - forceMoveMarkers: false }], 'bcd efgh', [ @@ -622,7 +607,6 @@ suite('Editor Model - modelLine.applyEdits text & tokens', () => { startColumn: 2, endColumn: 4, text: '', - forceMoveMarkers: false }], 'ad efgh', [ @@ -645,7 +629,6 @@ suite('Editor Model - modelLine.applyEdits text & tokens', () => { startColumn: 1, endColumn: 5, text: '', - forceMoveMarkers: false }], ' efgh', [ @@ -667,7 +650,6 @@ suite('Editor Model - modelLine.applyEdits text & tokens', () => { startColumn: 5, endColumn: 6, text: '', - forceMoveMarkers: false }], 'abcdefgh', [ @@ -689,7 +671,6 @@ suite('Editor Model - modelLine.applyEdits text & tokens', () => { startColumn: 5, endColumn: 7, text: '', - forceMoveMarkers: false }], 'abcdfgh', [ @@ -711,7 +692,6 @@ suite('Editor Model - modelLine.applyEdits text & tokens', () => { startColumn: 5, endColumn: 10, text: '', - forceMoveMarkers: false }], 'abcd', [ @@ -732,7 +712,6 @@ suite('Editor Model - modelLine.applyEdits text & tokens', () => { startColumn: 1, endColumn: 10, text: '', - forceMoveMarkers: false }], '', [ @@ -753,7 +732,6 @@ suite('Editor Model - modelLine.applyEdits text & tokens', () => { startColumn: 1, endColumn: 1, text: '', - forceMoveMarkers: false }], 'abcd efgh', [ @@ -776,7 +754,6 @@ suite('Editor Model - modelLine.applyEdits text & tokens', () => { startColumn: 1, endColumn: 3, text: '', - forceMoveMarkers: false }], 'cd efgh', [ @@ -799,7 +776,6 @@ suite('Editor Model - modelLine.applyEdits text & tokens', () => { startColumn: 5, endColumn: 10, text: '', - forceMoveMarkers: false }], 'abcd', [ @@ -822,7 +798,6 @@ suite('Editor Model - modelLine.applyEdits text & tokens', () => { startColumn: 1, endColumn: 6, text: 'Hi', - forceMoveMarkers: false }], 'Hi world, ciao', [ @@ -849,12 +824,10 @@ suite('Editor Model - modelLine.applyEdits text & tokens', () => { startColumn: 1, endColumn: 6, text: 'Hi', - forceMoveMarkers: false }, { startColumn: 8, endColumn: 12, text: 'my friends', - forceMoveMarkers: false }], 'Hi wmy friends, ciao', [ @@ -873,7 +846,7 @@ suite('Editor Model - modelLine.split text & tokens', () => { let line = new ModelLine(initialText, NO_TAB_SIZE); line.setTokens(0, TestToken.toTokens(initialTokens)); - let other = line.split(new MarkersTracker(), splitColumn, false, NO_TAB_SIZE); + let other = line.split(splitColumn, NO_TAB_SIZE); assert.equal(line.text, expectedText1); assert.equal(other.text, expectedText2); @@ -960,7 +933,7 @@ suite('Editor Model - modelLine.append text & tokens', () => { let b = new ModelLine(bText, NO_TAB_SIZE); b.setTokens(0, TestToken.toTokens(bTokens)); - a.append(new MarkersTracker(), 1, b, NO_TAB_SIZE); + a.append(b, NO_TAB_SIZE); assert.equal(a.text, expectedText); assertLineTokens(a.getTokens(0), expectedTokens); @@ -1071,1260 +1044,483 @@ suite('Editor Model - modelLine.append text & tokens', () => { }); }); -interface ILightWeightMarker { - id: string; - lineNumber: number; - column: number; - stickToPreviousCharacter: boolean; -} +suite('Editor Model - modelLine.applyEdits', () => { -suite('Editor Model - modelLine.applyEdits text & markers', () => { - - function marker(id: number, column: number, stickToPreviousCharacter: boolean): LineMarker { - return new LineMarker(String(id), id, new Position(0, column), stickToPreviousCharacter); - } - - function toLightWeightMarker(marker: LineMarker): ILightWeightMarker { - return { - id: marker.id, - lineNumber: marker.position.lineNumber, - column: marker.position.column, - stickToPreviousCharacter: marker.stickToPreviousCharacter - }; - } - - function testLineEditMarkers(initialText: string, initialMarkers: LineMarker[], edits: ILineEdit[], expectedText: string, expectedChangedMarkers: number[], _expectedMarkers: LineMarker[]): void { + function testLineEdit(initialText: string, edits: ILineEdit[], expectedText: string): void { let line = new ModelLine(initialText, NO_TAB_SIZE); - line.addMarkers(initialMarkers); - let changedMarkers = new MarkersTracker(); - line.applyEdits(changedMarkers, edits, NO_TAB_SIZE); + line.applyEdits(edits, NO_TAB_SIZE); assert.equal(line.text, expectedText, 'text'); - - let actualMarkers = line.getMarkers().map(toLightWeightMarker); - let expectedMarkers = _expectedMarkers.map(toLightWeightMarker); - assert.deepEqual(actualMarkers, expectedMarkers, 'markers'); - - let actualChangedMarkers = changedMarkers.getDecorationIds(); - actualChangedMarkers.sort(); - assert.deepEqual(actualChangedMarkers, expectedChangedMarkers, 'changed markers'); } test('insertion: updates markers 1', () => { - testLineEditMarkers( + testLineEdit( 'abcd efgh', - [ - marker(1, 1, true), - marker(2, 1, false), - marker(3, 2, true), - marker(4, 2, false), - marker(5, 5, true), - marker(6, 5, false), - marker(7, 10, true), - marker(8, 10, false) - ], [{ startColumn: 1, endColumn: 1, text: 'abc', - forceMoveMarkers: false }], 'abcabcd efgh', - [2, 3, 4, 5, 6, 7, 8], - [ - marker(1, 1, true), - marker(2, 4, false), - marker(3, 5, true), - marker(4, 5, false), - marker(5, 8, true), - marker(6, 8, false), - marker(7, 13, true), - marker(8, 13, false) - ] ); }); test('insertion: updates markers 2', () => { - testLineEditMarkers( + testLineEdit( 'abcd efgh', - [ - marker(1, 1, true), - marker(2, 1, false), - marker(3, 2, true), - marker(4, 2, false), - marker(5, 5, true), - marker(6, 5, false), - marker(7, 10, true), - marker(8, 10, false) - ], [{ startColumn: 2, endColumn: 2, text: 'abc', - forceMoveMarkers: false }], 'aabcbcd efgh', - [4, 5, 6, 7, 8], - [ - marker(1, 1, true), - marker(2, 1, false), - marker(3, 2, true), - marker(4, 5, false), - marker(5, 8, true), - marker(6, 8, false), - marker(7, 13, true), - marker(8, 13, false) - ] ); }); test('insertion: updates markers 3', () => { - testLineEditMarkers( + testLineEdit( 'abcd efgh', - [ - marker(1, 1, true), - marker(2, 1, false), - marker(3, 2, true), - marker(4, 2, false), - marker(5, 5, true), - marker(6, 5, false), - marker(7, 10, true), - marker(8, 10, false) - ], [{ startColumn: 3, endColumn: 3, text: 'abc', - forceMoveMarkers: false }], 'ababccd efgh', - [5, 6, 7, 8], - [ - marker(1, 1, true), - marker(2, 1, false), - marker(3, 2, true), - marker(4, 2, false), - marker(5, 8, true), - marker(6, 8, false), - marker(7, 13, true), - marker(8, 13, false) - ] ); }); test('insertion: updates markers 4', () => { - testLineEditMarkers( + testLineEdit( 'abcd efgh', - [ - marker(1, 1, true), - marker(2, 1, false), - marker(3, 2, true), - marker(4, 2, false), - marker(5, 5, true), - marker(6, 5, false), - marker(7, 10, true), - marker(8, 10, false) - ], [{ startColumn: 5, endColumn: 5, text: 'abc', - forceMoveMarkers: false }], 'abcdabc efgh', - [6, 7, 8], - [ - marker(1, 1, true), - marker(2, 1, false), - marker(3, 2, true), - marker(4, 2, false), - marker(5, 5, true), - marker(6, 8, false), - marker(7, 13, true), - marker(8, 13, false) - ] ); }); test('insertion: updates markers 5', () => { - testLineEditMarkers( + testLineEdit( 'abcd efgh', - [ - marker(1, 1, true), - marker(2, 1, false), - marker(3, 2, true), - marker(4, 2, false), - marker(5, 5, true), - marker(6, 5, false), - marker(7, 10, true), - marker(8, 10, false) - ], [{ startColumn: 10, endColumn: 10, text: 'abc', - forceMoveMarkers: false }], 'abcd efghabc', - [8], - [ - marker(1, 1, true), - marker(2, 1, false), - marker(3, 2, true), - marker(4, 2, false), - marker(5, 5, true), - marker(6, 5, false), - marker(7, 10, true), - marker(8, 13, false) - ] ); }); test('insertion bis: updates markers 1', () => { - testLineEditMarkers( + testLineEdit( 'abcd efgh', - [ - marker(1, 1, true), - marker(2, 1, false), - marker(3, 2, true), - marker(4, 2, false), - marker(5, 5, true), - marker(6, 5, false), - marker(7, 10, true), - marker(8, 10, false) - ], [{ startColumn: 1, endColumn: 1, text: 'a', - forceMoveMarkers: false }], 'aabcd efgh', - [2, 3, 4, 5, 6, 7, 8], - [ - marker(1, 1, true), - marker(2, 2, false), - marker(3, 3, true), - marker(4, 3, false), - marker(5, 6, true), - marker(6, 6, false), - marker(7, 11, true), - marker(8, 11, false) - ] ); }); test('insertion bis: updates markers 2', () => { - testLineEditMarkers( + testLineEdit( 'abcd efgh', - [ - marker(1, 1, true), - marker(2, 1, false), - marker(3, 2, true), - marker(4, 2, false), - marker(5, 5, true), - marker(6, 5, false), - marker(7, 10, true), - marker(8, 10, false) - ], [{ startColumn: 2, endColumn: 2, text: 'a', - forceMoveMarkers: false }], 'aabcd efgh', - [4, 5, 6, 7, 8], - [ - marker(1, 1, true), - marker(2, 1, false), - marker(3, 2, true), - marker(4, 3, false), - marker(5, 6, true), - marker(6, 6, false), - marker(7, 11, true), - marker(8, 11, false) - ] ); }); test('insertion bis: updates markers 3', () => { - testLineEditMarkers( + testLineEdit( 'abcd efgh', - [ - marker(1, 1, true), - marker(2, 1, false), - marker(3, 2, true), - marker(4, 2, false), - marker(5, 5, true), - marker(6, 5, false), - marker(7, 10, true), - marker(8, 10, false) - ], [{ startColumn: 3, endColumn: 3, text: 'a', - forceMoveMarkers: false }], 'abacd efgh', - [5, 6, 7, 8], - [ - marker(1, 1, true), - marker(2, 1, false), - marker(3, 2, true), - marker(4, 2, false), - marker(5, 6, true), - marker(6, 6, false), - marker(7, 11, true), - marker(8, 11, false) - ] ); }); test('insertion bis: updates markers 4', () => { - testLineEditMarkers( + testLineEdit( 'abcd efgh', - [ - marker(1, 1, true), - marker(2, 1, false), - marker(3, 2, true), - marker(4, 2, false), - marker(5, 5, true), - marker(6, 5, false), - marker(7, 10, true), - marker(8, 10, false) - ], [{ startColumn: 5, endColumn: 5, text: 'a', - forceMoveMarkers: false }], 'abcda efgh', - [6, 7, 8], - [ - marker(1, 1, true), - marker(2, 1, false), - marker(3, 2, true), - marker(4, 2, false), - marker(5, 5, true), - marker(6, 6, false), - marker(7, 11, true), - marker(8, 11, false) - ] ); }); test('insertion bis: updates markers 5', () => { - testLineEditMarkers( + testLineEdit( 'abcd efgh', - [ - marker(1, 1, true), - marker(2, 1, false), - marker(3, 2, true), - marker(4, 2, false), - marker(5, 5, true), - marker(6, 5, false), - marker(7, 10, true), - marker(8, 10, false) - ], [{ startColumn: 10, endColumn: 10, text: 'a', - forceMoveMarkers: false }], 'abcd efgha', - [8], - [ - marker(1, 1, true), - marker(2, 1, false), - marker(3, 2, true), - marker(4, 2, false), - marker(5, 5, true), - marker(6, 5, false), - marker(7, 10, true), - marker(8, 11, false) - ] ); }); test('insertion: does not move marker at column 1', () => { - testLineEditMarkers( + testLineEdit( 'abcd efgh', - [marker(1, 1, true)], [{ startColumn: 1, endColumn: 1, text: 'a', - forceMoveMarkers: false }], 'aabcd efgh', - [], - [marker(1, 1, true)] ); }); test('insertion: does move marker at column 1', () => { - testLineEditMarkers( + testLineEdit( 'abcd efgh', - [marker(1, 1, false)], [{ startColumn: 1, endColumn: 1, text: 'a', - forceMoveMarkers: false }], 'aabcd efgh', - [1], - [marker(1, 2, false)] ); }); test('insertion: two markers at column 1', () => { - testLineEditMarkers( + testLineEdit( 'abcd efgh', - [ - marker(1, 1, true), - marker(2, 1, false), - ], [{ startColumn: 1, endColumn: 1, text: 'a', - forceMoveMarkers: false }], 'aabcd efgh', - [2], - [ - marker(1, 1, true), - marker(2, 2, false) - ] ); }); test('insertion: two markers at column 1 unsorted', () => { - testLineEditMarkers( + testLineEdit( 'abcd efgh', - [ - marker(2, 1, false), - marker(1, 1, true), - ], [{ startColumn: 1, endColumn: 1, text: 'a', - forceMoveMarkers: false }], 'aabcd efgh', - [2], - [ - marker(1, 1, true), - marker(2, 2, false) - ] ); }); test('deletion: updates markers 1', () => { - testLineEditMarkers( + testLineEdit( 'abcd efgh', - [ - marker(1, 1, true), - marker(2, 1, false), - marker(3, 2, true), - marker(4, 2, false), - marker(5, 5, true), - marker(6, 5, false), - marker(7, 10, true), - marker(8, 10, false) - ], [{ startColumn: 1, endColumn: 2, text: '', - forceMoveMarkers: false }], 'bcd efgh', - [3, 4, 5, 6, 7, 8], - [ - marker(1, 1, true), - marker(2, 1, false), - marker(3, 1, true), - marker(4, 1, false), - marker(5, 4, true), - marker(6, 4, false), - marker(7, 9, true), - marker(8, 9, false) - ] ); }); test('deletion: updates markers 2', () => { - testLineEditMarkers( + testLineEdit( 'abcd efgh', - [ - marker(1, 1, true), - marker(2, 1, false), - marker(3, 2, true), - marker(4, 2, false), - marker(5, 5, true), - marker(6, 5, false), - marker(7, 10, true), - marker(8, 10, false) - ], [{ startColumn: 1, endColumn: 4, text: '', - forceMoveMarkers: false }], 'd efgh', - [3, 4, 5, 6, 7, 8], - [ - marker(1, 1, true), - marker(2, 1, false), - marker(3, 1, true), - marker(4, 1, false), - marker(5, 2, true), - marker(6, 2, false), - marker(7, 7, true), - marker(8, 7, false) - ] ); }); test('deletion: updates markers 3', () => { - testLineEditMarkers( + testLineEdit( 'abcd efgh', - [ - marker(1, 1, true), - marker(2, 1, false), - marker(3, 2, true), - marker(4, 2, false), - marker(5, 5, true), - marker(6, 5, false), - marker(7, 10, true), - marker(8, 10, false) - ], [{ startColumn: 5, endColumn: 6, text: '', - forceMoveMarkers: false }], 'abcdefgh', - [7, 8], - [ - marker(1, 1, true), - marker(2, 1, false), - marker(3, 2, true), - marker(4, 2, false), - marker(5, 5, true), - marker(6, 5, false), - marker(7, 9, true), - marker(8, 9, false) - ] ); }); test('replace: updates markers 1', () => { - testLineEditMarkers( + testLineEdit( 'abcd efgh', - [ - marker(1, 1, true), - marker(2, 1, false), - marker(3, 2, true), - marker(4, 2, false), - marker(5, 5, true), - marker(6, 5, false), - marker(7, 10, true), - marker(8, 10, false) - ], [{ startColumn: 1, endColumn: 1, text: 'a', - forceMoveMarkers: false }, { startColumn: 2, endColumn: 3, text: '', - forceMoveMarkers: false }], 'aacd efgh', - [2, 3, 4], - [ - marker(1, 1, true), - marker(2, 2, false), - marker(3, 3, true), - marker(4, 3, false), - marker(5, 5, true), - marker(6, 5, false), - marker(7, 10, true), - marker(8, 10, false) - ] ); }); test('delete near markers', () => { - testLineEditMarkers( + testLineEdit( 'abcd', - [ - marker(1, 3, true), - marker(2, 3, false) - ], [{ startColumn: 3, endColumn: 4, text: '', - forceMoveMarkers: false }], 'abd', - [], - [ - marker(1, 3, true), - marker(2, 3, false) - ] ); }); test('replace: updates markers 2', () => { - testLineEditMarkers( + testLineEdit( 'Hello world, how are you', - [ - marker(1, 1, false), - marker(2, 6, true), - marker(3, 14, false), - marker(4, 21, true) - ], [{ startColumn: 1, endColumn: 1, text: ' - ', - forceMoveMarkers: false }, { startColumn: 6, endColumn: 12, text: '', - forceMoveMarkers: false }, { startColumn: 22, endColumn: 25, text: 'things', - forceMoveMarkers: false }], ' - Hello, how are things', - [1, 2, 3, 4], - [ - marker(1, 4, false), - marker(2, 9, true), - marker(3, 11, false), - marker(4, 18, true) - ] ); }); test('sorts markers', () => { - testLineEditMarkers( + testLineEdit( 'Hello world, how are you', - [ - marker(4, 21, true), - marker(2, 6, true), - marker(1, 1, false), - marker(3, 14, false) - ], [{ startColumn: 1, endColumn: 1, text: ' - ', - forceMoveMarkers: false }, { startColumn: 6, endColumn: 12, text: '', - forceMoveMarkers: false }, { startColumn: 22, endColumn: 25, text: 'things', - forceMoveMarkers: false }], ' - Hello, how are things', - [1, 2, 3, 4], - [ - marker(1, 4, false), - marker(2, 9, true), - marker(3, 11, false), - marker(4, 18, true) - ] ); }); test('change text inside markers', () => { - testLineEditMarkers( + testLineEdit( 'abcd efgh', - [ - marker(1, 1, true), - marker(2, 1, false), - marker(3, 6, false), - marker(4, 10, true) - ], [{ startColumn: 6, endColumn: 10, text: '1234567', - forceMoveMarkers: false }], 'abcd 1234567', - [], - [ - marker(1, 1, true), - marker(2, 1, false), - marker(3, 6, false), - marker(4, 10, true) - ] ); }); test('inserting is different than replacing for markers part 1', () => { - testLineEditMarkers( + testLineEdit( 'abcd', - [ - marker(1, 2, false) - ], [{ startColumn: 2, endColumn: 2, text: 'INSERT', - forceMoveMarkers: false }], 'aINSERTbcd', - [1], - [ - marker(1, 8, false) - ] ); }); test('inserting is different than replacing for markers part 2', () => { - testLineEditMarkers( + testLineEdit( 'abcd', - [ - marker(1, 2, false) - ], [{ startColumn: 2, endColumn: 3, text: 'REPLACED', - forceMoveMarkers: false }], 'aREPLACEDcd', - [], - [ - marker(1, 2, false) - ] ); }); test('replacing the entire line with more text', () => { - testLineEditMarkers( + testLineEdit( 'this is a short text', - [ - marker(1, 1, false), - marker(2, 16, true), - ], [{ startColumn: 1, endColumn: 21, text: 'Some new text here', - forceMoveMarkers: false }], 'Some new text here', - [], - [ - marker(1, 1, false), - marker(2, 16, true), - ] ); }); test('replacing the entire line with less text', () => { - testLineEditMarkers( + testLineEdit( 'this is a short text', - [ - marker(1, 1, false), - marker(2, 16, true), - ], [{ startColumn: 1, endColumn: 21, text: 'ttt', - forceMoveMarkers: false }], 'ttt', - [2], - [ - marker(1, 1, false), - marker(2, 4, true), - ] ); }); test('replace selection', () => { - testLineEditMarkers( + testLineEdit( 'first', - [ - marker(1, 1, true), - marker(2, 6, false), - ], [{ startColumn: 1, endColumn: 6, text: 'something', - forceMoveMarkers: false }], 'something', - [2], - [ - marker(1, 1, true), - marker(2, 10, false), - ] ); }); }); -suite('Editor Model - modelLine.split text & markers', () => { +suite('Editor Model - modelLine.split', () => { - function marker(id: number, column: number, stickToPreviousCharacter: boolean): LineMarker { - return new LineMarker(String(id), id, new Position(0, column), stickToPreviousCharacter); - } - - function toLightWeightMarker(marker: LineMarker): ILightWeightMarker { - return { - id: marker.id, - lineNumber: marker.position.lineNumber, - column: marker.position.column, - stickToPreviousCharacter: marker.stickToPreviousCharacter - }; - } - - function testLineSplitMarkers(initialText: string, initialMarkers: LineMarker[], splitColumn: number, forceMoveMarkers: boolean, expectedText1: string, expectedText2: string, expectedChangedMarkers: number[], _expectedMarkers1: LineMarker[], _expectedMarkers2: LineMarker[]): void { + function testLineSplit(initialText: string, splitColumn: number, forceMoveMarkers: boolean, expectedText1: string, expectedText2: string): void { let line = new ModelLine(initialText, NO_TAB_SIZE); - line.addMarkers(initialMarkers); - let changedMarkers = new MarkersTracker(); - let otherLine = line.split(changedMarkers, splitColumn, forceMoveMarkers, NO_TAB_SIZE); + let otherLine = line.split(splitColumn, NO_TAB_SIZE); assert.equal(line.text, expectedText1, 'text'); assert.equal(otherLine.text, expectedText2, 'text'); - - let actualMarkers1 = line.getMarkers().map(toLightWeightMarker); - let expectedMarkers1 = _expectedMarkers1.map(toLightWeightMarker); - assert.deepEqual(actualMarkers1, expectedMarkers1, 'markers'); - - let actualMarkers2 = otherLine.getMarkers().map(toLightWeightMarker); - let expectedMarkers2 = _expectedMarkers2.map(toLightWeightMarker); - assert.deepEqual(actualMarkers2, expectedMarkers2, 'markers'); - - let actualChangedMarkers = changedMarkers.getDecorationIds(); - actualChangedMarkers.sort(); - assert.deepEqual(actualChangedMarkers, expectedChangedMarkers, 'changed markers'); } test('split at the beginning', () => { - testLineSplitMarkers( + testLineSplit( 'abcd efgh', - [ - marker(1, 1, true), - marker(2, 1, false), - marker(3, 2, true), - marker(4, 2, false), - marker(5, 5, true), - marker(6, 5, false), - marker(7, 10, true), - marker(8, 10, false) - ], 1, false, '', 'abcd efgh', - [], - [ - marker(1, 1, true) - ], - [ - marker(2, 1, false), - marker(3, 2, true), - marker(4, 2, false), - marker(5, 5, true), - marker(6, 5, false), - marker(7, 10, true), - marker(8, 10, false) - ] ); }); test('split at the beginning 2', () => { - testLineSplitMarkers( + testLineSplit( 'abcd efgh', - [ - marker(1, 1, true), - marker(2, 1, false), - marker(3, 2, true), - marker(4, 2, false), - marker(5, 5, true), - marker(6, 5, false), - marker(7, 10, true), - marker(8, 10, false) - ], 1, true, '', 'abcd efgh', - [], - [], - [ - marker(1, 1, true), - marker(2, 1, false), - marker(3, 2, true), - marker(4, 2, false), - marker(5, 5, true), - marker(6, 5, false), - marker(7, 10, true), - marker(8, 10, false) - ] ); }); test('split at the end', () => { - testLineSplitMarkers( + testLineSplit( 'abcd efgh', - [ - marker(1, 1, true), - marker(2, 1, false), - marker(3, 2, true), - marker(4, 2, false), - marker(5, 5, true), - marker(6, 5, false), - marker(7, 10, true), - marker(8, 10, false) - ], 10, false, 'abcd efgh', '', - [8], - [ - marker(1, 1, true), - marker(2, 1, false), - marker(3, 2, true), - marker(4, 2, false), - marker(5, 5, true), - marker(6, 5, false), - marker(7, 10, true), - ], - [ - marker(8, 1, false) - ] ); }); test('split it the middle 1', () => { - testLineSplitMarkers( + testLineSplit( 'abcd efgh', - [ - marker(1, 1, true), - marker(2, 1, false), - marker(3, 2, true), - marker(4, 2, false), - marker(5, 5, true), - marker(6, 5, false), - marker(7, 10, true), - marker(8, 10, false) - ], 2, false, 'a', 'bcd efgh', - [4, 5, 6, 7, 8], - [ - marker(1, 1, true), - marker(2, 1, false), - marker(3, 2, true), - ], - [ - marker(4, 1, false), - marker(5, 4, true), - marker(6, 4, false), - marker(7, 9, true), - marker(8, 9, false) - ] ); }); test('split it the middle 2', () => { - testLineSplitMarkers( + testLineSplit( 'abcd efgh', - [ - marker(1, 1, true), - marker(2, 1, false), - marker(3, 2, true), - marker(4, 2, false), - marker(5, 5, true), - marker(6, 5, false), - marker(7, 10, true), - marker(8, 10, false) - ], 3, false, 'ab', 'cd efgh', - [5, 6, 7, 8], - [ - marker(1, 1, true), - marker(2, 1, false), - marker(3, 2, true), - marker(4, 2, false), - ], - [ - marker(5, 3, true), - marker(6, 3, false), - marker(7, 8, true), - marker(8, 8, false) - ] ); }); test('split it the middle 3', () => { - testLineSplitMarkers( + testLineSplit( 'abcd efgh', - [ - marker(1, 1, true), - marker(2, 1, false), - marker(3, 2, true), - marker(4, 2, false), - marker(5, 5, true), - marker(6, 5, false), - marker(7, 10, true), - marker(8, 10, false) - ], 5, false, 'abcd', ' efgh', - [6, 7, 8], - [ - marker(1, 1, true), - marker(2, 1, false), - marker(3, 2, true), - marker(4, 2, false), - marker(5, 5, true), - ], - [ - marker(6, 1, false), - marker(7, 6, true), - marker(8, 6, false) - ] ); }); test('split it the middle 4', () => { - testLineSplitMarkers( + testLineSplit( 'abcd efgh', - [ - marker(1, 1, true), - marker(2, 1, false), - marker(3, 2, true), - marker(4, 2, false), - marker(5, 5, true), - marker(6, 5, false), - marker(7, 10, true), - marker(8, 10, false) - ], 6, false, 'abcd ', 'efgh', - [7, 8], - [ - marker(1, 1, true), - marker(2, 1, false), - marker(3, 2, true), - marker(4, 2, false), - marker(5, 5, true), - marker(6, 5, false), - ], - [ - marker(7, 5, true), - marker(8, 5, false) - ] ); }); }); -suite('Editor Model - modelLine.append text & markers', () => { +suite('Editor Model - modelLine.append', () => { - function markerOnFirstLine(id: number, column: number, stickToPreviousCharacter: boolean): LineMarker { - return new LineMarker(String(id), id, new Position(1, column), stickToPreviousCharacter); - } - - function markerOnSecondLine(id: number, column: number, stickToPreviousCharacter: boolean): LineMarker { - return new LineMarker(String(id), id, new Position(2, column), stickToPreviousCharacter); - } - - function toLightWeightMarker(marker: LineMarker): ILightWeightMarker { - return { - id: marker.id, - lineNumber: marker.position.lineNumber, - column: marker.position.column, - stickToPreviousCharacter: marker.stickToPreviousCharacter - }; - } - - function testLinePrependMarkers(aText: string, aMarkers: LineMarker[], bText: string, bMarkers: LineMarker[], expectedText: string, expectedChangedMarkers: number[], _expectedMarkers: LineMarker[]): void { + function testLinePrependMarkers(aText: string, bText: string, expectedText: string): void { let a = new ModelLine(aText, NO_TAB_SIZE); - a.addMarkers(aMarkers); - let b = new ModelLine(bText, NO_TAB_SIZE); - b.addMarkers(bMarkers); - let changedMarkers = new MarkersTracker(); - a.append(changedMarkers, 1, b, NO_TAB_SIZE); + a.append(b, NO_TAB_SIZE); assert.equal(a.text, expectedText, 'text'); - - let actualMarkers = a.getMarkers().map(toLightWeightMarker); - let expectedMarkers = _expectedMarkers.map(toLightWeightMarker); - assert.deepEqual(actualMarkers, expectedMarkers, 'markers'); - - let actualChangedMarkers = changedMarkers.getDecorationIds(); - actualChangedMarkers.sort(); - assert.deepEqual(actualChangedMarkers, expectedChangedMarkers, 'changed markers'); } test('append to an empty', () => { testLinePrependMarkers( 'abcd efgh', - [ - markerOnFirstLine(1, 1, true), - markerOnFirstLine(2, 1, false), - markerOnFirstLine(3, 2, true), - markerOnFirstLine(4, 2, false), - markerOnFirstLine(5, 5, true), - markerOnFirstLine(6, 5, false), - markerOnFirstLine(7, 10, true), - markerOnFirstLine(8, 10, false), - ], '', - [ - ], 'abcd efgh', - [], - [ - markerOnFirstLine(1, 1, true), - markerOnFirstLine(2, 1, false), - markerOnFirstLine(3, 2, true), - markerOnFirstLine(4, 2, false), - markerOnFirstLine(5, 5, true), - markerOnFirstLine(6, 5, false), - markerOnFirstLine(7, 10, true), - markerOnFirstLine(8, 10, false) - ] ); }); test('append an empty', () => { testLinePrependMarkers( '', - [ - ], 'abcd efgh', - [ - markerOnSecondLine(1, 1, true), - markerOnSecondLine(2, 1, false), - markerOnSecondLine(3, 2, true), - markerOnSecondLine(4, 2, false), - markerOnSecondLine(5, 5, true), - markerOnSecondLine(6, 5, false), - markerOnSecondLine(7, 10, true), - markerOnSecondLine(8, 10, false), - ], 'abcd efgh', - [1, 2, 3, 4, 5, 6, 7, 8], - [ - markerOnFirstLine(1, 1, true), - markerOnFirstLine(2, 1, false), - markerOnFirstLine(3, 2, true), - markerOnFirstLine(4, 2, false), - markerOnFirstLine(5, 5, true), - markerOnFirstLine(6, 5, false), - markerOnFirstLine(7, 10, true), - markerOnFirstLine(8, 10, false) - ] ); }); test('append 1', () => { testLinePrependMarkers( 'abcd', - [ - markerOnFirstLine(1, 1, true), - markerOnFirstLine(2, 1, false), - markerOnFirstLine(3, 2, true), - markerOnFirstLine(4, 2, false) - ], ' efgh', - [ - markerOnSecondLine(5, 1, true), - markerOnSecondLine(6, 1, false), - markerOnSecondLine(7, 6, true), - markerOnSecondLine(8, 6, false), - ], 'abcd efgh', - [5, 6, 7, 8], - [ - markerOnFirstLine(1, 1, true), - markerOnFirstLine(2, 1, false), - markerOnFirstLine(3, 2, true), - markerOnFirstLine(4, 2, false), - markerOnFirstLine(5, 5, true), - markerOnFirstLine(6, 5, false), - markerOnFirstLine(7, 10, true), - markerOnFirstLine(8, 10, false) - ] ); }); test('append 2', () => { testLinePrependMarkers( 'abcd e', - [ - markerOnFirstLine(1, 1, true), - markerOnFirstLine(2, 1, false), - markerOnFirstLine(3, 2, true), - markerOnFirstLine(4, 2, false), - markerOnFirstLine(5, 5, true), - markerOnFirstLine(6, 5, false) - ], 'fgh', - [ - markerOnSecondLine(7, 4, true), - markerOnSecondLine(8, 4, false), - ], 'abcd efgh', - [7, 8], - [ - markerOnFirstLine(1, 1, true), - markerOnFirstLine(2, 1, false), - markerOnFirstLine(3, 2, true), - markerOnFirstLine(4, 2, false), - markerOnFirstLine(5, 5, true), - markerOnFirstLine(6, 5, false), - markerOnFirstLine(7, 10, true), - markerOnFirstLine(8, 10, false) - ] ); }); }); diff --git a/src/vs/editor/test/common/model/modelDecorations.test.ts b/src/vs/editor/test/common/model/modelDecorations.test.ts index 1b2264090f0..ea402ff32c4 100644 --- a/src/vs/editor/test/common/model/modelDecorations.test.ts +++ b/src/vs/editor/test/common/model/modelDecorations.test.ts @@ -8,7 +8,7 @@ import * as assert from 'assert'; import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; -import { IModelDeltaDecoration, TrackedRangeStickiness } from 'vs/editor/common/editorCommon'; +import { IModelDeltaDecoration, TrackedRangeStickiness, EndOfLineSequence } from 'vs/editor/common/editorCommon'; import { Model } from 'vs/editor/common/model/model'; // --------- utils @@ -27,7 +27,8 @@ function modelHasDecorations(model: Model, decorations: ILightWeightDecoration2[ className: actualDecorations[i].options.className }); } - assert.deepEqual(modelDecorations, decorations, 'Model decorations'); + modelDecorations.sort((a, b) => Range.compareRangesUsingStarts(a.range, b.range)); + assert.deepEqual(modelDecorations, decorations); } function modelHasDecoration(model: Model, startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, className: string) { @@ -168,13 +169,13 @@ suite('Editor Model - Model Decorations', () => { var decId1 = addDecoration(thisModel, 1, 2, 3, 2, 'myType1'); var decId2 = addDecoration(thisModel, 1, 2, 3, 1, 'myType2'); modelHasDecorations(thisModel, [ - { - range: new Range(1, 2, 3, 2), - className: 'myType1' - }, { range: new Range(1, 2, 3, 1), className: 'myType2' + }, + { + range: new Range(1, 2, 3, 2), + className: 'myType1' } ]); thisModel.changeDecorations((changeAccessor) => { @@ -207,9 +208,6 @@ suite('Editor Model - Model Decorations', () => { let listenerCalled = 0; thisModel.onDidChangeDecorations((e) => { listenerCalled++; - assert.equal(e.addedDecorations.length, 1); - assert.equal(e.changedDecorations.length, 0); - assert.equal(e.removedDecorations.length, 0); }); addDecoration(thisModel, 1, 2, 3, 2, 'myType'); assert.equal(listenerCalled, 1, 'listener called'); @@ -220,10 +218,6 @@ suite('Editor Model - Model Decorations', () => { let decId = addDecoration(thisModel, 1, 2, 3, 2, 'myType'); thisModel.onDidChangeDecorations((e) => { listenerCalled++; - assert.equal(e.addedDecorations.length, 0); - assert.equal(e.changedDecorations.length, 1); - assert.equal(e.changedDecorations[0], decId); - assert.equal(e.removedDecorations.length, 0); }); thisModel.changeDecorations((changeAccessor) => { changeAccessor.changeDecoration(decId, new Range(1, 1, 1, 2)); @@ -236,10 +230,6 @@ suite('Editor Model - Model Decorations', () => { let decId = addDecoration(thisModel, 1, 2, 3, 2, 'myType'); thisModel.onDidChangeDecorations((e) => { listenerCalled++; - assert.equal(e.addedDecorations.length, 0); - assert.equal(e.changedDecorations.length, 0); - assert.equal(e.removedDecorations.length, 1); - assert.equal(e.removedDecorations[0], decId); }); thisModel.changeDecorations((changeAccessor) => { changeAccessor.removeDecoration(decId); @@ -249,20 +239,31 @@ suite('Editor Model - Model Decorations', () => { test('decorations emit event when inserting one line text before it', () => { let listenerCalled = 0; - let decId = addDecoration(thisModel, 1, 2, 3, 2, 'myType'); + addDecoration(thisModel, 1, 2, 3, 2, 'myType'); thisModel.onDidChangeDecorations((e) => { listenerCalled++; - assert.equal(e.addedDecorations.length, 0); - assert.equal(e.changedDecorations.length, 1); - assert.equal(e.changedDecorations[0], decId); - assert.equal(e.removedDecorations.length, 0); }); thisModel.applyEdits([EditOperation.insert(new Position(1, 1), 'Hallo ')]); assert.equal(listenerCalled, 1, 'listener called'); }); + test('decorations do not emit event on no-op deltaDecorations', () => { + let listenerCalled = 0; + + thisModel.onDidChangeDecorations((e) => { + listenerCalled++; + }); + + thisModel.deltaDecorations([], []); + thisModel.changeDecorations((accessor) => { + accessor.deltaDecorations([], []); + }); + + assert.equal(listenerCalled, 0, 'listener not called'); + }); + // --------- editing text & effects on decorations test('decorations are updated when inserting one line text before it', () => { @@ -365,6 +366,740 @@ suite('Editor Model - Model Decorations', () => { thisModel.applyEdits([EditOperation.delete(new Range(1, 1, 3, 1))]); modelHasDecoration(thisModel, 1, 1, 2, 1, 'myType'); }); + + test('decorations are updated when changing EOL', () => { + addDecoration(thisModel, 1, 2, 4, 1, 'myType1'); + addDecoration(thisModel, 1, 3, 4, 1, 'myType2'); + addDecoration(thisModel, 1, 4, 4, 1, 'myType3'); + addDecoration(thisModel, 1, 5, 4, 1, 'myType4'); + addDecoration(thisModel, 1, 6, 4, 1, 'myType5'); + addDecoration(thisModel, 1, 7, 4, 1, 'myType6'); + addDecoration(thisModel, 1, 8, 4, 1, 'myType7'); + addDecoration(thisModel, 1, 9, 4, 1, 'myType8'); + addDecoration(thisModel, 1, 10, 4, 1, 'myType9'); + thisModel.applyEdits([EditOperation.insert(new Position(1, 1), 'x')]); + thisModel.setEOL(EndOfLineSequence.CRLF); + thisModel.applyEdits([EditOperation.insert(new Position(1, 1), 'x')]); + modelHasDecorations(thisModel, [ + { range: new Range(1, 4, 4, 1), className: 'myType1' }, + { range: new Range(1, 5, 4, 1), className: 'myType2' }, + { range: new Range(1, 6, 4, 1), className: 'myType3' }, + { range: new Range(1, 7, 4, 1), className: 'myType4' }, + { range: new Range(1, 8, 4, 1), className: 'myType5' }, + { range: new Range(1, 9, 4, 1), className: 'myType6' }, + { range: new Range(1, 10, 4, 1), className: 'myType7' }, + { range: new Range(1, 11, 4, 1), className: 'myType8' }, + { range: new Range(1, 12, 4, 1), className: 'myType9' }, + ]); + }); + + test('an apparently simple edit', () => { + addDecoration(thisModel, 1, 2, 4, 1, 'myType1'); + thisModel.applyEdits([EditOperation.replace(new Range(1, 14, 2, 1), 'x')]); + modelHasDecorations(thisModel, [ + { range: new Range(1, 2, 3, 1), className: 'myType1' }, + ]); + }); + + test('removeAllDecorationsWithOwnerId can be called after model dispose', () => { + let model = Model.createFromString('asd'); + model.dispose(); + model.removeAllDecorationsWithOwnerId(1); + }); + + test('removeAllDecorationsWithOwnerId works', () => { + thisModel.deltaDecorations([], [{ range: new Range(1, 2, 4, 1), options: { className: 'myType1' } }], 1); + thisModel.removeAllDecorationsWithOwnerId(1); + modelHasNoDecorations(thisModel); + }); +}); + +suite('Decorations and editing', () => { + + function _runTest(decRange: Range, stickiness: TrackedRangeStickiness, editRange: Range, editText: string, editForceMoveMarkers: boolean, expectedDecRange: Range, msg: string): void { + let model = Model.createFromString([ + 'My First Line', + 'My Second Line', + 'Third Line' + ].join('\n')); + + const id = model.deltaDecorations([], [{ range: decRange, options: { stickiness: stickiness } }])[0]; + model.applyEdits([{ range: editRange, text: editText, forceMoveMarkers: editForceMoveMarkers, identifier: null }]); + const actual = model.getDecorationRange(id); + assert.deepEqual(actual, expectedDecRange, msg); + + model.dispose(); + } + + function runTest(decRange: Range, editRange: Range, editText: string, expectedDecRange: Range[][]): void { + _runTest(decRange, 0, editRange, editText, false, expectedDecRange[0][0], 'no-0-AlwaysGrowsWhenTypingAtEdges'); + _runTest(decRange, 1, editRange, editText, false, expectedDecRange[0][1], 'no-1-NeverGrowsWhenTypingAtEdges'); + _runTest(decRange, 2, editRange, editText, false, expectedDecRange[0][2], 'no-2-GrowsOnlyWhenTypingBefore'); + _runTest(decRange, 3, editRange, editText, false, expectedDecRange[0][3], 'no-3-GrowsOnlyWhenTypingAfter'); + + _runTest(decRange, 0, editRange, editText, true, expectedDecRange[1][0], 'force-0-AlwaysGrowsWhenTypingAtEdges'); + _runTest(decRange, 1, editRange, editText, true, expectedDecRange[1][1], 'force-1-NeverGrowsWhenTypingAtEdges'); + _runTest(decRange, 2, editRange, editText, true, expectedDecRange[1][2], 'force-2-GrowsOnlyWhenTypingBefore'); + _runTest(decRange, 3, editRange, editText, true, expectedDecRange[1][3], 'force-3-GrowsOnlyWhenTypingAfter'); + } + + suite('insert', () => { + suite('collapsed dec', () => { + test('before', () => { + runTest( + new Range(1, 4, 1, 4), + new Range(1, 3, 1, 3), 'xx', + [ + [new Range(1, 6, 1, 6), new Range(1, 6, 1, 6), new Range(1, 6, 1, 6), new Range(1, 6, 1, 6)], + [new Range(1, 6, 1, 6), new Range(1, 6, 1, 6), new Range(1, 6, 1, 6), new Range(1, 6, 1, 6)], + ] + ); + }); + test('equal', () => { + runTest( + new Range(1, 4, 1, 4), + new Range(1, 4, 1, 4), 'xx', + [ + [new Range(1, 4, 1, 6), new Range(1, 6, 1, 6), new Range(1, 4, 1, 4), new Range(1, 6, 1, 6)], + [new Range(1, 6, 1, 6), new Range(1, 6, 1, 6), new Range(1, 6, 1, 6), new Range(1, 6, 1, 6)], + ] + ); + }); + test('after', () => { + runTest( + new Range(1, 4, 1, 4), + new Range(1, 5, 1, 5), 'xx', + [ + [new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4)], + [new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4)], + ] + ); + }); + }); + suite('non-collapsed dec', () => { + test('before', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 3, 1, 3), 'xx', + [ + [new Range(1, 6, 1, 11), new Range(1, 6, 1, 11), new Range(1, 6, 1, 11), new Range(1, 6, 1, 11)], + [new Range(1, 6, 1, 11), new Range(1, 6, 1, 11), new Range(1, 6, 1, 11), new Range(1, 6, 1, 11)], + ] + ); + }); + test('start', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 4, 1, 4), 'xx', + [ + [new Range(1, 4, 1, 11), new Range(1, 6, 1, 11), new Range(1, 4, 1, 11), new Range(1, 6, 1, 11)], + [new Range(1, 6, 1, 11), new Range(1, 6, 1, 11), new Range(1, 6, 1, 11), new Range(1, 6, 1, 11)], + ] + ); + }); + test('inside', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 5, 1, 5), 'xx', + [ + [new Range(1, 4, 1, 11), new Range(1, 4, 1, 11), new Range(1, 4, 1, 11), new Range(1, 4, 1, 11)], + [new Range(1, 4, 1, 11), new Range(1, 4, 1, 11), new Range(1, 4, 1, 11), new Range(1, 4, 1, 11)], + ] + ); + }); + test('end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 9, 1, 9), 'xx', + [ + [new Range(1, 4, 1, 11), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 11)], + [new Range(1, 4, 1, 11), new Range(1, 4, 1, 11), new Range(1, 4, 1, 11), new Range(1, 4, 1, 11)], + ] + ); + }); + test('after', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 10, 1, 10), 'xx', + [ + [new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9)], + [new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9)], + ] + ); + }); + }); + }); + + suite('delete', () => { + suite('collapsed dec', () => { + test('edit.end < range.start', () => { + runTest( + new Range(1, 4, 1, 4), + new Range(1, 1, 1, 3), '', + [ + [new Range(1, 2, 1, 2), new Range(1, 2, 1, 2), new Range(1, 2, 1, 2), new Range(1, 2, 1, 2)], + [new Range(1, 2, 1, 2), new Range(1, 2, 1, 2), new Range(1, 2, 1, 2), new Range(1, 2, 1, 2)], + ] + ); + }); + test('edit.end <= range.start', () => { + runTest( + new Range(1, 4, 1, 4), + new Range(1, 2, 1, 4), '', + [ + [new Range(1, 2, 1, 2), new Range(1, 2, 1, 2), new Range(1, 2, 1, 2), new Range(1, 2, 1, 2)], + [new Range(1, 2, 1, 2), new Range(1, 2, 1, 2), new Range(1, 2, 1, 2), new Range(1, 2, 1, 2)], + ] + ); + }); + test('edit.start < range.start && edit.end > range.end', () => { + runTest( + new Range(1, 4, 1, 4), + new Range(1, 3, 1, 5), '', + [ + [new Range(1, 3, 1, 3), new Range(1, 3, 1, 3), new Range(1, 3, 1, 3), new Range(1, 3, 1, 3)], + [new Range(1, 3, 1, 3), new Range(1, 3, 1, 3), new Range(1, 3, 1, 3), new Range(1, 3, 1, 3)], + ] + ); + }); + test('edit.start >= range.end', () => { + runTest( + new Range(1, 4, 1, 4), + new Range(1, 4, 1, 6), '', + [ + [new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4)], + [new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4)], + ] + ); + }); + test('edit.start > range.end', () => { + runTest( + new Range(1, 4, 1, 4), + new Range(1, 5, 1, 7), '', + [ + [new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4)], + [new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4)], + ] + ); + }); + }); + suite('non-collapsed dec', () => { + test('edit.end < range.start', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 1, 1, 3), '', + [ + [new Range(1, 2, 1, 7), new Range(1, 2, 1, 7), new Range(1, 2, 1, 7), new Range(1, 2, 1, 7)], + [new Range(1, 2, 1, 7), new Range(1, 2, 1, 7), new Range(1, 2, 1, 7), new Range(1, 2, 1, 7)], + ] + ); + }); + test('edit.end <= range.start', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 2, 1, 4), '', + [ + [new Range(1, 2, 1, 7), new Range(1, 2, 1, 7), new Range(1, 2, 1, 7), new Range(1, 2, 1, 7)], + [new Range(1, 2, 1, 7), new Range(1, 2, 1, 7), new Range(1, 2, 1, 7), new Range(1, 2, 1, 7)], + ] + ); + }); + test('edit.start < range.start && edit.end < range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 3, 1, 5), '', + [ + [new Range(1, 3, 1, 7), new Range(1, 3, 1, 7), new Range(1, 3, 1, 7), new Range(1, 3, 1, 7)], + [new Range(1, 3, 1, 7), new Range(1, 3, 1, 7), new Range(1, 3, 1, 7), new Range(1, 3, 1, 7)], + ] + ); + }); + + test('edit.start < range.start && edit.end == range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 3, 1, 9), '', + [ + [new Range(1, 3, 1, 3), new Range(1, 3, 1, 3), new Range(1, 3, 1, 3), new Range(1, 3, 1, 3)], + [new Range(1, 3, 1, 3), new Range(1, 3, 1, 3), new Range(1, 3, 1, 3), new Range(1, 3, 1, 3)], + ] + ); + }); + + test('edit.start < range.start && edit.end > range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 3, 1, 10), '', + [ + [new Range(1, 3, 1, 3), new Range(1, 3, 1, 3), new Range(1, 3, 1, 3), new Range(1, 3, 1, 3)], + [new Range(1, 3, 1, 3), new Range(1, 3, 1, 3), new Range(1, 3, 1, 3), new Range(1, 3, 1, 3)], + ] + ); + }); + + test('edit.start == range.start && edit.end < range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 4, 1, 6), '', + [ + [new Range(1, 4, 1, 7), new Range(1, 4, 1, 7), new Range(1, 4, 1, 7), new Range(1, 4, 1, 7)], + [new Range(1, 4, 1, 7), new Range(1, 4, 1, 7), new Range(1, 4, 1, 7), new Range(1, 4, 1, 7)], + ] + ); + }); + + test('edit.start == range.start && edit.end == range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 4, 1, 9), '', + [ + [new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4)], + [new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4)], + ] + ); + }); + + test('edit.start == range.start && edit.end > range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 4, 1, 10), '', + [ + [new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4)], + [new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4)], + ] + ); + }); + + test('edit.start > range.start && edit.start < range.end && edit.end < range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 5, 1, 7), '', + [ + [new Range(1, 4, 1, 7), new Range(1, 4, 1, 7), new Range(1, 4, 1, 7), new Range(1, 4, 1, 7)], + [new Range(1, 4, 1, 7), new Range(1, 4, 1, 7), new Range(1, 4, 1, 7), new Range(1, 4, 1, 7)], + ] + ); + }); + + test('edit.start > range.start && edit.start < range.end && edit.end == range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 5, 1, 9), '', + [ + [new Range(1, 4, 1, 5), new Range(1, 4, 1, 5), new Range(1, 4, 1, 5), new Range(1, 4, 1, 5)], + [new Range(1, 4, 1, 5), new Range(1, 4, 1, 5), new Range(1, 4, 1, 5), new Range(1, 4, 1, 5)], + ] + ); + }); + + test('edit.start > range.start && edit.start < range.end && edit.end > range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 5, 1, 10), '', + [ + [new Range(1, 4, 1, 5), new Range(1, 4, 1, 5), new Range(1, 4, 1, 5), new Range(1, 4, 1, 5)], + [new Range(1, 4, 1, 5), new Range(1, 4, 1, 5), new Range(1, 4, 1, 5), new Range(1, 4, 1, 5)], + ] + ); + }); + + test('edit.start == range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 9, 1, 11), '', + [ + [new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9)], + [new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9)], + ] + ); + }); + + test('edit.start > range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 10, 1, 11), '', + [ + [new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9)], + [new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9)], + ] + ); + }); + }); + }); + + suite('replace short', () => { + suite('collapsed dec', () => { + test('edit.end < range.start', () => { + runTest( + new Range(1, 4, 1, 4), + new Range(1, 1, 1, 3), 'c', + [ + [new Range(1, 3, 1, 3), new Range(1, 3, 1, 3), new Range(1, 3, 1, 3), new Range(1, 3, 1, 3)], + [new Range(1, 3, 1, 3), new Range(1, 3, 1, 3), new Range(1, 3, 1, 3), new Range(1, 3, 1, 3)], + ] + ); + }); + test('edit.end <= range.start', () => { + runTest( + new Range(1, 4, 1, 4), + new Range(1, 2, 1, 4), 'c', + [ + [new Range(1, 3, 1, 3), new Range(1, 3, 1, 3), new Range(1, 3, 1, 3), new Range(1, 3, 1, 3)], + [new Range(1, 3, 1, 3), new Range(1, 3, 1, 3), new Range(1, 3, 1, 3), new Range(1, 3, 1, 3)], + ] + ); + }); + test('edit.start < range.start && edit.end > range.end', () => { + runTest( + new Range(1, 4, 1, 4), + new Range(1, 3, 1, 5), 'c', + [ + [new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4)], + [new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4)], + ] + ); + }); + test('edit.start >= range.end', () => { + runTest( + new Range(1, 4, 1, 4), + new Range(1, 4, 1, 6), 'c', + [ + [new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4)], + [new Range(1, 5, 1, 5), new Range(1, 5, 1, 5), new Range(1, 5, 1, 5), new Range(1, 5, 1, 5)], + ] + ); + }); + test('edit.start > range.end', () => { + runTest( + new Range(1, 4, 1, 4), + new Range(1, 5, 1, 7), 'c', + [ + [new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4)], + [new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4)], + ] + ); + }); + }); + suite('non-collapsed dec', () => { + test('edit.end < range.start', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 1, 1, 3), 'c', + [ + [new Range(1, 3, 1, 8), new Range(1, 3, 1, 8), new Range(1, 3, 1, 8), new Range(1, 3, 1, 8)], + [new Range(1, 3, 1, 8), new Range(1, 3, 1, 8), new Range(1, 3, 1, 8), new Range(1, 3, 1, 8)], + ] + ); + }); + test('edit.end <= range.start', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 2, 1, 4), 'c', + [ + [new Range(1, 3, 1, 8), new Range(1, 3, 1, 8), new Range(1, 3, 1, 8), new Range(1, 3, 1, 8)], + [new Range(1, 3, 1, 8), new Range(1, 3, 1, 8), new Range(1, 3, 1, 8), new Range(1, 3, 1, 8)], + ] + ); + }); + test('edit.start < range.start && edit.end < range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 3, 1, 5), 'c', + [ + [new Range(1, 4, 1, 8), new Range(1, 4, 1, 8), new Range(1, 4, 1, 8), new Range(1, 4, 1, 8)], + [new Range(1, 4, 1, 8), new Range(1, 4, 1, 8), new Range(1, 4, 1, 8), new Range(1, 4, 1, 8)], + ] + ); + }); + test('edit.start < range.start && edit.end == range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 3, 1, 9), 'c', + [ + [new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4)], + [new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4)], + ] + ); + }); + test('edit.start < range.start && edit.end > range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 3, 1, 10), 'c', + [ + [new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4)], + [new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4)], + ] + ); + }); + test('edit.start == range.start && edit.end < range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 4, 1, 6), 'c', + [ + [new Range(1, 4, 1, 8), new Range(1, 4, 1, 8), new Range(1, 4, 1, 8), new Range(1, 4, 1, 8)], + [new Range(1, 5, 1, 8), new Range(1, 5, 1, 8), new Range(1, 5, 1, 8), new Range(1, 5, 1, 8)], + ] + ); + }); + test('edit.start == range.start && edit.end == range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 4, 1, 9), 'c', + [ + [new Range(1, 4, 1, 5), new Range(1, 4, 1, 5), new Range(1, 4, 1, 5), new Range(1, 4, 1, 5)], + [new Range(1, 5, 1, 5), new Range(1, 5, 1, 5), new Range(1, 5, 1, 5), new Range(1, 5, 1, 5)], + ] + ); + }); + test('edit.start == range.start && edit.end > range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 4, 1, 10), 'c', + [ + [new Range(1, 4, 1, 5), new Range(1, 4, 1, 5), new Range(1, 4, 1, 5), new Range(1, 4, 1, 5)], + [new Range(1, 5, 1, 5), new Range(1, 5, 1, 5), new Range(1, 5, 1, 5), new Range(1, 5, 1, 5)], + ] + ); + }); + test('edit.start > range.start && edit.start < range.end && edit.end < range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 5, 1, 7), 'c', + [ + [new Range(1, 4, 1, 8), new Range(1, 4, 1, 8), new Range(1, 4, 1, 8), new Range(1, 4, 1, 8)], + [new Range(1, 4, 1, 8), new Range(1, 4, 1, 8), new Range(1, 4, 1, 8), new Range(1, 4, 1, 8)], + ] + ); + }); + test('edit.start > range.start && edit.start < range.end && edit.end == range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 5, 1, 9), 'c', + [ + [new Range(1, 4, 1, 6), new Range(1, 4, 1, 6), new Range(1, 4, 1, 6), new Range(1, 4, 1, 6)], + [new Range(1, 4, 1, 6), new Range(1, 4, 1, 6), new Range(1, 4, 1, 6), new Range(1, 4, 1, 6)], + ] + ); + }); + test('edit.start > range.start && edit.start < range.end && edit.end > range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 5, 1, 10), 'c', + [ + [new Range(1, 4, 1, 6), new Range(1, 4, 1, 6), new Range(1, 4, 1, 6), new Range(1, 4, 1, 6)], + [new Range(1, 4, 1, 6), new Range(1, 4, 1, 6), new Range(1, 4, 1, 6), new Range(1, 4, 1, 6)], + ] + ); + }); + test('edit.start == range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 9, 1, 11), 'c', + [ + [new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9)], + [new Range(1, 4, 1, 10), new Range(1, 4, 1, 10), new Range(1, 4, 1, 10), new Range(1, 4, 1, 10)], + ] + ); + }); + test('edit.start > range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 10, 1, 11), 'c', + [ + [new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9)], + [new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9)], + ] + ); + }); + }); + }); + + suite('replace long', () => { + suite('collapsed dec', () => { + test('edit.end < range.start', () => { + runTest( + new Range(1, 4, 1, 4), + new Range(1, 1, 1, 3), 'cccc', + [ + [new Range(1, 6, 1, 6), new Range(1, 6, 1, 6), new Range(1, 6, 1, 6), new Range(1, 6, 1, 6)], + [new Range(1, 6, 1, 6), new Range(1, 6, 1, 6), new Range(1, 6, 1, 6), new Range(1, 6, 1, 6)], + ] + ); + }); + test('edit.end <= range.start', () => { + runTest( + new Range(1, 4, 1, 4), + new Range(1, 2, 1, 4), 'cccc', + [ + [new Range(1, 4, 1, 6), new Range(1, 6, 1, 6), new Range(1, 4, 1, 4), new Range(1, 6, 1, 6)], + [new Range(1, 6, 1, 6), new Range(1, 6, 1, 6), new Range(1, 6, 1, 6), new Range(1, 6, 1, 6)], + ] + ); + }); + test('edit.start < range.start && edit.end > range.end', () => { + runTest( + new Range(1, 4, 1, 4), + new Range(1, 3, 1, 5), 'cccc', + [ + [new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4)], + [new Range(1, 7, 1, 7), new Range(1, 7, 1, 7), new Range(1, 7, 1, 7), new Range(1, 7, 1, 7)], + ] + ); + }); + test('edit.start >= range.end', () => { + runTest( + new Range(1, 4, 1, 4), + new Range(1, 4, 1, 6), 'cccc', + [ + [new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4)], + [new Range(1, 8, 1, 8), new Range(1, 8, 1, 8), new Range(1, 8, 1, 8), new Range(1, 8, 1, 8)], + ] + ); + }); + test('edit.start > range.end', () => { + runTest( + new Range(1, 4, 1, 4), + new Range(1, 5, 1, 7), 'cccc', + [ + [new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4)], + [new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4), new Range(1, 4, 1, 4)], + ] + ); + }); + }); + suite('non-collapsed dec', () => { + test('edit.end < range.start', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 1, 1, 3), 'cccc', + [ + [new Range(1, 6, 1, 11), new Range(1, 6, 1, 11), new Range(1, 6, 1, 11), new Range(1, 6, 1, 11)], + [new Range(1, 6, 1, 11), new Range(1, 6, 1, 11), new Range(1, 6, 1, 11), new Range(1, 6, 1, 11)], + ] + ); + }); + test('edit.end <= range.start', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 2, 1, 4), 'cccc', + [ + [new Range(1, 4, 1, 11), new Range(1, 6, 1, 11), new Range(1, 4, 1, 11), new Range(1, 6, 1, 11)], + [new Range(1, 6, 1, 11), new Range(1, 6, 1, 11), new Range(1, 6, 1, 11), new Range(1, 6, 1, 11)], + ] + ); + }); + test('edit.start < range.start && edit.end < range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 3, 1, 5), 'cccc', + [ + [new Range(1, 4, 1, 11), new Range(1, 4, 1, 11), new Range(1, 4, 1, 11), new Range(1, 4, 1, 11)], + [new Range(1, 7, 1, 11), new Range(1, 7, 1, 11), new Range(1, 7, 1, 11), new Range(1, 7, 1, 11)], + ] + ); + }); + test('edit.start < range.start && edit.end == range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 3, 1, 9), 'cccc', + [ + [new Range(1, 4, 1, 7), new Range(1, 4, 1, 7), new Range(1, 4, 1, 7), new Range(1, 4, 1, 7)], + [new Range(1, 7, 1, 7), new Range(1, 7, 1, 7), new Range(1, 7, 1, 7), new Range(1, 7, 1, 7)], + ] + ); + }); + test('edit.start < range.start && edit.end > range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 3, 1, 10), 'cccc', + [ + [new Range(1, 4, 1, 7), new Range(1, 4, 1, 7), new Range(1, 4, 1, 7), new Range(1, 4, 1, 7)], + [new Range(1, 7, 1, 7), new Range(1, 7, 1, 7), new Range(1, 7, 1, 7), new Range(1, 7, 1, 7)], + ] + ); + }); + test('edit.start == range.start && edit.end < range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 4, 1, 6), 'cccc', + [ + [new Range(1, 4, 1, 11), new Range(1, 4, 1, 11), new Range(1, 4, 1, 11), new Range(1, 4, 1, 11)], + [new Range(1, 8, 1, 11), new Range(1, 8, 1, 11), new Range(1, 8, 1, 11), new Range(1, 8, 1, 11)], + ] + ); + }); + test('edit.start == range.start && edit.end == range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 4, 1, 9), 'cccc', + [ + [new Range(1, 4, 1, 8), new Range(1, 4, 1, 8), new Range(1, 4, 1, 8), new Range(1, 4, 1, 8)], + [new Range(1, 8, 1, 8), new Range(1, 8, 1, 8), new Range(1, 8, 1, 8), new Range(1, 8, 1, 8)], + ] + ); + }); + test('edit.start == range.start && edit.end > range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 4, 1, 10), 'cccc', + [ + [new Range(1, 4, 1, 8), new Range(1, 4, 1, 8), new Range(1, 4, 1, 8), new Range(1, 4, 1, 8)], + [new Range(1, 8, 1, 8), new Range(1, 8, 1, 8), new Range(1, 8, 1, 8), new Range(1, 8, 1, 8)], + ] + ); + }); + test('edit.start > range.start && edit.start < range.end && edit.end < range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 5, 1, 7), 'cccc', + [ + [new Range(1, 4, 1, 11), new Range(1, 4, 1, 11), new Range(1, 4, 1, 11), new Range(1, 4, 1, 11)], + [new Range(1, 4, 1, 11), new Range(1, 4, 1, 11), new Range(1, 4, 1, 11), new Range(1, 4, 1, 11)], + ] + ); + }); + test('edit.start > range.start && edit.start < range.end && edit.end == range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 5, 1, 9), 'cccc', + [ + [new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9)], + [new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9)], + ] + ); + }); + test('edit.start > range.start && edit.start < range.end && edit.end > range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 5, 1, 10), 'cccc', + [ + [new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9)], + [new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9)], + ] + ); + }); + test('edit.start == range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 9, 1, 11), 'cccc', + [ + [new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9)], + [new Range(1, 4, 1, 13), new Range(1, 4, 1, 13), new Range(1, 4, 1, 13), new Range(1, 4, 1, 13)], + ] + ); + }); + test('edit.start > range.end', () => { + runTest( + new Range(1, 4, 1, 9), + new Range(1, 10, 1, 11), 'cccc', + [ + [new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9)], + [new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9), new Range(1, 4, 1, 9)], + ] + ); + }); + }); + }); }); interface ILightWeightDecoration { @@ -420,7 +1155,6 @@ suite('deltaDecorations', () => { assert.equal(initialIds.length, decorations.length, 'returns expected cnt of ids'); assert.equal(initialIds.length, model.getAllDecorations().length, 'does not leak decorations'); assert.equal(initialIds.length, model._getTrackedRangesCount(), 'does not leak tracked ranges'); - assert.equal(2 * initialIds.length, model._getMarkersCount(), 'does not leak markers'); actualDecorations.sort((a, b) => strcmp(a.id, b.id)); decorations.sort((a, b) => strcmp(a.id, b.id)); assert.deepEqual(actualDecorations, decorations); @@ -431,7 +1165,6 @@ suite('deltaDecorations', () => { assert.equal(newIds.length, newDecorations.length, 'returns expected cnt of ids'); assert.equal(newIds.length, model.getAllDecorations().length, 'does not leak decorations'); assert.equal(newIds.length, model._getTrackedRangesCount(), 'does not leak tracked ranges'); - assert.equal(2 * newIds.length, model._getMarkersCount(), 'does not leak markers'); actualNewDecorations.sort((a, b) => strcmp(a.id, b.id)); newDecorations.sort((a, b) => strcmp(a.id, b.id)); assert.deepEqual(actualDecorations, decorations); diff --git a/src/vs/editor/test/common/viewModel/viewModelDecorations.test.ts b/src/vs/editor/test/common/viewModel/viewModelDecorations.test.ts index 0f52c5129c3..20bbda92be7 100644 --- a/src/vs/editor/test/common/viewModel/viewModelDecorations.test.ts +++ b/src/vs/editor/test/common/viewModel/viewModelDecorations.test.ts @@ -91,23 +91,23 @@ suite('ViewModelDecorations', () => { let actualDecorations = viewModel.getDecorationsInViewport( new Range(2, viewModel.getLineMinColumn(2), 3, viewModel.getLineMaxColumn(3)) ).map((dec) => { - return dec.source.id; + return dec.options.className; }); assert.deepEqual(actualDecorations, [ - dec2, - dec3, - dec4, - dec5, - dec6, - dec7, - dec8, - dec9, - dec10, - dec11, - dec12, - dec13, - dec14, + 'dec2', + 'dec3', + 'dec4', + 'dec5', + 'dec6', + 'dec7', + 'dec8', + 'dec9', + 'dec10', + 'dec11', + 'dec12', + 'dec13', + 'dec14', ]); let inlineDecorations1 = viewModel.getViewLineRenderingData( diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index c43c2f05b34..9142f59537b 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -109,7 +109,7 @@ declare module monaco { * * */ - export class Uri { + export class Uri implements UriComponents { static isUri(thing: any): thing is Uri; /** * scheme is the 'http' part of 'http://www.msft.com/some/path?query#fragment'. @@ -161,8 +161,16 @@ declare module monaco { * @param skipEncoding Do not encode the result, default is `false` */ toString(skipEncoding?: boolean): string; - toJSON(): any; - static revive(data: any): Uri; + toJSON(): object; + static revive(data: UriComponents | any): Uri; + } + + export interface UriComponents { + scheme: string; + authority: string; + path: string; + query: string; + fragment: string; } /** @@ -1188,10 +1196,6 @@ declare module monaco.editor { * Options associated with this decoration. */ readonly options: IModelDecorationOptions; - /** - * A flag describing if this is a problem decoration (e.g. warning/error). - */ - readonly isForValidation: boolean; } /** @@ -1641,12 +1645,6 @@ declare module monaco.editor { getWordUntilPosition(position: IPosition): IWordAtPosition; } - /** - * A model that can track markers. - */ - export interface ITextModelWithMarkers extends ITextModel { - } - /** * Describes the behavior of decorations when typing/editing near their edges. * Note: Please do not edit the values, as they very carefully match `DecorationRangeBehavior` @@ -1717,12 +1715,18 @@ declare module monaco.editor { * @param filterOutValidation If set, it will ignore decorations specific to validation (i.e. warnings, errors). */ getAllDecorations(ownerId?: number, filterOutValidation?: boolean): IModelDecoration[]; + /** + * Gets all the decorations that should be rendered in the overview ruler as an array. + * @param ownerId If set, it will ignore decorations belonging to other owners. + * @param filterOutValidation If set, it will ignore decorations specific to validation (i.e. warnings, errors). + */ + getOverviewRulerDecorations(ownerId?: number, filterOutValidation?: boolean): IModelDecoration[]; } /** * An editable text model. */ - export interface IEditableTextModel extends ITextModelWithMarkers { + export interface IEditableTextModel extends ITextModel { /** * Normalize a string containing whitespace according to indentation rules (converts to spaces or to tabs). */ @@ -1766,7 +1770,7 @@ declare module monaco.editor { /** * A model. */ - export interface IModel extends IReadOnlyModel, IEditableTextModel, ITextModelWithMarkers, ITokenizedModel, ITextModelWithDecorations { + export interface IModel extends IReadOnlyModel, IEditableTextModel, ITokenizedModel, ITextModelWithDecorations { /** * An event emitted when the contents of the model have changed. * @event @@ -2482,18 +2486,6 @@ declare module monaco.editor { * An event describing that model decorations have changed. */ export interface IModelDecorationsChangedEvent { - /** - * Lists of ids for added decorations. - */ - readonly addedDecorations: string[]; - /** - * Lists of ids for changed decorations. - */ - readonly changedDecorations: string[]; - /** - * List of ids for removed decorations. - */ - readonly removedDecorations: string[]; } /** diff --git a/src/vs/platform/backup/test/electron-main/backupMainService.test.ts b/src/vs/platform/backup/test/electron-main/backupMainService.test.ts index 7d5a37d4edb..d200c14c5f7 100644 --- a/src/vs/platform/backup/test/electron-main/backupMainService.test.ts +++ b/src/vs/platform/backup/test/electron-main/backupMainService.test.ts @@ -23,9 +23,10 @@ import { LogMainService } from 'vs/platform/log/common/log'; import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { createHash } from 'crypto'; import { WorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService'; +import { getRandomTestPath } from 'vs/workbench/test/workbenchTestServices'; suite('BackupMainService', () => { - const parentDir = path.join(os.tmpdir(), 'vsctests', 'service'); + const parentDir = getRandomTestPath(os.tmpdir(), 'vsctests', 'backupservice'); const backupHome = path.join(parentDir, 'Backups'); const backupWorkspacesPath = path.join(backupHome, 'workspaces.json'); diff --git a/src/vs/platform/configuration/common/configuration.ts b/src/vs/platform/configuration/common/configuration.ts index d541ed739df..0fb11cd76f5 100644 --- a/src/vs/platform/configuration/common/configuration.ts +++ b/src/vs/platform/configuration/common/configuration.ts @@ -4,94 +4,186 @@ *--------------------------------------------------------------------------------------------*/ import { TPromise } from 'vs/base/common/winjs.base'; -import * as arrays from 'vs/base/common/arrays'; -import * as types from 'vs/base/common/types'; import * as objects from 'vs/base/common/objects'; +import * as types from 'vs/base/common/types'; import URI from 'vs/base/common/uri'; -import { StrictResourceMap } from 'vs/base/common/map'; -import { Workspace } from 'vs/platform/workspace/common/workspace'; -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import Event from 'vs/base/common/event'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry'; +import { StrictResourceMap } from 'vs/base/common/map'; export const IConfigurationService = createDecorator('configurationService'); +export function isConfigurationOverrides(thing: any): thing is IConfigurationOverrides { + return thing + && typeof thing === 'object' + && (!thing.overrideIdentifier || typeof thing.overrideIdentifier === 'string') + && (!thing.resource || thing.resource instanceof URI); +} + export interface IConfigurationOverrides { overrideIdentifier?: string; resource?: URI; } -export type IConfigurationValues = { [key: string]: IConfigurationValue }; +export enum ConfigurationTarget { + USER = 1, + WORKSPACE, + WORKSPACE_FOLDER, + DEFAULT, + MEMORY +} + +export interface IConfigurationChangeEvent { + + source: ConfigurationTarget; + affectedKeys: string[]; + affectsConfiguration(configuration: string, resource?: URI): boolean; + + // Following data is used for telemetry + sourceConfig: any; + + // Following data is used for Extension host configuration event + changedConfiguration: IConfigurationModel; + changedConfigurationByResource: StrictResourceMap; +} export interface IConfigurationService { _serviceBrand: any; - getConfigurationData(): IConfigurationData; + onDidChangeConfiguration: Event; - /** - * Fetches the appropriate section of the configuration JSON file. - * This will be an object keyed off the section name. - */ - getConfiguration(section?: string, overrides?: IConfigurationOverrides): T; + getConfigurationData(): IConfigurationData; - /** - * Resolves a configuration key to its values in the different scopes - * the setting is defined. - */ - lookup(key: string, overrides?: IConfigurationOverrides): IConfigurationValue; + getConfiguration(): T; + getConfiguration(section: string): T; + getConfiguration(overrides: IConfigurationOverrides): T; + getConfiguration(section: string, overrides: IConfigurationOverrides): T; - /** - * Returns the defined keys of configurations in the different scopes - * the key is defined. - */ - keys(overrides?: IConfigurationOverrides): IConfigurationKeys; + getValue(key: string, overrides?: IConfigurationOverrides): T; - /** - * Similar to #getConfiguration() but ensures that the latest configuration - * from disk is fetched. - */ - reloadConfiguration(section?: string): TPromise; + updateValue(key: string, value: any): TPromise; + updateValue(key: string, value: any, overrides: IConfigurationOverrides): TPromise; + updateValue(key: string, value: any, target: ConfigurationTarget): TPromise; + updateValue(key: string, value: any, overrides: IConfigurationOverrides, target: ConfigurationTarget, donotNotifyError?: boolean): TPromise; - /** - * Event that fires when the configuration changes. - */ - onDidUpdateConfiguration: Event; + reloadConfiguration(): TPromise; + reloadConfiguration(folder: IWorkspaceFolder): TPromise; - /** - * Returns the defined values of configurations in the different scopes. - */ - values(): IConfigurationValues; + inspect(key: string): { + default: T, + user: T, + workspace: T, + workspaceFolder: T, + memory?: T, + value: T, + }; + + keys(): { + default: string[]; + user: string[]; + workspace: string[]; + workspaceFolder: string[]; + memory?: string[]; + }; } -export enum ConfigurationSource { - Default = 1, - User, - Workspace +export interface IConfigurationModel { + contents: any; + keys: string[]; + overrides: IOverrides[]; } -export interface IConfigurationServiceEvent { - /** - * The type of source that triggered this event. - */ - source: ConfigurationSource; - /** - * The part of the configuration contributed by the source of this event. - */ - sourceConfig: any; +export interface IOverrides { + contents: any; + identifiers: string[]; } -export interface IConfigurationValue { - value: T; - default: T; - user: T; - workspace: T; - folder: T; +export interface IConfigurationData { + defaults: IConfigurationModel; + user: IConfigurationModel; + workspace: IConfigurationModel; + folders: { [folder: string]: IConfigurationModel }; } -export interface IConfigurationKeys { - default: string[]; - user: string[]; - workspace: string[]; - folder: string[]; +export function compare(from: IConfigurationModel, to: IConfigurationModel): { added: string[], removed: string[], updated: string[] } { + const added = to.keys.filter(key => from.keys.indexOf(key) === -1); + const removed = from.keys.filter(key => to.keys.indexOf(key) === -1); + const updated = []; + + for (const key of from.keys) { + const value1 = getConfigurationValue(from.contents, key); + const value2 = getConfigurationValue(to.contents, key); + if (!objects.equals(value1, value2)) { + updated.push(key); + } + } + + return { added, removed, updated }; +} + +export function toValuesTree(properties: { [qualifiedKey: string]: any }, conflictReporter: (message: string) => void): any { + const root = Object.create(null); + + for (let key in properties) { + addToValueTree(root, key, properties[key], conflictReporter); + } + + return root; +} + +export function addToValueTree(settingsTreeRoot: any, key: string, value: any, conflictReporter: (message: string) => void): void { + const segments = key.split('.'); + const last = segments.pop(); + + let curr = settingsTreeRoot; + for (let i = 0; i < segments.length; i++) { + let s = segments[i]; + let obj = curr[s]; + switch (typeof obj) { + case 'undefined': + obj = curr[s] = Object.create(null); + break; + case 'object': + break; + default: + conflictReporter(`Ignoring ${key} as ${segments.slice(0, i + 1).join('.')} is ${JSON.stringify(obj)}`); + return; + } + curr = obj; + }; + + if (typeof curr === 'object') { + curr[last] = value; // workaround https://github.com/Microsoft/vscode/issues/13606 + } else { + conflictReporter(`Ignoring ${key} as ${segments.join('.')} is ${JSON.stringify(curr)}`); + } +} + +export function removeFromValueTree(valueTree: any, key: string): void { + const segments = key.split('.'); + doRemoveFromValueTree(valueTree, segments); +} + +function doRemoveFromValueTree(valueTree: any, segments: string[]): void { + const first = segments.shift(); + if (segments.length === 0) { + // Reached last segment + delete valueTree[first]; + return; + } + + if (Object.keys(valueTree).indexOf(first) !== -1) { + const value = valueTree[first]; + if (typeof value === 'object' && !Array.isArray(value)) { + doRemoveFromValueTree(value, segments); + if (Object.keys(value).length === 0) { + delete valueTree[first]; + } + } + } } /** @@ -129,240 +221,27 @@ export function merge(base: any, add: any, overwrite: boolean): void { }); } -export interface IConfiguraionModel { - contents: T; - keys: string[]; - overrides: IOverrides[]; +export function getConfigurationKeys(): string[] { + const properties = Registry.as(Extensions.Configuration).getConfigurationProperties(); + return Object.keys(properties); } -export interface IOverrides { - contents: T; - identifiers: string[]; +export function getDefaultValues(): any { + const valueTreeRoot: any = Object.create(null); + const properties = Registry.as(Extensions.Configuration).getConfigurationProperties(); + + for (let key in properties) { + let value = properties[key].default; + addToValueTree(valueTreeRoot, key, value, message => console.error(`Conflict in default settings: ${message}`)); + } + + return valueTreeRoot; } -export class ConfigurationModel implements IConfiguraionModel { - - constructor(protected _contents: T = {}, protected _keys: string[] = [], protected _overrides: IOverrides[] = []) { - } - - public get contents(): T { - return this._contents; - } - - public get overrides(): IOverrides[] { - return this._overrides; - } - - public get keys(): string[] { - return this._keys; - } - - public getContentsFor(section: string): V { - return objects.clone(this.contents[section]); - } - - public override(identifier: string): ConfigurationModel { - const result = new ConfigurationModel(); - const contents = objects.clone(this.contents); - if (this._overrides) { - for (const override of this._overrides) { - if (override.identifiers.indexOf(identifier) !== -1) { - merge(contents, override.contents, true); - } - } - } - result._contents = contents; - return result; - } - - public merge(other: ConfigurationModel, overwrite: boolean = true): ConfigurationModel { - const mergedModel = new ConfigurationModel(); - this.doMerge(mergedModel, this, overwrite); - this.doMerge(mergedModel, other, overwrite); - return mergedModel; - } - - protected doMerge(source: ConfigurationModel, target: ConfigurationModel, overwrite: boolean = true) { - merge(source.contents, objects.clone(target.contents), overwrite); - const overrides = objects.clone(source._overrides); - for (const override of target._overrides) { - const [sourceOverride] = overrides.filter(o => arrays.equals(o.identifiers, override.identifiers)); - if (sourceOverride) { - merge(sourceOverride.contents, override.contents, overwrite); - } else { - overrides.push(override); - } - } - source._overrides = overrides; - } +export function overrideIdentifierFromKey(key: string): string { + return key.substring(1, key.length - 1); } -export interface IConfigurationData { - defaults: IConfiguraionModel; - user: IConfiguraionModel; - workspace: IConfiguraionModel; - folders: { [folder: string]: IConfiguraionModel }; +export function keyFromOverrideIdentifier(overrideIdentifier: string): string { + return `[${overrideIdentifier}]`; } - -export class Configuration { - - private _globalConfiguration: ConfigurationModel; - private _workspaceConsolidatedConfiguration: ConfigurationModel; - protected _foldersConsolidatedConfigurations: StrictResourceMap>; - - constructor(protected _defaults: ConfigurationModel, protected _user: ConfigurationModel, protected _workspaceConfiguration: ConfigurationModel = new ConfigurationModel(), protected folders: StrictResourceMap> = new StrictResourceMap>(), protected _workspace?: Workspace) { - this.merge(); - } - - get defaults(): ConfigurationModel { - return this._defaults; - } - - get user(): ConfigurationModel { - return this._user; - } - - protected merge(): void { - this._globalConfiguration = new ConfigurationModel().merge(this._defaults).merge(this._user); - this._workspaceConsolidatedConfiguration = new ConfigurationModel().merge(this._globalConfiguration).merge(this._workspaceConfiguration); - this._foldersConsolidatedConfigurations = new StrictResourceMap>(); - for (const folder of this.folders.keys()) { - this.mergeFolder(folder); - } - } - - protected mergeFolder(folder: URI) { - this._foldersConsolidatedConfigurations.set(folder, new ConfigurationModel().merge(this._workspaceConsolidatedConfiguration).merge(this.folders.get(folder))); - } - - getValue(section: string = '', overrides: IConfigurationOverrides = {}): C { - const configModel = this.getConsolidateConfigurationModel(overrides); - return section ? configModel.getContentsFor(section) : configModel.contents; - } - - lookup(key: string, overrides: IConfigurationOverrides = {}): IConfigurationValue { - // make sure to clone the configuration so that the receiver does not tamper with the values - const consolidateConfigurationModel = this.getConsolidateConfigurationModel(overrides); - const folderConfigurationModel = this.getFolderConfigurationModelForResource(overrides.resource); - return { - default: objects.clone(getConfigurationValue(overrides.overrideIdentifier ? this._defaults.override(overrides.overrideIdentifier).contents : this._defaults.contents, key)), - user: objects.clone(getConfigurationValue(overrides.overrideIdentifier ? this._user.override(overrides.overrideIdentifier).contents : this._user.contents, key)), - workspace: objects.clone(this._workspace ? getConfigurationValue(overrides.overrideIdentifier ? this._workspaceConfiguration.override(overrides.overrideIdentifier).contents : this._workspaceConfiguration.contents, key) : void 0), //Check on workspace exists or not because _workspaceConfiguration is never null - folder: objects.clone(folderConfigurationModel ? getConfigurationValue(overrides.overrideIdentifier ? folderConfigurationModel.override(overrides.overrideIdentifier).contents : folderConfigurationModel.contents, key) : void 0), - value: objects.clone(getConfigurationValue(consolidateConfigurationModel.contents, key)) - }; - } - - keys(overrides: IConfigurationOverrides = {}): IConfigurationKeys { - const folderConfigurationModel = this.getFolderConfigurationModelForResource(overrides.resource); - return { - default: this._defaults.keys, - user: this._user.keys, - workspace: this._workspaceConfiguration.keys, - folder: folderConfigurationModel ? folderConfigurationModel.keys : [] - }; - } - - values(): IConfigurationValues { - const result = Object.create(null); - const keyset = this.keys(); - const keys = [...keyset.workspace, ...keyset.user, ...keyset.default].sort(); - - let lastKey: string; - for (const key of keys) { - if (key !== lastKey) { - lastKey = key; - result[key] = this.lookup(key); - } - } - - return result; - } - - values2(): Map> { - const result: Map> = new Map>(); - const keyset = this.keys(); - const keys = [...keyset.workspace, ...keyset.user, ...keyset.default].sort(); - - let lastKey: string; - for (const key of keys) { - if (key !== lastKey) { - lastKey = key; - result.set(key, this.lookup(key)); - } - } - - return result; - } - - private getConsolidateConfigurationModel(overrides: IConfigurationOverrides): ConfigurationModel { - let configurationModel = this.getConsolidatedConfigurationModelForResource(overrides); - return overrides.overrideIdentifier ? configurationModel.override(overrides.overrideIdentifier) : configurationModel; - } - - private getConsolidatedConfigurationModelForResource({ resource }: IConfigurationOverrides): ConfigurationModel { - if (!this._workspace) { - return this._globalConfiguration; - } - - if (!resource) { - return this._workspaceConsolidatedConfiguration; - } - - const root = this._workspace.getFolder(resource); - if (!root) { - return this._workspaceConsolidatedConfiguration; - } - - return this._foldersConsolidatedConfigurations.get(root.uri) || this._workspaceConsolidatedConfiguration; - } - - private getFolderConfigurationModelForResource(resource: URI): ConfigurationModel { - if (!this._workspace || !resource) { - return null; - } - - const root = this._workspace.getFolder(resource); - return root ? this.folders.get(root.uri) : null; - } - - public toData(): IConfigurationData { - return { - defaults: { - contents: this._defaults.contents, - overrides: this._defaults.overrides, - keys: this._defaults.keys - }, - user: { - contents: this._user.contents, - overrides: this._user.overrides, - keys: this._user.keys - }, - workspace: { - contents: this._workspaceConfiguration.contents, - overrides: this._workspaceConfiguration.overrides, - keys: this._workspaceConfiguration.keys - }, - folders: this.folders.keys().reduce((result, folder) => { - const { contents, overrides, keys } = this.folders.get(folder); - result[folder.toString()] = { contents, overrides, keys }; - return result; - }, Object.create({})) - }; - } - - public static parse(data: IConfigurationData, workspace: Workspace): Configuration { - const defaultConfiguration = Configuration.parseConfigurationModel(data.defaults); - const userConfiguration = Configuration.parseConfigurationModel(data.user); - const workspaceConfiguration = Configuration.parseConfigurationModel(data.workspace); - const folders: StrictResourceMap> = Object.keys(data.folders).reduce((result, key) => { - result.set(URI.parse(key), Configuration.parseConfigurationModel(data.folders[key])); - return result; - }, new StrictResourceMap>()); - return new Configuration(defaultConfiguration, userConfiguration, workspaceConfiguration, folders, workspace); - } - - private static parseConfigurationModel(model: IConfiguraionModel): ConfigurationModel { - return new ConfigurationModel(model.contents, model.keys, model.overrides); - } -} \ No newline at end of file diff --git a/src/vs/platform/configuration/common/configurationModels.ts b/src/vs/platform/configuration/common/configurationModels.ts new file mode 100644 index 00000000000..dcd1c8eb569 --- /dev/null +++ b/src/vs/platform/configuration/common/configurationModels.ts @@ -0,0 +1,602 @@ +/*--------------------------------------------------------------------------------------------- + * 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 json from 'vs/base/common/json'; +import { StrictResourceMap } from 'vs/base/common/map'; +import * as arrays from 'vs/base/common/arrays'; +import * as objects from 'vs/base/common/objects'; +import URI from 'vs/base/common/uri'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IConfigurationRegistry, Extensions, OVERRIDE_PROPERTY_PATTERN } from 'vs/platform/configuration/common/configurationRegistry'; +import { IOverrides, overrideIdentifierFromKey, addToValueTree, toValuesTree, IConfigurationModel, merge, getConfigurationValue, IConfigurationOverrides, IConfigurationData, getDefaultValues, getConfigurationKeys, IConfigurationChangeEvent, ConfigurationTarget, removeFromValueTree } from 'vs/platform/configuration/common/configuration'; +import { Workspace } from 'vs/platform/workspace/common/workspace'; + +export class ConfigurationModel implements IConfigurationModel { + + constructor(protected _contents: any = {}, protected _keys: string[] = [], protected _overrides: IOverrides[] = []) { + } + + public get contents(): any { + return this._contents; + } + + public get overrides(): IOverrides[] { + return this._overrides; + } + + public get keys(): string[] { + return this._keys; + } + + public getSectionContents(section: string): V { + return this.contents[section]; + } + + public setValue(key: string, value: any) { + this.addKey(key); + addToValueTree(this._contents, key, value, e => { throw new Error(e); }); + } + + public removeValue(key: string): void { + if (this.removeKey(key)) { + removeFromValueTree(this._contents, key); + } + } + + public setValueInOverrides(overrideIdentifier: string, key: string, value: any): void { + let override = this._overrides.filter(override => override.identifiers.indexOf(overrideIdentifier) !== -1)[0]; + if (!override) { + override = { identifiers: [overrideIdentifier], contents: {} }; + this._overrides.push(override); + } + addToValueTree(override.contents, key, value, e => { throw new Error(e); }); + } + + public override(identifier: string): ConfigurationModel { + const overrideContents = this.getContentsForOverrideIdentifer(identifier); + + if (!overrideContents || typeof overrideContents !== 'object' || !Object.keys(overrideContents).length) { + // If there are no valid overrides, use base contents + return new ConfigurationModel(this._contents); + } + + let contents = {}; + for (const key of arrays.distinct([...Object.keys(this._contents), ...Object.keys(overrideContents)])) { + + let contentsForKey = this._contents[key]; + let overrideContentsForKey = overrideContents[key]; + + // If there are override contents for the key, clone and merge otherwise use base contents + if (overrideContentsForKey) { + // Clone and merge only if base contents and override contents are of type object otherwise just override + if (typeof contentsForKey === 'object' && typeof overrideContentsForKey === 'object') { + contentsForKey = objects.clone(contentsForKey); + merge(contentsForKey, overrideContentsForKey, true); + } else { + contentsForKey = overrideContentsForKey; + } + } + + contents[key] = contentsForKey; + } + return new ConfigurationModel(contents); + } + + public merge(other: ConfigurationModel, overwrite: boolean = true): ConfigurationModel { + const mergedModel = new ConfigurationModel(); + this.doMerge(mergedModel, this, overwrite); + this.doMerge(mergedModel, other, overwrite); + return mergedModel; + } + + protected doMerge(source: ConfigurationModel, target: ConfigurationModel, overwrite: boolean = true) { + merge(source.contents, objects.clone(target.contents), overwrite); + const overrides = objects.clone(source._overrides); + for (const override of target._overrides) { + const [sourceOverride] = overrides.filter(o => arrays.equals(o.identifiers, override.identifiers)); + if (sourceOverride) { + merge(sourceOverride.contents, override.contents, overwrite); + } else { + overrides.push(override); + } + } + source._overrides = overrides; + source._keys = arrays.distinct([...source._keys, ...target.keys]); + } + + private getContentsForOverrideIdentifer(identifier: string): any { + for (const override of this._overrides) { + if (override.identifiers.indexOf(identifier) !== -1) { + return override.contents; + } + } + return null; + } + + private addKey(key: string): void { + let index = this._keys.length; + for (let i = 0; i < index; i++) { + if (key.indexOf(this._keys[i]) === 0) { + index = i; + } + } + this._keys.splice(index, 1, key); + } + + private removeKey(key: string): boolean { + let index = this._keys.indexOf(key); + if (index !== -1) { + this._keys.splice(index, 1); + return true; + } + return false; + } + + toJSON(): IConfigurationModel { + return { + contents: this.contents, + overrides: this.overrides, + keys: this.keys + }; + } +} + +export class DefaultConfigurationModel extends ConfigurationModel { + + constructor() { + super(getDefaultValues()); + this._keys = getConfigurationKeys(); + this._overrides = Object.keys(this._contents) + .filter(key => OVERRIDE_PROPERTY_PATTERN.test(key)) + .map(key => { + return { + identifiers: [overrideIdentifierFromKey(key).trim()], + contents: toValuesTree(this._contents[key], message => console.error(`Conflict in default settings file: ${message}`)) + }; + }); + } + + public get keys(): string[] { + return this._keys; + } +} + +interface Overrides extends IOverrides { + raw: any; +} + +export class CustomConfigurationModel extends ConfigurationModel { + + protected _parseErrors: any[] = []; + + constructor(content: string = '', private name: string = '') { + super(); + if (content) { + this.update(content); + } + } + + public get errors(): any[] { + return this._parseErrors; + } + + public update(content: string): void { + let parsed: any = {}; + let overrides: Overrides[] = []; + let currentProperty: string = null; + let currentParent: any = []; + let previousParents: any[] = []; + let parseErrors: json.ParseError[] = []; + + function onValue(value: any) { + if (Array.isArray(currentParent)) { + (currentParent).push(value); + } else if (currentProperty) { + currentParent[currentProperty] = value; + } + if (OVERRIDE_PROPERTY_PATTERN.test(currentProperty)) { + onOverrideSettingsValue(currentProperty, value); + } + } + + function onOverrideSettingsValue(property: string, value: any): void { + overrides.push({ + identifiers: [overrideIdentifierFromKey(property).trim()], + raw: value, + contents: null + }); + } + + let visitor: json.JSONVisitor = { + onObjectBegin: () => { + let object = {}; + onValue(object); + previousParents.push(currentParent); + currentParent = object; + currentProperty = null; + }, + onObjectProperty: (name: string) => { + currentProperty = name; + }, + onObjectEnd: () => { + currentParent = previousParents.pop(); + }, + onArrayBegin: () => { + let array: any[] = []; + onValue(array); + previousParents.push(currentParent); + currentParent = array; + currentProperty = null; + }, + onArrayEnd: () => { + currentParent = previousParents.pop(); + }, + onLiteralValue: onValue, + onError: (error: json.ParseErrorCode) => { + parseErrors.push({ error: error }); + } + }; + if (content) { + try { + json.visit(content, visitor); + parsed = currentParent[0] || {}; + } catch (e) { + console.error(`Error while parsing settings file ${this.name}: ${e}`); + this._parseErrors = [e]; + } + } + this.processRaw(parsed); + + const configurationProperties = Registry.as(Extensions.Configuration).getConfigurationProperties(); + this._overrides = overrides.map(override => { + // Filter unknown and non-overridable properties + const raw = {}; + for (const key in override.raw) { + if (configurationProperties[key] && configurationProperties[key].overridable) { + raw[key] = override.raw[key]; + } + } + return { + identifiers: override.identifiers, + contents: toValuesTree(raw, message => console.error(`Conflict in settings file ${this.name}: ${message}`)) + }; + }); + } + + protected processRaw(raw: any): void { + this._contents = toValuesTree(raw, message => console.error(`Conflict in settings file ${this.name}: ${message}`)); + this._keys = Object.keys(raw); + } +} + +export class Configuration { + + private _globalConfiguration: ConfigurationModel; + private _workspaceConsolidatedConfiguration: ConfigurationModel; + protected _foldersConsolidatedConfigurations: StrictResourceMap; + + constructor(protected _defaults: ConfigurationModel, + protected _user: ConfigurationModel, + protected _workspaceConfiguration: ConfigurationModel = new ConfigurationModel(), + protected folders: StrictResourceMap = new StrictResourceMap(), + protected _memoryConfiguration: ConfigurationModel = new ConfigurationModel(), + protected _memoryConfigurationByResource: StrictResourceMap = new StrictResourceMap()) { + this.merge(); + } + + get defaults(): ConfigurationModel { + return this._defaults; + } + + get user(): ConfigurationModel { + return this._user; + } + + get workspace(): ConfigurationModel { + return this._workspaceConfiguration; + } + + protected merge(): void { + this._globalConfiguration = this._defaults.merge(this._user); + this.updateWorkspaceConsolidateConfiguration(); + this._foldersConsolidatedConfigurations = new StrictResourceMap(); + for (const folder of this.folders.keys()) { + this.mergeFolder(folder); + } + } + + private updateWorkspaceConsolidateConfiguration() { + this._workspaceConsolidatedConfiguration = this._globalConfiguration.merge(this._workspaceConfiguration).merge(this._memoryConfiguration); + } + + protected mergeFolder(folder: URI) { + this._foldersConsolidatedConfigurations.set(folder, this._workspaceConsolidatedConfiguration.merge(this.folders.get(folder))); + } + + getSection(section: string = '', overrides: IConfigurationOverrides, workspace: Workspace): C { + const configModel = this.getConsolidateConfigurationModel(overrides, workspace); + return objects.clone(section ? configModel.getSectionContents(section) : configModel.contents); + } + + getValue(key: string, overrides: IConfigurationOverrides, workspace: Workspace): any { + const consolidateConfigurationModel = this.getConsolidateConfigurationModel(overrides, workspace); + return objects.clone(getConfigurationValue(consolidateConfigurationModel.contents, key)); + } + + updateValue(key: string, value: any, overrides: IConfigurationOverrides = {}): void { + let memoryConfiguration: ConfigurationModel; + if (overrides.resource) { + memoryConfiguration = this._memoryConfigurationByResource.get(overrides.resource); + if (!memoryConfiguration) { + memoryConfiguration = new ConfigurationModel(); + this._memoryConfigurationByResource.set(overrides.resource, memoryConfiguration); + } + } else { + memoryConfiguration = this._memoryConfiguration; + } + + if (value === void 0) { + memoryConfiguration.removeValue(key); + } else { + memoryConfiguration.setValue(key, value); + } + + if (!overrides.resource) { + this.updateWorkspaceConsolidateConfiguration(); + } + } + + lookup(key: string, overrides: IConfigurationOverrides, workspace: Workspace): { + default: C, + user: C, + workspace: C, + workspaceFolder: C + memory?: C + value: C, + } { + const consolidateConfigurationModel = this.getConsolidateConfigurationModel(overrides, workspace); + const folderConfigurationModel = this.getFolderConfigurationModelForResource(overrides.resource, workspace); + const memoryConfigurationModel = overrides.resource ? this._memoryConfigurationByResource.get(overrides.resource) || this._memoryConfiguration : this._memoryConfiguration; + return objects.clone({ + default: getConfigurationValue(overrides.overrideIdentifier ? this._defaults.override(overrides.overrideIdentifier).contents : this._defaults.contents, key), + user: getConfigurationValue(overrides.overrideIdentifier ? this._user.override(overrides.overrideIdentifier).contents : this._user.contents, key), + workspace: workspace ? getConfigurationValue(overrides.overrideIdentifier ? this._workspaceConfiguration.override(overrides.overrideIdentifier).contents : this._workspaceConfiguration.contents, key) : void 0, //Check on workspace exists or not because _workspaceConfiguration is never null + workspaceFolder: folderConfigurationModel ? getConfigurationValue(overrides.overrideIdentifier ? folderConfigurationModel.override(overrides.overrideIdentifier).contents : folderConfigurationModel.contents, key) : void 0, + memory: getConfigurationValue(overrides.overrideIdentifier ? memoryConfigurationModel.override(overrides.overrideIdentifier).contents : memoryConfigurationModel.contents, key), + value: getConfigurationValue(consolidateConfigurationModel.contents, key) + }); + } + + keys(workspace: Workspace): { + default: string[]; + user: string[]; + workspace: string[]; + workspaceFolder: string[]; + } { + const folderConfigurationModel = this.getFolderConfigurationModelForResource(null, workspace); + return objects.clone({ + default: this._defaults.keys, + user: this._user.keys, + workspace: this._workspaceConfiguration.keys, + workspaceFolder: folderConfigurationModel ? folderConfigurationModel.keys : [] + }); + } + + private getConsolidateConfigurationModel(overrides: IConfigurationOverrides, workspace: Workspace): ConfigurationModel { + let configurationModel = this.getConsolidatedConfigurationModelForResource(overrides, workspace); + return overrides.overrideIdentifier ? configurationModel.override(overrides.overrideIdentifier) : configurationModel; + } + + private getConsolidatedConfigurationModelForResource({ resource }: IConfigurationOverrides, workspace: Workspace): ConfigurationModel { + if (!workspace) { + return this._globalConfiguration; + } + + if (!resource) { + return this._workspaceConsolidatedConfiguration; + } + + let consolidateConfiguration = this._workspaceConsolidatedConfiguration; + const root = workspace.getFolder(resource); + if (root) { + consolidateConfiguration = this._foldersConsolidatedConfigurations.get(root.uri) || this._workspaceConsolidatedConfiguration; + } + + const memoryConfigurationForResource = this._memoryConfigurationByResource.get(resource); + if (memoryConfigurationForResource) { + consolidateConfiguration = consolidateConfiguration.merge(memoryConfigurationForResource); + } + + return consolidateConfiguration; + } + + private getFolderConfigurationModelForResource(resource: URI, workspace: Workspace): ConfigurationModel { + if (!workspace || !resource) { + return null; + } + + const root = workspace.getFolder(resource); + return root ? this.folders.get(root.uri) : null; + } + + public toData(): IConfigurationData { + return { + defaults: { + contents: this._defaults.contents, + overrides: this._defaults.overrides, + keys: this._defaults.keys + }, + user: { + contents: this._user.contents, + overrides: this._user.overrides, + keys: this._user.keys + }, + workspace: { + contents: this._workspaceConfiguration.contents, + overrides: this._workspaceConfiguration.overrides, + keys: this._workspaceConfiguration.keys + }, + folders: this.folders.keys().reduce((result, folder) => { + const { contents, overrides, keys } = this.folders.get(folder); + result[folder.toString()] = { contents, overrides, keys }; + return result; + }, Object.create({})) + }; + } + + public static parse(data: IConfigurationData): Configuration { + const defaultConfiguration = Configuration.parseConfigurationModel(data.defaults); + const userConfiguration = Configuration.parseConfigurationModel(data.user); + const workspaceConfiguration = Configuration.parseConfigurationModel(data.workspace); + const folders: StrictResourceMap = Object.keys(data.folders).reduce((result, key) => { + result.set(URI.parse(key), Configuration.parseConfigurationModel(data.folders[key])); + return result; + }, new StrictResourceMap()); + return new Configuration(defaultConfiguration, userConfiguration, workspaceConfiguration, folders, new ConfigurationModel(), new StrictResourceMap()); + } + + private static parseConfigurationModel(model: IConfigurationModel): ConfigurationModel { + return new ConfigurationModel(model.contents, model.keys, model.overrides); + } +} + +export class AbstractConfigurationChangeEvent { + + protected doesConfigurationContains(configuration: ConfigurationModel, config: string): boolean { + let changedKeysTree = configuration.contents; + let requestedTree = toValuesTree({ [config]: true }, () => { }); + + let key; + while (typeof requestedTree === 'object' && (key = Object.keys(requestedTree)[0])) { // Only one key should present, since we added only one property + changedKeysTree = changedKeysTree[key]; + if (!changedKeysTree) { + return false; // Requested tree is not found + } + requestedTree = requestedTree[key]; + } + return true; + } + + protected updateKeys(configuration: ConfigurationModel, keys: string[], resource?: URI): void { + for (const key of keys) { + configuration.setValue(key, true); + } + } +} + +export class AllKeysConfigurationChangeEvent extends AbstractConfigurationChangeEvent implements IConfigurationChangeEvent { + + private _changedConfiguration: ConfigurationModel = null; + + constructor(readonly affectedKeys: string[], readonly source: ConfigurationTarget, readonly sourceConfig: any) { super(); } + + get changedConfiguration(): ConfigurationModel { + if (!this._changedConfiguration) { + this._changedConfiguration = new ConfigurationModel(); + this.updateKeys(this._changedConfiguration, this.affectedKeys); + } + return this._changedConfiguration; + } + + get changedConfigurationByResource(): StrictResourceMap { + return new StrictResourceMap(); + } + + affectsConfiguration(config: string, resource?: URI): boolean { + return this.doesConfigurationContains(this.changedConfiguration, config); + } +} + +export class ConfigurationChangeEvent extends AbstractConfigurationChangeEvent implements IConfigurationChangeEvent { + + private _source: ConfigurationTarget; + private _sourceConfig: any; + + constructor( + private _changedConfiguration: ConfigurationModel = new ConfigurationModel(), + private _changedConfigurationByResource: StrictResourceMap = new StrictResourceMap()) { + super(); + } + + get changedConfiguration(): IConfigurationModel { + return this._changedConfiguration; + } + + get changedConfigurationByResource(): StrictResourceMap { + return this._changedConfigurationByResource; + } + + change(event: ConfigurationChangeEvent): ConfigurationChangeEvent + change(keys: string[], resource?: URI): ConfigurationChangeEvent + change(arg1: any, arg2?: any): ConfigurationChangeEvent { + if (arg1 instanceof ConfigurationChangeEvent) { + this._changedConfiguration = this._changedConfiguration.merge(arg1._changedConfiguration); + for (const resource of arg1._changedConfigurationByResource.keys()) { + let changedConfigurationByResource = this.getOrSetChangedConfigurationForResource(resource); + changedConfigurationByResource = changedConfigurationByResource.merge(arg1._changedConfigurationByResource.get(resource)); + this._changedConfigurationByResource.set(resource, changedConfigurationByResource); + } + } else { + this.changeWithKeys(arg1, arg2); + } + return this; + } + + telemetryData(source: ConfigurationTarget, sourceConfig: any): ConfigurationChangeEvent { + this._source = source; + this._sourceConfig = sourceConfig; + return this; + } + + get affectedKeys(): string[] { + const keys = [...this._changedConfiguration.keys]; + this._changedConfigurationByResource.forEach(model => keys.push(...model.keys)); + return keys; + } + + get source(): ConfigurationTarget { + return this._source; + } + + get sourceConfig(): any { + return this._sourceConfig; + } + + affectsConfiguration(config: string, resource?: URI): boolean { + let configurationModelsToSearch: ConfigurationModel[] = [this._changedConfiguration]; + + if (resource) { + let model = this._changedConfigurationByResource.get(resource); + if (model) { + configurationModelsToSearch.push(model); + } + } else { + configurationModelsToSearch.push(...this._changedConfigurationByResource.values()); + } + + for (const configuration of configurationModelsToSearch) { + if (this.doesConfigurationContains(configuration, config)) { + return true; + } + } + + return false; + } + + private changeWithKeys(keys: string[], resource?: URI): void { + let changedConfiguration = resource ? this.getOrSetChangedConfigurationForResource(resource) : this._changedConfiguration; + this.updateKeys(changedConfiguration, keys); + } + + private getOrSetChangedConfigurationForResource(resource: URI): ConfigurationModel { + let changedConfigurationByResource = this._changedConfigurationByResource.get(resource); + if (!changedConfigurationByResource) { + changedConfigurationByResource = new ConfigurationModel(); + this._changedConfigurationByResource.set(resource, changedConfigurationByResource); + } + return changedConfigurationByResource; + } +} \ No newline at end of file diff --git a/src/vs/platform/configuration/common/configurationRegistry.ts b/src/vs/platform/configuration/common/configurationRegistry.ts index 52d651b43d7..c818c1f454f 100644 --- a/src/vs/platform/configuration/common/configurationRegistry.ts +++ b/src/vs/platform/configuration/common/configurationRegistry.ts @@ -38,10 +38,10 @@ export interface IConfigurationRegistry { registerDefaultConfigurations(defaultConfigurations: IDefaultConfigurationExtension[]): void; /** - * Event that fires whenver a configuratio has been + * Event that fires whenver a configuration has been * registered. */ - onDidRegisterConfiguration: Event; + onDidRegisterConfiguration: Event; /** * Returns all configuration nodes contributed to this registry. @@ -96,40 +96,39 @@ export const editorConfigurationSchemaId = 'vscode://schemas/settings/editor'; const contributionRegistry = Registry.as(JSONExtensions.JSONContribution); class ConfigurationRegistry implements IConfigurationRegistry { + private configurationContributors: IConfigurationNode[]; private configurationProperties: { [qualifiedKey: string]: IJSONSchema }; private editorConfigurationSchema: IJSONSchema; - private _onDidRegisterConfiguration: Emitter; private overrideIdentifiers: string[] = []; private overridePropertyPattern: string; + private _onDidRegisterConfiguration: Emitter = new Emitter(); + readonly onDidRegisterConfiguration: Event = this._onDidRegisterConfiguration.event; + constructor() { this.configurationContributors = []; this.editorConfigurationSchema = { properties: {}, patternProperties: {}, additionalProperties: false, errorMessage: 'Unknown editor configuration setting' }; - this._onDidRegisterConfiguration = new Emitter(); this.configurationProperties = {}; this.computeOverridePropertyPattern(); contributionRegistry.registerSchema(editorConfigurationSchemaId, this.editorConfigurationSchema); } - public get onDidRegisterConfiguration() { - return this._onDidRegisterConfiguration.event; - } - public registerConfiguration(configuration: IConfigurationNode, validate: boolean = true): void { this.registerConfigurations([configuration], validate); } public registerConfigurations(configurations: IConfigurationNode[], validate: boolean = true): void { + const properties: string[] = []; configurations.forEach(configuration => { - this.validateAndRegisterProperties(configuration, validate); // fills in defaults + properties.push(...this.validateAndRegisterProperties(configuration, validate)); // fills in defaults this.configurationContributors.push(configuration); this.registerJSONConfiguration(configuration); this.updateSchemaForOverrideSettingsConfiguration(configuration); }); - this._onDidRegisterConfiguration.fire(this); + this._onDidRegisterConfiguration.fire(properties); } public notifyConfigurationSchemaUpdated(configuration: IConfigurationNode) { @@ -165,9 +164,10 @@ class ConfigurationRegistry implements IConfigurationRegistry { } } - private validateAndRegisterProperties(configuration: IConfigurationNode, validate: boolean = true, scope: ConfigurationScope = ConfigurationScope.WINDOW, overridable: boolean = false) { + private validateAndRegisterProperties(configuration: IConfigurationNode, validate: boolean = true, scope: ConfigurationScope = ConfigurationScope.WINDOW, overridable: boolean = false): string[] { scope = configuration.scope !== void 0 && configuration.scope !== null ? configuration.scope : scope; overridable = configuration.overridable || overridable; + let propertyKeys = []; let properties = configuration.properties; if (properties) { for (let key in properties) { @@ -192,14 +192,16 @@ class ConfigurationRegistry implements IConfigurationRegistry { } // add to properties map this.configurationProperties[key] = properties[key]; + propertyKeys.push(key); } } let subNodes = configuration.allOf; if (subNodes) { for (let node of subNodes) { - this.validateAndRegisterProperties(node, validate, scope, overridable); + propertyKeys.push(...this.validateAndRegisterProperties(node, validate, scope, overridable)); } } + return propertyKeys; } validateProperty(property: string): boolean { @@ -314,4 +316,4 @@ export function validateProperty(property: string): string { return nls.localize('config.property.duplicate', "Cannot register '{0}'. This property is already registered.", property); } return null; -} +} \ No newline at end of file diff --git a/src/vs/platform/configuration/common/model.ts b/src/vs/platform/configuration/common/model.ts deleted file mode 100644 index 53b122efd0e..00000000000 --- a/src/vs/platform/configuration/common/model.ts +++ /dev/null @@ -1,202 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * 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 { Registry } from 'vs/platform/registry/common/platform'; -import * as json from 'vs/base/common/json'; -import { IConfigurationRegistry, Extensions, OVERRIDE_PROPERTY_PATTERN } from 'vs/platform/configuration/common/configurationRegistry'; -import { ConfigurationModel, IOverrides } from 'vs/platform/configuration/common/configuration'; - -export function getDefaultValues(): any { - const valueTreeRoot: any = Object.create(null); - const properties = Registry.as(Extensions.Configuration).getConfigurationProperties(); - - for (let key in properties) { - let value = properties[key].default; - addToValueTree(valueTreeRoot, key, value, message => console.error(`Conflict in default settings: ${message}`)); - } - - return valueTreeRoot; -} - -export function toValuesTree(properties: { [qualifiedKey: string]: any }, conflictReporter: (message: string) => void): any { - const root = Object.create(null); - - for (let key in properties) { - addToValueTree(root, key, properties[key], conflictReporter); - } - - return root; -} - -function addToValueTree(settingsTreeRoot: any, key: string, value: any, conflictReporter: (message: string) => void): void { - const segments = key.split('.'); - const last = segments.pop(); - - let curr = settingsTreeRoot; - for (let i = 0; i < segments.length; i++) { - let s = segments[i]; - let obj = curr[s]; - switch (typeof obj) { - case 'undefined': - obj = curr[s] = Object.create(null); - break; - case 'object': - break; - default: - conflictReporter(`Ignoring ${key} as ${segments.slice(0, i + 1).join('.')} is ${JSON.stringify(obj)}`); - return; - } - curr = obj; - }; - - if (typeof curr === 'object') { - curr[last] = value; // workaround https://github.com/Microsoft/vscode/issues/13606 - } else { - conflictReporter(`Ignoring ${key} as ${segments.join('.')} is ${JSON.stringify(curr)}`); - } -} - -export function getConfigurationKeys(): string[] { - const properties = Registry.as(Extensions.Configuration).getConfigurationProperties(); - - return Object.keys(properties); -} - -export class DefaultConfigurationModel extends ConfigurationModel { - - constructor() { - super(getDefaultValues()); - this._keys = getConfigurationKeys(); - this._overrides = Object.keys(this._contents) - .filter(key => OVERRIDE_PROPERTY_PATTERN.test(key)) - .map(key => { - return >{ - identifiers: [overrideIdentifierFromKey(key).trim()], - contents: toValuesTree(this._contents[key], message => console.error(`Conflict in default settings file: ${message}`)) - }; - }); - } - - public get keys(): string[] { - return this._keys; - } -} - -interface Overrides extends IOverrides { - raw: any; -} - -export class CustomConfigurationModel extends ConfigurationModel { - - protected _parseErrors: any[] = []; - - constructor(content: string = '', private name: string = '') { - super(); - if (content) { - this.update(content); - } - } - - public get errors(): any[] { - return this._parseErrors; - } - - public update(content: string): void { - let parsed: T = {}; - let overrides: Overrides[] = []; - let currentProperty: string = null; - let currentParent: any = []; - let previousParents: any[] = []; - let parseErrors: json.ParseError[] = []; - - function onValue(value: any) { - if (Array.isArray(currentParent)) { - (currentParent).push(value); - } else if (currentProperty) { - currentParent[currentProperty] = value; - } - if (OVERRIDE_PROPERTY_PATTERN.test(currentProperty)) { - onOverrideSettingsValue(currentProperty, value); - } - } - - function onOverrideSettingsValue(property: string, value: any): void { - overrides.push({ - identifiers: [overrideIdentifierFromKey(property).trim()], - raw: value, - contents: null - }); - } - - let visitor: json.JSONVisitor = { - onObjectBegin: () => { - let object = {}; - onValue(object); - previousParents.push(currentParent); - currentParent = object; - currentProperty = null; - }, - onObjectProperty: (name: string) => { - currentProperty = name; - }, - onObjectEnd: () => { - currentParent = previousParents.pop(); - }, - onArrayBegin: () => { - let array: any[] = []; - onValue(array); - previousParents.push(currentParent); - currentParent = array; - currentProperty = null; - }, - onArrayEnd: () => { - currentParent = previousParents.pop(); - }, - onLiteralValue: onValue, - onError: (error: json.ParseErrorCode) => { - parseErrors.push({ error: error }); - } - }; - if (content) { - try { - json.visit(content, visitor); - parsed = currentParent[0] || {}; - } catch (e) { - console.error(`Error while parsing settings file ${this.name}: ${e}`); - this._parseErrors = [e]; - } - } - this.processRaw(parsed); - - const configurationProperties = Registry.as(Extensions.Configuration).getConfigurationProperties(); - this._overrides = overrides.map>(override => { - // Filter unknown and non-overridable properties - const raw = {}; - for (const key in override.raw) { - if (configurationProperties[key] && configurationProperties[key].overridable) { - raw[key] = override.raw[key]; - } - } - return { - identifiers: override.identifiers, - contents: toValuesTree(raw, message => console.error(`Conflict in settings file ${this.name}: ${message}`)) - }; - }); - } - - protected processRaw(raw: T): void { - this._contents = toValuesTree(raw, message => console.error(`Conflict in settings file ${this.name}: ${message}`)); - this._keys = Object.keys(raw); - } -} - -export function overrideIdentifierFromKey(key: string): string { - return key.substring(1, key.length - 1); -} - -export function keyFromOverrideIdentifier(overrideIdentifier: string): string { - return `[${overrideIdentifier}]`; -} \ No newline at end of file diff --git a/src/vs/platform/configuration/node/configurationService.ts b/src/vs/platform/configuration/node/configurationService.ts index 8f6005c6430..43ad33bce80 100644 --- a/src/vs/platform/configuration/node/configurationService.ts +++ b/src/vs/platform/configuration/node/configurationService.ts @@ -4,26 +4,28 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ConfigWatcher } from 'vs/base/node/config'; import { Registry } from 'vs/platform/registry/common/platform'; import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry'; import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; -import { ConfigurationSource, IConfigurationService, IConfigurationServiceEvent, IConfigurationValue, IConfigurationKeys, ConfigurationModel, IConfigurationOverrides, Configuration, IConfigurationValues, IConfigurationData } from 'vs/platform/configuration/common/configuration'; -import { CustomConfigurationModel, DefaultConfigurationModel } from 'vs/platform/configuration/common/model'; +import { IConfigurationService, IConfigurationChangeEvent, IConfigurationOverrides, ConfigurationTarget, compare, isConfigurationOverrides, IConfigurationData } from 'vs/platform/configuration/common/configuration'; +import { CustomConfigurationModel, DefaultConfigurationModel, ConfigurationModel, Configuration, ConfigurationChangeEvent } from 'vs/platform/configuration/common/configurationModels'; import Event, { Emitter } from 'vs/base/common/event'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { onUnexpectedError } from 'vs/base/common/errors'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { equals } from 'vs/base/common/objects'; +import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; -export class ConfigurationService extends Disposable implements IConfigurationService, IDisposable { +export class ConfigurationService extends Disposable implements IConfigurationService, IDisposable { _serviceBrand: any; - private _configuration: Configuration; - private userConfigModelWatcher: ConfigWatcher>; + private _configuration: Configuration; + private userConfigModelWatcher: ConfigWatcher; - private _onDidUpdateConfiguration: Emitter = this._register(new Emitter()); - public readonly onDidUpdateConfiguration: Event = this._onDidUpdateConfiguration.event; + private _onDidChangeConfiguration: Emitter = this._register(new Emitter()); + readonly onDidChangeConfiguration: Event = this._onDidChangeConfiguration.event; constructor( @IEnvironmentService environmentService: IEnvironmentService @@ -31,70 +33,111 @@ export class ConfigurationService extends Disposable implements IConfiguratio super(); this.userConfigModelWatcher = new ConfigWatcher(environmentService.appSettingsPath, { - changeBufferDelay: 300, onError: error => onUnexpectedError(error), defaultConfig: new CustomConfigurationModel(null, environmentService.appSettingsPath), parse: (content: string, parseErrors: any[]) => { - const userConfigModel = new CustomConfigurationModel(content, environmentService.appSettingsPath); + changeBufferDelay: 300, onError: error => onUnexpectedError(error), defaultConfig: new CustomConfigurationModel(null, environmentService.appSettingsPath), parse: (content: string, parseErrors: any[]) => { + const userConfigModel = new CustomConfigurationModel(content, environmentService.appSettingsPath); parseErrors = [...userConfigModel.errors]; return userConfigModel; } }); this._register(this.userConfigModelWatcher); + this.reset(); + // Listeners - this._register(this.userConfigModelWatcher.onDidUpdateConfiguration(() => this.onConfigurationChange(ConfigurationSource.User))); - this._register(Registry.as(Extensions.Configuration).onDidRegisterConfiguration(() => this.onConfigurationChange(ConfigurationSource.Default))); + this._register(this.userConfigModelWatcher.onDidUpdateConfiguration(() => this.onDidUpdateConfigModel())); + this._register(Registry.as(Extensions.Configuration).onDidRegisterConfiguration(configurationProperties => this.onDidRegisterConfiguration(configurationProperties))); } - public configuration(): Configuration { - return this._configuration || (this._configuration = this.consolidateConfigurations()); + get configuration(): Configuration { + return this._configuration; } - private onConfigurationChange(source: ConfigurationSource): void { + getConfigurationData(): IConfigurationData { + return this.configuration.toData(); + } + + getConfiguration(): T + getConfiguration(section: string): T + getConfiguration(overrides: IConfigurationOverrides): T + getConfiguration(section: string, overrides: IConfigurationOverrides): T + getConfiguration(arg1?: any, arg2?: any): any { + const section = typeof arg1 === 'string' ? arg1 : void 0; + const overrides = isConfigurationOverrides(arg1) ? arg1 : isConfigurationOverrides(arg2) ? arg2 : {}; + return this.configuration.getSection(section, overrides, null); + } + + getValue(key: string, overrides: IConfigurationOverrides = {}): any { + return this.configuration.getValue(key, overrides, null); + } + + updateValue(key: string, value: any): TPromise + updateValue(key: string, value: any, overrides: IConfigurationOverrides): TPromise + updateValue(key: string, value: any, target: ConfigurationTarget): TPromise + updateValue(key: string, value: any, overrides: IConfigurationOverrides, target: ConfigurationTarget): TPromise + updateValue(key: string, value: any, arg3?: any, arg4?: any): TPromise { + return TPromise.wrapError(new Error('not supported')); + } + + inspect(key: string): { + default: T, + user: T, + workspace: T, + workspaceFolder: T + value: T + } { + return this.configuration.lookup(key, {}, null); + } + + keys(): { + default: string[]; + user: string[]; + workspace: string[]; + workspaceFolder: string[]; + } { + return this.configuration.keys(null); + } + + reloadConfiguration(folder?: IWorkspaceFolder): TPromise { + return folder ? TPromise.as(null) : + new TPromise((c, e) => this.userConfigModelWatcher.reload(() => c(this.onDidUpdateConfigModel()))); + } + + private onDidUpdateConfigModel(): void { + let changedKeys = []; + const { added, updated, removed } = compare(this._configuration.user, this.userConfigModelWatcher.getConfig()); + changedKeys = [...added, ...updated, ...removed]; + if (changedKeys.length) { + const oldConfiguartion = this._configuration; + this.reset(); + changedKeys = changedKeys.filter(key => !equals(oldConfiguartion.getValue(key, {}, null), this._configuration.getValue(key, {}, null))); + if (changedKeys.length) { + this.trigger(changedKeys, ConfigurationTarget.USER); + } + } + } + + private onDidRegisterConfiguration(keys: string[]): void { this.reset(); // reset our caches - - const cache = this.configuration(); - - this._onDidUpdateConfiguration.fire({ - source, - sourceConfig: source === ConfigurationSource.Default ? cache.defaults.contents : cache.user.contents - }); - } - - public reloadConfiguration(section?: string): TPromise { - return new TPromise(c => { - this.userConfigModelWatcher.reload(() => { - this.reset(); // reset our caches - c(this.getConfiguration(section)); - }); - }); - } - - public getConfiguration(section?: string, options?: IConfigurationOverrides): C { - return this.configuration().getValue(section, options); - } - - public lookup(key: string, overrides?: IConfigurationOverrides): IConfigurationValue { - return this.configuration().lookup(key, overrides); - } - - public keys(overrides?: IConfigurationOverrides): IConfigurationKeys { - return this.configuration().keys(overrides); - } - - public values(): IConfigurationValues { - return this._configuration.values(); - } - - public getConfigurationData(): IConfigurationData { - return this.configuration().toData(); + this.trigger(keys, ConfigurationTarget.DEFAULT); } private reset(): void { - this._configuration = this.consolidateConfigurations(); + const defaults = new DefaultConfigurationModel(); + const user = this.userConfigModelWatcher.getConfig(); + this._configuration = new Configuration(defaults, user); } - private consolidateConfigurations(): Configuration { - const defaults = new DefaultConfigurationModel(); - const user = this.userConfigModelWatcher.getConfig(); - return new Configuration(defaults, user); + private trigger(keys: string[], source: ConfigurationTarget): void { + this._onDidChangeConfiguration.fire(new ConfigurationChangeEvent().change(keys).telemetryData(source, this.getTargetConfiguration(source))); + } + + private getTargetConfiguration(target: ConfigurationTarget): any { + switch (target) { + case ConfigurationTarget.DEFAULT: + return this._configuration.defaults.contents; + case ConfigurationTarget.USER: + return this._configuration.user.contents; + } + return {}; } } \ No newline at end of file diff --git a/src/vs/platform/configuration/test/common/configuration.model.test.ts b/src/vs/platform/configuration/test/common/configuration.model.test.ts deleted file mode 100644 index 42b52919b33..00000000000 --- a/src/vs/platform/configuration/test/common/configuration.model.test.ts +++ /dev/null @@ -1,87 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * 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 assert from 'assert'; -import { ConfigurationModel } from 'vs/platform/configuration/common/configuration'; -import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; -import { Registry } from 'vs/platform/registry/common/platform'; - -suite('Configuration', () => { - - suiteSetup(() => { - Registry.as(Extensions.Configuration).registerConfiguration({ - 'id': 'a', - 'order': 1, - 'title': 'a', - 'type': 'object', - 'properties': { - 'a': { - 'description': 'a', - 'type': 'boolean', - 'default': true, - 'overridable': true - } - } - }); - }); - - test('simple merge', () => { - let base = new ConfigurationModel({ 'a': 1, 'b': 2 }); - let add = new ConfigurationModel({ 'a': 3, 'c': 4 }); - let result = base.merge(add); - assert.deepEqual(result.contents, { 'a': 3, 'b': 2, 'c': 4 }); - }); - - test('recursive merge', () => { - let base = new ConfigurationModel({ 'a': { 'b': 1 } }); - let add = new ConfigurationModel({ 'a': { 'b': 2 } }); - let result = base.merge(add); - assert.deepEqual(result.contents, { 'a': { 'b': 2 } }); - }); - - test('simple merge overrides', () => { - let base = new ConfigurationModel({ 'a': { 'b': 1 } }, [], [{ identifiers: ['c'], contents: { 'a': 2 } }]); - let add = new ConfigurationModel({ 'a': { 'b': 2 } }, [], [{ identifiers: ['c'], contents: { 'b': 2 } }]); - let result = base.merge(add); - assert.deepEqual(result.contents, { 'a': { 'b': 2 } }); - assert.deepEqual(result.overrides, [{ identifiers: ['c'], contents: { 'a': 2, 'b': 2 } }]); - }); - - test('recursive merge overrides', () => { - let base = new ConfigurationModel({ 'a': { 'b': 1 } }, [], [{ identifiers: ['c'], contents: { 'a': { 'd': 1 } } }]); - let add = new ConfigurationModel({ 'a': { 'b': 2 } }, [], [{ identifiers: ['c'], contents: { 'a': { 'e': 2 } } }]); - let result = base.merge(add); - assert.deepEqual(result.contents, { 'a': { 'b': 2 } }); - assert.deepEqual(result.overrides, [{ identifiers: ['c'], contents: { 'a': { 'd': 1, 'e': 2 } } }]); - }); - - test('merge ignore keys', () => { - let base = new ConfigurationModel({ 'a': 1, 'b': 2 }); - let add = new ConfigurationModel({ 'a': 3, 'c': 4 }); - let result = base.merge(add); - assert.deepEqual(result.keys, []); - }); - - test('Test contents while getting an existing property', () => { - let testObject = new ConfigurationModel({ 'a': 1 }); - assert.deepEqual(testObject.getContentsFor('a'), 1); - - testObject = new ConfigurationModel({ 'a': { 'b': 1 } }); - assert.deepEqual(testObject.getContentsFor('a'), { 'b': 1 }); - }); - - test('Test contents are undefined for non existing properties', () => { - const testObject = new ConfigurationModel({ awesome: true }); - - assert.deepEqual(testObject.getContentsFor('unknownproperty'), undefined); - }); - - test('Test override gives all content merged with overrides', () => { - const testObject = new ConfigurationModel({ 'a': 1, 'c': 1 }, [], [{ identifiers: ['b'], contents: { 'a': 2 } }]); - - assert.deepEqual(testObject.override('b').contents, { 'a': 2, 'c': 1 }); - }); -}); \ No newline at end of file diff --git a/src/vs/platform/configuration/test/common/configuration.test.ts b/src/vs/platform/configuration/test/common/configuration.test.ts index 11024298132..456df853e01 100644 --- a/src/vs/platform/configuration/test/common/configuration.test.ts +++ b/src/vs/platform/configuration/test/common/configuration.test.ts @@ -5,7 +5,7 @@ 'use strict'; import * as assert from 'assert'; -import { merge } from 'vs/platform/configuration/common/configuration'; +import { merge, removeFromValueTree } from 'vs/platform/configuration/common/configuration'; suite('Configuration', () => { @@ -18,5 +18,92 @@ suite('Configuration', () => { assert.deepEqual(base, { 'a': 1, 'b': 2, 'c': 4 }); }); + test('removeFromValueTree: remove a non existing key', () => { + let target = { 'a': { 'b': 2 } }; + + removeFromValueTree(target, 'c'); + + assert.deepEqual(target, { 'a': { 'b': 2 } }); + }); + + test('removeFromValueTree: remove a multi segmented key from an object that has only sub sections of the key', () => { + let target = { 'a': { 'b': 2 } }; + + removeFromValueTree(target, 'a.b.c'); + + assert.deepEqual(target, { 'a': { 'b': 2 } }); + }); + + test('removeFromValueTree: remove a single segemented key', () => { + let target = { 'a': 1 }; + + removeFromValueTree(target, 'a'); + + assert.deepEqual(target, {}); + }); + + test('removeFromValueTree: remove a single segemented key when its value is undefined', () => { + let target = { 'a': void 0 }; + + removeFromValueTree(target, 'a'); + + assert.deepEqual(target, {}); + }); + + test('removeFromValueTree: remove a multi segemented key when its value is undefined', () => { + let target = { 'a': { 'b': 1 } }; + + removeFromValueTree(target, 'a.b'); + + assert.deepEqual(target, {}); + }); + + test('removeFromValueTree: remove a multi segemented key when its value is array', () => { + let target = { 'a': { 'b': [1] } }; + + removeFromValueTree(target, 'a.b'); + + assert.deepEqual(target, {}); + }); + + test('removeFromValueTree: remove a multi segemented key first segment value is array', () => { + let target = { 'a': [1] }; + + removeFromValueTree(target, 'a.0'); + + assert.deepEqual(target, { 'a': [1] }); + }); + + test('removeFromValueTree: remove when key is the first segmenet', () => { + let target = { 'a': { 'b': 1 } }; + + removeFromValueTree(target, 'a'); + + assert.deepEqual(target, {}); + }); + + test('removeFromValueTree: remove a multi segemented key when the first node has more values', () => { + let target = { 'a': { 'b': { 'c': 1 }, 'd': 1 } }; + + removeFromValueTree(target, 'a.b.c'); + + assert.deepEqual(target, { 'a': { 'd': 1 } }); + }); + + test('removeFromValueTree: remove a multi segemented key when in between node has more values', () => { + let target = { 'a': { 'b': { 'c': { 'd': 1 }, 'd': 1 } } }; + + removeFromValueTree(target, 'a.b.c.d'); + + assert.deepEqual(target, { 'a': { 'b': { 'd': 1 } } }); + }); + + test('removeFromValueTree: remove a multi segemented key when the last but one node has more values', () => { + let target = { 'a': { 'b': { 'c': 1, 'd': 1 } } }; + + removeFromValueTree(target, 'a.b.c'); + + assert.deepEqual(target, { 'a': { 'b': { 'd': 1 } } }); + }); }); \ No newline at end of file diff --git a/src/vs/platform/configuration/test/common/configurationModels.test.ts b/src/vs/platform/configuration/test/common/configurationModels.test.ts new file mode 100644 index 00000000000..eadf6dd928e --- /dev/null +++ b/src/vs/platform/configuration/test/common/configurationModels.test.ts @@ -0,0 +1,539 @@ +/*--------------------------------------------------------------------------------------------- + * 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 assert from 'assert'; +import { ConfigurationModel, CustomConfigurationModel, DefaultConfigurationModel, ConfigurationChangeEvent, AllKeysConfigurationChangeEvent } from 'vs/platform/configuration/common/configurationModels'; +import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; +import { Registry } from 'vs/platform/registry/common/platform'; +import URI from 'vs/base/common/uri'; +import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; + +suite('ConfigurationModel', () => { + + test('setValue for a key that has no sections and not defined', () => { + let testObject = new ConfigurationModel({ 'a': { 'b': 1 } }, ['a.b']); + + testObject.setValue('f', 1); + + assert.deepEqual(testObject.contents, { 'a': { 'b': 1 }, 'f': 1 }); + assert.deepEqual(testObject.keys, ['a.b', 'f']); + }); + + test('setValue for a key that has no sections and defined', () => { + let testObject = new ConfigurationModel({ 'a': { 'b': 1 }, 'f': 1 }, ['a.b', 'f']); + + testObject.setValue('f', 3); + + assert.deepEqual(testObject.contents, { 'a': { 'b': 1 }, 'f': 3 }); + assert.deepEqual(testObject.keys, ['a.b', 'f']); + }); + + test('setValue for a key that has sections and not defined', () => { + let testObject = new ConfigurationModel({ 'a': { 'b': 1 }, 'f': 1 }, ['a.b', 'f']); + + testObject.setValue('b.c', 1); + + assert.deepEqual(testObject.contents, { 'a': { 'b': 1 }, 'b': { 'c': 1 }, 'f': 1 }); + assert.deepEqual(testObject.keys, ['a.b', 'f', 'b.c']); + }); + + test('setValue for a key that has sections and defined', () => { + let testObject = new ConfigurationModel({ 'a': { 'b': 1 }, 'b': { 'c': 1 }, 'f': 1 }, ['a.b', 'b.c', 'f']); + + testObject.setValue('b.c', 3); + + assert.deepEqual(testObject.contents, { 'a': { 'b': 1 }, 'b': { 'c': 3 }, 'f': 1 }); + assert.deepEqual(testObject.keys, ['a.b', 'b.c', 'f']); + }); + + test('setValue for a key that has sections and sub section not defined', () => { + let testObject = new ConfigurationModel({ 'a': { 'b': 1 }, 'f': 1 }, ['a.b', 'f']); + + testObject.setValue('a.c', 1); + + assert.deepEqual(testObject.contents, { 'a': { 'b': 1, 'c': 1 }, 'f': 1 }); + assert.deepEqual(testObject.keys, ['a.b', 'f', 'a.c']); + }); + + test('setValue for a key that has sections and sub section defined', () => { + let testObject = new ConfigurationModel({ 'a': { 'b': 1, 'c': 1 }, 'f': 1 }, ['a.b', 'a.c', 'f']); + + testObject.setValue('a.c', 3); + + assert.deepEqual(testObject.contents, { 'a': { 'b': 1, 'c': 3 }, 'f': 1 }); + assert.deepEqual(testObject.keys, ['a.b', 'a.c', 'f']); + }); + + test('setValue for a key that has sections and last section is added', () => { + let testObject = new ConfigurationModel({ 'a': { 'b': {} }, 'f': 1 }, ['a.b', 'f']); + + testObject.setValue('a.b.c', 1); + + assert.deepEqual(testObject.contents, { 'a': { 'b': { 'c': 1 } }, 'f': 1 }); + assert.deepEqual(testObject.keys, ['a.b.c', 'f']); + }); + + test('removeValue: remove a non existing key', () => { + let testObject = new ConfigurationModel({ 'a': { 'b': 2 } }, ['a.b']); + + testObject.removeValue('a.b.c'); + + assert.deepEqual(testObject.contents, { 'a': { 'b': 2 } }); + assert.deepEqual(testObject.keys, ['a.b']); + }); + + test('removeValue: remove a single segemented key', () => { + let testObject = new ConfigurationModel({ 'a': 1 }, ['a']); + + testObject.removeValue('a'); + + assert.deepEqual(testObject.contents, {}); + assert.deepEqual(testObject.keys, []); + }); + + test('removeValue: remove a multi segemented key', () => { + let testObject = new ConfigurationModel({ 'a': { 'b': 1 } }, ['a.b']); + + testObject.removeValue('a.b'); + + assert.deepEqual(testObject.contents, {}); + assert.deepEqual(testObject.keys, []); + }); + + test('setValueInOverrides adds to overrides if does not exist', () => { + let testObject = new ConfigurationModel({ 'a': 1, 'b': 1 }, ['a']); + + testObject.setValueInOverrides('or', 'a', 2); + + assert.deepEqual(testObject.overrides[0].contents, { 'a': 2 }); + assert.deepEqual(testObject.override('or').contents, { 'a': 2, 'b': 1 }); + }); + + test('setValueInOverrides adds to overrides if exist', () => { + let testObject = new ConfigurationModel({ 'a': 1, 'b': 1 }, ['a'], [{ identifiers: ['or'], contents: { 'a': 2 } }]); + + testObject.setValueInOverrides('or', 'a', 3); + + assert.deepEqual(testObject.overrides[0].contents, { 'a': 3 }); + assert.deepEqual(testObject.override('or').contents, { 'a': 3, 'b': 1 }); + }); + + test('setValueInOverrides adds a nested key to overrides if exist', () => { + let testObject = new ConfigurationModel({ 'a': 1, 'b': 1 }, ['a'], [{ identifiers: ['or'], contents: { 'a': { 'c': 1 } } }]); + + testObject.setValueInOverrides('or', 'a.c', 2); + + assert.deepEqual(testObject.overrides[0].contents, { 'a': { 'c': 2 } }); + assert.deepEqual(testObject.override('or').contents, { 'a': { 'c': 2 }, 'b': 1 }); + }); + + test('setValueInOverrides adds new overrides if exist', () => { + let testObject = new ConfigurationModel({ 'a': 1, 'b': 1 }, ['a'], [{ identifiers: ['or1'], contents: { 'a': 2 } }]); + + testObject.setValueInOverrides('or2', 'b', 2); + + assert.deepEqual(testObject.overrides[0].contents, { 'a': 2 }); + assert.deepEqual(testObject.overrides[1].contents, { 'b': 2 }); + assert.deepEqual(testObject.override('or1').contents, { 'a': 2, 'b': 1 }); + assert.deepEqual(testObject.override('or2').contents, { 'a': 1, 'b': 2 }); + }); + + test('get overriding configuration model for an existing identifier', () => { + let testObject = new ConfigurationModel( + { 'a': { 'b': 1 }, 'f': 1 }, [], + [{ identifiers: ['c'], contents: { 'a': { 'd': 1 } } }]); + + assert.deepEqual(testObject.override('c').contents, { 'a': { 'b': 1, 'd': 1 }, 'f': 1 }); + }); + + test('get overriding configuration model for an identifier that does not exist', () => { + let testObject = new ConfigurationModel( + { 'a': { 'b': 1 }, 'f': 1 }, [], + [{ identifiers: ['c'], contents: { 'a': { 'd': 1 } } }]); + + assert.deepEqual(testObject.override('xyz').contents, { 'a': { 'b': 1 }, 'f': 1 }); + }); + + test('get overriding configuration when one of the keys does not exist in base', () => { + let testObject = new ConfigurationModel( + { 'a': { 'b': 1 }, 'f': 1 }, [], + [{ identifiers: ['c'], contents: { 'a': { 'd': 1 }, 'g': 1 } }]); + + assert.deepEqual(testObject.override('c').contents, { 'a': { 'b': 1, 'd': 1 }, 'f': 1, 'g': 1 }); + }); + + test('get overriding configuration when one of the key in base is not of object type', () => { + let testObject = new ConfigurationModel( + { 'a': { 'b': 1 }, 'f': 1 }, [], + [{ identifiers: ['c'], contents: { 'a': { 'd': 1 }, 'f': { 'g': 1 } } }]); + + assert.deepEqual(testObject.override('c').contents, { 'a': { 'b': 1, 'd': 1 }, 'f': { 'g': 1 } }); + }); + + test('get overriding configuration when one of the key in overriding contents is not of object type', () => { + let testObject = new ConfigurationModel( + { 'a': { 'b': 1 }, 'f': { 'g': 1 } }, [], + [{ identifiers: ['c'], contents: { 'a': { 'd': 1 }, 'f': 1 } }]); + + assert.deepEqual(testObject.override('c').contents, { 'a': { 'b': 1, 'd': 1 }, 'f': 1 }); + }); + + test('get overriding configuration if the value of overriding identifier is not object', () => { + let testObject = new ConfigurationModel( + { 'a': { 'b': 1 }, 'f': { 'g': 1 } }, [], + [{ identifiers: ['c'], contents: 'abc' }]); + + assert.deepEqual(testObject.override('c').contents, { 'a': { 'b': 1 }, 'f': { 'g': 1 } }); + }); + + test('get overriding configuration if the value of overriding identifier is an empty object', () => { + let testObject = new ConfigurationModel( + { 'a': { 'b': 1 }, 'f': { 'g': 1 } }, [], + [{ identifiers: ['c'], contents: {} }]); + + assert.deepEqual(testObject.override('c').contents, { 'a': { 'b': 1 }, 'f': { 'g': 1 } }); + }); + + test('simple merge', () => { + let base = new ConfigurationModel({ 'a': 1, 'b': 2 }, ['a', 'b']); + let add = new ConfigurationModel({ 'a': 3, 'c': 4 }, ['a', 'c']); + let result = base.merge(add); + + assert.deepEqual(result.contents, { 'a': 3, 'b': 2, 'c': 4 }); + assert.deepEqual(result.keys, ['a', 'b', 'c']); + }); + + test('recursive merge', () => { + let base = new ConfigurationModel({ 'a': { 'b': 1 } }, ['a.b']); + let add = new ConfigurationModel({ 'a': { 'b': 2 } }, ['a.b']); + let result = base.merge(add); + + assert.deepEqual(result.contents, { 'a': { 'b': 2 } }); + assert.deepEqual(result.getSectionContents('a'), { 'b': 2 }); + assert.deepEqual(result.keys, ['a.b']); + }); + + test('simple merge overrides', () => { + let base = new ConfigurationModel({ 'a': { 'b': 1 } }, ['a.b'], [{ identifiers: ['c'], contents: { 'a': 2 } }]); + let add = new ConfigurationModel({ 'a': { 'b': 2 } }, ['a.b'], [{ identifiers: ['c'], contents: { 'b': 2 } }]); + let result = base.merge(add); + + assert.deepEqual(result.contents, { 'a': { 'b': 2 } }); + assert.deepEqual(result.overrides, [{ identifiers: ['c'], contents: { 'a': 2, 'b': 2 } }]); + assert.deepEqual(result.override('c').contents, { 'a': 2, 'b': 2 }); + assert.deepEqual(result.keys, ['a.b']); + }); + + test('recursive merge overrides', () => { + let base = new ConfigurationModel({ 'a': { 'b': 1 }, 'f': 1 }, ['a.b', 'f'], [{ identifiers: ['c'], contents: { 'a': { 'd': 1 } } }]); + let add = new ConfigurationModel({ 'a': { 'b': 2 } }, ['a.b'], [{ identifiers: ['c'], contents: { 'a': { 'e': 2 } } }]); + let result = base.merge(add); + + assert.deepEqual(result.contents, { 'a': { 'b': 2 }, 'f': 1 }); + assert.deepEqual(result.overrides, [{ identifiers: ['c'], contents: { 'a': { 'd': 1, 'e': 2 } } }]); + assert.deepEqual(result.override('c').contents, { 'a': { 'b': 2, 'd': 1, 'e': 2 }, 'f': 1 }); + assert.deepEqual(result.keys, ['a.b', 'f']); + }); + + test('Test contents while getting an existing property', () => { + let testObject = new ConfigurationModel({ 'a': 1 }); + assert.deepEqual(testObject.getSectionContents('a'), 1); + + testObject = new ConfigurationModel({ 'a': { 'b': 1 } }); + assert.deepEqual(testObject.getSectionContents('a'), { 'b': 1 }); + }); + + test('Test contents are undefined for non existing properties', () => { + const testObject = new ConfigurationModel({ awesome: true }); + + assert.deepEqual(testObject.getSectionContents('unknownproperty'), undefined); + }); + + test('Test override gives all content merged with overrides', () => { + const testObject = new ConfigurationModel({ 'a': 1, 'c': 1 }, [], [{ identifiers: ['b'], contents: { 'a': 2 } }]); + + assert.deepEqual(testObject.override('b').contents, { 'a': 2, 'c': 1 }); + }); +}); + +suite('CustomConfigurationModel', () => { + + suiteSetup(() => { + Registry.as(Extensions.Configuration).registerConfiguration({ + 'id': 'a', + 'order': 1, + 'title': 'a', + 'type': 'object', + 'properties': { + 'a': { + 'description': 'a', + 'type': 'boolean', + 'default': true, + 'overridable': true + } + } + }); + }); + + test('simple merge using models', () => { + let base = new CustomConfigurationModel(JSON.stringify({ 'a': 1, 'b': 2 })); + let add = new CustomConfigurationModel(JSON.stringify({ 'a': 3, 'c': 4 })); + let result = base.merge(add); + assert.deepEqual(result.contents, { 'a': 3, 'b': 2, 'c': 4 }); + }); + + test('simple merge with an undefined contents', () => { + let base = new CustomConfigurationModel(JSON.stringify({ 'a': 1, 'b': 2 })); + let add = new CustomConfigurationModel(null); + let result = base.merge(add); + assert.deepEqual(result.contents, { 'a': 1, 'b': 2 }); + + base = new CustomConfigurationModel(null); + add = new CustomConfigurationModel(JSON.stringify({ 'a': 1, 'b': 2 })); + result = base.merge(add); + assert.deepEqual(result.contents, { 'a': 1, 'b': 2 }); + + base = new CustomConfigurationModel(null); + add = new CustomConfigurationModel(null); + result = base.merge(add); + assert.deepEqual(result.contents, {}); + }); + + test('Recursive merge using config models', () => { + let base = new CustomConfigurationModel(JSON.stringify({ 'a': { 'b': 1 } })); + let add = new CustomConfigurationModel(JSON.stringify({ 'a': { 'b': 2 } })); + let result = base.merge(add); + assert.deepEqual(result.contents, { 'a': { 'b': 2 } }); + }); + + test('Test contents while getting an existing property', () => { + let testObject = new CustomConfigurationModel(JSON.stringify({ 'a': 1 })); + assert.deepEqual(testObject.getSectionContents('a'), 1); + + testObject = new CustomConfigurationModel(JSON.stringify({ 'a': { 'b': 1 } })); + assert.deepEqual(testObject.getSectionContents('a'), { 'b': 1 }); + }); + + test('Test contents are undefined for non existing properties', () => { + const testObject = new CustomConfigurationModel(JSON.stringify({ + awesome: true + })); + + assert.deepEqual(testObject.getSectionContents('unknownproperty'), undefined); + }); + + test('Test contents are undefined for undefined config', () => { + const testObject = new CustomConfigurationModel(null); + + assert.deepEqual(testObject.getSectionContents('unknownproperty'), undefined); + }); + + test('Test configWithOverrides gives all content merged with overrides', () => { + const testObject = new CustomConfigurationModel(JSON.stringify({ 'a': 1, 'c': 1, '[b]': { 'a': 2 } })); + + assert.deepEqual(testObject.override('b').contents, { 'a': 2, 'c': 1, '[b]': { 'a': 2 } }); + }); + + test('Test configWithOverrides gives empty contents', () => { + const testObject = new CustomConfigurationModel(null); + + assert.deepEqual(testObject.override('b').contents, {}); + }); + + test('Test update with empty data', () => { + const testObject = new CustomConfigurationModel(); + testObject.update(''); + + assert.deepEqual(testObject.contents, {}); + assert.deepEqual(testObject.keys, []); + + testObject.update(null); + + assert.deepEqual(testObject.contents, {}); + assert.deepEqual(testObject.keys, []); + + testObject.update(undefined); + + assert.deepEqual(testObject.contents, {}); + assert.deepEqual(testObject.keys, []); + }); + + test('Test registering the same property again', () => { + Registry.as(Extensions.Configuration).registerConfiguration({ + 'id': 'a', + 'order': 1, + 'title': 'a', + 'type': 'object', + 'properties': { + 'a': { + 'description': 'a', + 'type': 'boolean', + 'default': false, + } + } + }); + assert.equal(true, new DefaultConfigurationModel().getSectionContents('a')); + }); + + test('Test registering the language property', () => { + Registry.as(Extensions.Configuration).registerConfiguration({ + 'id': '[a]', + 'order': 1, + 'title': 'a', + 'type': 'object', + 'properties': { + '[a]': { + 'description': 'a', + 'type': 'boolean', + 'default': false, + } + } + }); + assert.equal(undefined, new DefaultConfigurationModel().getSectionContents('[a]')); + }); + +}); + +suite('ConfigurationChangeEvent', () => { + + test('changeEvent affecting keys for all resources', () => { + let testObject = new ConfigurationChangeEvent(); + + testObject.change(['window.zoomLevel', 'workbench.editor.enablePreview', 'files', '[markdown]']); + + assert.deepEqual(testObject.affectedKeys, ['window.zoomLevel', 'workbench.editor.enablePreview', 'files', '[markdown]']); + assert.ok(testObject.affectsConfiguration('window.zoomLevel')); + assert.ok(testObject.affectsConfiguration('window')); + assert.ok(testObject.affectsConfiguration('workbench.editor.enablePreview')); + assert.ok(testObject.affectsConfiguration('workbench.editor')); + assert.ok(testObject.affectsConfiguration('workbench')); + assert.ok(testObject.affectsConfiguration('files')); + assert.ok(!testObject.affectsConfiguration('files.exclude')); + assert.ok(testObject.affectsConfiguration('[markdown]')); + }); + + test('changeEvent affecting keys for resources', () => { + let testObject = new ConfigurationChangeEvent(); + + testObject.change(['window.title']); + testObject.change(['window.zoomLevel'], URI.file('file1')); + testObject.change(['workbench.editor.enablePreview'], URI.file('file2')); + testObject.change(['window.restoreFullscreen'], URI.file('file1')); + testObject.change(['window.restoreWindows'], URI.file('file2')); + + assert.deepEqual(testObject.affectedKeys, ['window.title', 'window.zoomLevel', 'window.restoreFullscreen', 'workbench.editor.enablePreview', 'window.restoreWindows']); + + assert.ok(testObject.affectsConfiguration('window.zoomLevel')); + assert.ok(testObject.affectsConfiguration('window.zoomLevel', URI.file('file1'))); + assert.ok(!testObject.affectsConfiguration('window.zoomLevel', URI.file('file2'))); + + assert.ok(testObject.affectsConfiguration('window.restoreFullscreen')); + assert.ok(testObject.affectsConfiguration('window.restoreFullscreen', URI.file('file1'))); + assert.ok(!testObject.affectsConfiguration('window.restoreFullscreen', URI.file('file2'))); + + assert.ok(testObject.affectsConfiguration('window.restoreWindows')); + assert.ok(testObject.affectsConfiguration('window.restoreWindows', URI.file('file2'))); + assert.ok(!testObject.affectsConfiguration('window.restoreWindows', URI.file('file1'))); + + assert.ok(testObject.affectsConfiguration('window.title')); + assert.ok(testObject.affectsConfiguration('window.title', URI.file('file1'))); + assert.ok(testObject.affectsConfiguration('window.title', URI.file('file2'))); + + assert.ok(testObject.affectsConfiguration('window')); + assert.ok(testObject.affectsConfiguration('window', URI.file('file1'))); + assert.ok(testObject.affectsConfiguration('window', URI.file('file2'))); + + assert.ok(testObject.affectsConfiguration('workbench.editor.enablePreview')); + assert.ok(testObject.affectsConfiguration('workbench.editor.enablePreview', URI.file('file2'))); + assert.ok(!testObject.affectsConfiguration('workbench.editor.enablePreview', URI.file('file1'))); + + assert.ok(testObject.affectsConfiguration('workbench.editor')); + assert.ok(testObject.affectsConfiguration('workbench.editor', URI.file('file2'))); + assert.ok(!testObject.affectsConfiguration('workbench.editor', URI.file('file1'))); + + assert.ok(testObject.affectsConfiguration('workbench')); + assert.ok(testObject.affectsConfiguration('workbench', URI.file('file2'))); + assert.ok(!testObject.affectsConfiguration('workbench', URI.file('file1'))); + + assert.ok(!testObject.affectsConfiguration('files')); + assert.ok(!testObject.affectsConfiguration('files', URI.file('file1'))); + assert.ok(!testObject.affectsConfiguration('files', URI.file('file2'))); + }); + + test('merging change events', () => { + let event1 = new ConfigurationChangeEvent().change(['window.zoomLevel', 'files']); + let event2 = new ConfigurationChangeEvent().change(['window.title'], URI.file('file1')).change(['[markdown]']); + + let actual = event1.change(event2); + + assert.deepEqual(actual.affectedKeys, ['window.zoomLevel', 'files', '[markdown]', 'window.title']); + + assert.ok(actual.affectsConfiguration('window.zoomLevel')); + assert.ok(actual.affectsConfiguration('window.zoomLevel', URI.file('file1'))); + assert.ok(actual.affectsConfiguration('window.zoomLevel', URI.file('file2'))); + + assert.ok(actual.affectsConfiguration('window')); + assert.ok(actual.affectsConfiguration('window', URI.file('file1'))); + assert.ok(actual.affectsConfiguration('window', URI.file('file2'))); + + assert.ok(actual.affectsConfiguration('files')); + assert.ok(actual.affectsConfiguration('files', URI.file('file1'))); + assert.ok(actual.affectsConfiguration('files', URI.file('file2'))); + + assert.ok(actual.affectsConfiguration('window.title')); + assert.ok(actual.affectsConfiguration('window.title', URI.file('file1'))); + assert.ok(!actual.affectsConfiguration('window.title', URI.file('file2'))); + + assert.ok(actual.affectsConfiguration('[markdown]')); + assert.ok(actual.affectsConfiguration('[markdown]', URI.file('file1'))); + assert.ok(actual.affectsConfiguration('[markdown]', URI.file('file2'))); + }); + +}); + +suite('AllKeysConfigurationChangeEvent', () => { + + test('changeEvent affects keys for any resource', () => { + let testObject = new AllKeysConfigurationChangeEvent(['window.title', 'window.zoomLevel', 'window.restoreFullscreen', 'workbench.editor.enablePreview', 'window.restoreWindows'], ConfigurationTarget.USER, null); + + assert.deepEqual(testObject.affectedKeys, ['window.title', 'window.zoomLevel', 'window.restoreFullscreen', 'workbench.editor.enablePreview', 'window.restoreWindows']); + + assert.ok(testObject.affectsConfiguration('window.zoomLevel')); + assert.ok(testObject.affectsConfiguration('window.zoomLevel', URI.file('file1'))); + assert.ok(testObject.affectsConfiguration('window.zoomLevel', URI.file('file2'))); + + assert.ok(testObject.affectsConfiguration('window.restoreFullscreen')); + assert.ok(testObject.affectsConfiguration('window.restoreFullscreen', URI.file('file1'))); + assert.ok(testObject.affectsConfiguration('window.restoreFullscreen', URI.file('file2'))); + + assert.ok(testObject.affectsConfiguration('window.restoreWindows')); + assert.ok(testObject.affectsConfiguration('window.restoreWindows', URI.file('file2'))); + assert.ok(testObject.affectsConfiguration('window.restoreWindows', URI.file('file1'))); + + assert.ok(testObject.affectsConfiguration('window.title')); + assert.ok(testObject.affectsConfiguration('window.title', URI.file('file1'))); + assert.ok(testObject.affectsConfiguration('window.title', URI.file('file2'))); + + assert.ok(testObject.affectsConfiguration('window')); + assert.ok(testObject.affectsConfiguration('window', URI.file('file1'))); + assert.ok(testObject.affectsConfiguration('window', URI.file('file2'))); + + assert.ok(testObject.affectsConfiguration('workbench.editor.enablePreview')); + assert.ok(testObject.affectsConfiguration('workbench.editor.enablePreview', URI.file('file2'))); + assert.ok(testObject.affectsConfiguration('workbench.editor.enablePreview', URI.file('file1'))); + + assert.ok(testObject.affectsConfiguration('workbench.editor')); + assert.ok(testObject.affectsConfiguration('workbench.editor', URI.file('file2'))); + assert.ok(testObject.affectsConfiguration('workbench.editor', URI.file('file1'))); + + assert.ok(testObject.affectsConfiguration('workbench')); + assert.ok(testObject.affectsConfiguration('workbench', URI.file('file2'))); + assert.ok(testObject.affectsConfiguration('workbench', URI.file('file1'))); + + assert.ok(!testObject.affectsConfiguration('files')); + assert.ok(!testObject.affectsConfiguration('files', URI.file('file1'))); + }); +}); \ No newline at end of file diff --git a/src/vs/platform/configuration/test/common/model.test.ts b/src/vs/platform/configuration/test/common/model.test.ts deleted file mode 100644 index aa9597b6702..00000000000 --- a/src/vs/platform/configuration/test/common/model.test.ts +++ /dev/null @@ -1,148 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * 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 assert from 'assert'; -import { CustomConfigurationModel, DefaultConfigurationModel } from 'vs/platform/configuration/common/model'; -import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; -import { Registry } from 'vs/platform/registry/common/platform'; - -suite('Configuration', () => { - - suiteSetup(() => { - Registry.as(Extensions.Configuration).registerConfiguration({ - 'id': 'a', - 'order': 1, - 'title': 'a', - 'type': 'object', - 'properties': { - 'a': { - 'description': 'a', - 'type': 'boolean', - 'default': true, - 'overridable': true - } - } - }); - }); - - test('simple merge using models', () => { - let base = new CustomConfigurationModel(JSON.stringify({ 'a': 1, 'b': 2 })); - let add = new CustomConfigurationModel(JSON.stringify({ 'a': 3, 'c': 4 })); - let result = base.merge(add); - assert.deepEqual(result.contents, { 'a': 3, 'b': 2, 'c': 4 }); - }); - - test('simple merge with an undefined contents', () => { - let base = new CustomConfigurationModel(JSON.stringify({ 'a': 1, 'b': 2 })); - let add = new CustomConfigurationModel(null); - let result = base.merge(add); - assert.deepEqual(result.contents, { 'a': 1, 'b': 2 }); - - base = new CustomConfigurationModel(null); - add = new CustomConfigurationModel(JSON.stringify({ 'a': 1, 'b': 2 })); - result = base.merge(add); - assert.deepEqual(result.contents, { 'a': 1, 'b': 2 }); - - base = new CustomConfigurationModel(null); - add = new CustomConfigurationModel(null); - result = base.merge(add); - assert.deepEqual(result.contents, {}); - }); - - test('Recursive merge using config models', () => { - let base = new CustomConfigurationModel(JSON.stringify({ 'a': { 'b': 1 } })); - let add = new CustomConfigurationModel(JSON.stringify({ 'a': { 'b': 2 } })); - let result = base.merge(add); - assert.deepEqual(result.contents, { 'a': { 'b': 2 } }); - }); - - test('Test contents while getting an existing property', () => { - let testObject = new CustomConfigurationModel(JSON.stringify({ 'a': 1 })); - assert.deepEqual(testObject.getContentsFor('a'), 1); - - testObject = new CustomConfigurationModel(JSON.stringify({ 'a': { 'b': 1 } })); - assert.deepEqual(testObject.getContentsFor('a'), { 'b': 1 }); - }); - - test('Test contents are undefined for non existing properties', () => { - const testObject = new CustomConfigurationModel(JSON.stringify({ - awesome: true - })); - - assert.deepEqual(testObject.getContentsFor('unknownproperty'), undefined); - }); - - test('Test contents are undefined for undefined config', () => { - const testObject = new CustomConfigurationModel(null); - - assert.deepEqual(testObject.getContentsFor('unknownproperty'), undefined); - }); - - test('Test configWithOverrides gives all content merged with overrides', () => { - const testObject = new CustomConfigurationModel(JSON.stringify({ 'a': 1, 'c': 1, '[b]': { 'a': 2 } })); - - assert.deepEqual(testObject.override('b').contents, { 'a': 2, 'c': 1, '[b]': { 'a': 2 } }); - }); - - test('Test configWithOverrides gives empty contents', () => { - const testObject = new CustomConfigurationModel(null); - - assert.deepEqual(testObject.override('b').contents, {}); - }); - - test('Test update with empty data', () => { - const testObject = new CustomConfigurationModel(); - testObject.update(''); - - assert.deepEqual(testObject.contents, {}); - assert.deepEqual(testObject.keys, []); - - testObject.update(null); - - assert.deepEqual(testObject.contents, {}); - assert.deepEqual(testObject.keys, []); - - testObject.update(undefined); - - assert.deepEqual(testObject.contents, {}); - assert.deepEqual(testObject.keys, []); - }); - - test('Test registering the same property again', () => { - Registry.as(Extensions.Configuration).registerConfiguration({ - 'id': 'a', - 'order': 1, - 'title': 'a', - 'type': 'object', - 'properties': { - 'a': { - 'description': 'a', - 'type': 'boolean', - 'default': false, - } - } - }); - assert.equal(true, new DefaultConfigurationModel().getContentsFor('a')); - }); - - test('Test registering the language property', () => { - Registry.as(Extensions.Configuration).registerConfiguration({ - 'id': '[a]', - 'order': 1, - 'title': 'a', - 'type': 'object', - 'properties': { - '[a]': { - 'description': 'a', - 'type': 'boolean', - 'default': false, - } - } - }); - assert.equal(undefined, new DefaultConfigurationModel().getContentsFor('[a]')); - }); - -}); \ No newline at end of file diff --git a/src/vs/platform/configuration/test/common/testConfigurationService.ts b/src/vs/platform/configuration/test/common/testConfigurationService.ts index aed4574d678..dcb88e2eb1d 100644 --- a/src/vs/platform/configuration/test/common/testConfigurationService.ts +++ b/src/vs/platform/configuration/test/common/testConfigurationService.ts @@ -9,8 +9,7 @@ import { TernarySearchTree } 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'; -import { getConfigurationKeys } from 'vs/platform/configuration/common/model'; -import { IConfigurationOverrides, IConfigurationService, getConfigurationValue, IConfigurationValue, IConfigurationKeys, IConfigurationValues, IConfigurationData, Configuration, ConfigurationModel } from 'vs/platform/configuration/common/configuration'; +import { getConfigurationKeys, IConfigurationOverrides, IConfigurationService, getConfigurationValue, isConfigurationOverrides } from 'vs/platform/configuration/common/configuration'; export class TestConfigurationService extends EventEmitter implements IConfigurationService { public _serviceBrand: any; @@ -19,11 +18,12 @@ export class TestConfigurationService extends EventEmitter implements IConfigura private configurationByRoot: TernarySearchTree = TernarySearchTree.forPaths(); - public reloadConfiguration(section?: string): TPromise { + public reloadConfiguration(): TPromise { return TPromise.as(this.getConfiguration()); } - public getConfiguration(section?: string, overrides?: IConfigurationOverrides): any { + public getConfiguration(arg1?: any, arg2?: any): C { + const overrides = isConfigurationOverrides(arg1) ? arg1 : isConfigurationOverrides(arg2) ? arg2 : void 0; if (overrides && overrides.resource) { const configForResource = this.configurationByRoot.findSubstr(overrides.resource.fsPath); return configForResource || this.configuration; @@ -32,8 +32,12 @@ export class TestConfigurationService extends EventEmitter implements IConfigura return this.configuration; } - public getConfigurationData(): IConfigurationData { - return new Configuration(new ConfigurationModel(), new ConfigurationModel(this.configuration)).toData(); + public getValue(key: string, overrides?: IConfigurationOverrides): any { + return this.inspect(key).value; + } + + public updateValue(key: string, overrides?: IConfigurationOverrides): TPromise { + return TPromise.as(null); } public setUserConfiguration(key: any, value: any, root?: URI): Thenable { @@ -48,32 +52,38 @@ export class TestConfigurationService extends EventEmitter implements IConfigura return TPromise.as(null); } - public onDidUpdateConfiguration() { + public onDidChangeConfiguration() { return { dispose() { } }; } - public lookup(key: string, overrides?: IConfigurationOverrides): IConfigurationValue { + public inspect(key: string, overrides?: IConfigurationOverrides): { + default: T, + user: T, + workspace: T, + workspaceFolder: T + value: T, + } { const config = this.getConfiguration(undefined, overrides); return { - value: getConfigurationValue(config, key), - default: getConfigurationValue(config, key), - user: getConfigurationValue(config, key), + value: getConfigurationValue(config, key), + default: getConfigurationValue(config, key), + user: getConfigurationValue(config, key), workspace: null, - folder: null + workspaceFolder: null }; } - public keys(): IConfigurationKeys { + public keys() { return { default: getConfigurationKeys(), user: Object.keys(this.configuration), workspace: [], - folder: [] + workspaceFolder: [] }; } - public values(): IConfigurationValues { - return {}; + public getConfigurationData() { + return null; } } diff --git a/src/vs/platform/configuration/test/node/configurationService.test.ts b/src/vs/platform/configuration/test/node/configurationService.test.ts index 594a6eb5287..60652d4b614 100644 --- a/src/vs/platform/configuration/test/node/configurationService.test.ts +++ b/src/vs/platform/configuration/test/node/configurationService.test.ts @@ -122,7 +122,8 @@ suite('ConfigurationService - Node', () => { assert.equal(config.foo, 'bar'); // force a reload to get latest - service.reloadConfiguration<{ foo: string }>().then(config => { + service.reloadConfiguration().then(() => { + config = service.getConfiguration<{ foo: string }>(); assert.ok(config); assert.equal(config.foo, 'changed'); @@ -202,12 +203,12 @@ suite('ConfigurationService - Node', () => { testFile((testFile, cleanUp) => { const service = new ConfigurationService(new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, testFile)); - let res = service.lookup('something.missing'); + let res = service.inspect('something.missing'); assert.strictEqual(res.value, void 0); assert.strictEqual(res.default, void 0); assert.strictEqual(res.user, void 0); - res = service.lookup('lookup.service.testSetting'); + res = service.inspect('lookup.service.testSetting'); assert.strictEqual(res.default, 'isSet'); assert.strictEqual(res.value, 'isSet'); assert.strictEqual(res.user, void 0); @@ -215,7 +216,7 @@ suite('ConfigurationService - Node', () => { fs.writeFileSync(testFile, '{ "lookup.service.testSetting": "bar" }'); return service.reloadConfiguration().then(() => { - res = service.lookup('lookup.service.testSetting'); + res = service.inspect('lookup.service.testSetting'); assert.strictEqual(res.default, 'isSet'); assert.strictEqual(res.user, 'bar'); assert.strictEqual(res.value, 'bar'); @@ -242,7 +243,7 @@ suite('ConfigurationService - Node', () => { testFile((testFile, cleanUp) => { const service = new ConfigurationService(new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, testFile)); - let res = service.lookup('lookup.service.testNullSetting'); + let res = service.inspect('lookup.service.testNullSetting'); assert.strictEqual(res.default, null); assert.strictEqual(res.value, null); assert.strictEqual(res.user, void 0); @@ -250,7 +251,7 @@ suite('ConfigurationService - Node', () => { fs.writeFileSync(testFile, '{ "lookup.service.testNullSetting": null }'); return service.reloadConfiguration().then(() => { - res = service.lookup('lookup.service.testNullSetting'); + res = service.inspect('lookup.service.testNullSetting'); assert.strictEqual(res.default, null); assert.strictEqual(res.value, null); assert.strictEqual(res.user, null); diff --git a/src/vs/platform/contextkey/browser/contextKeyService.ts b/src/vs/platform/contextkey/browser/contextKeyService.ts index df96f3c0c0d..f12ad44deeb 100644 --- a/src/vs/platform/contextkey/browser/contextKeyService.ts +++ b/src/vs/platform/contextkey/browser/contextKeyService.ts @@ -8,7 +8,7 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { KeybindingResolver } from 'vs/platform/keybinding/common/keybindingResolver'; import { IContextKey, IContext, IContextKeyServiceTarget, IContextKeyService, SET_CONTEXT_COMMAND_ID, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationService, IConfigurationChangeEvent, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import Event, { Emitter, debounceEvent } from 'vs/base/common/event'; const KEYBINDING_CONTEXT_ATTR = 'data-keybinding-context'; @@ -51,22 +51,42 @@ export class Context implements IContext { class ConfigAwareContextValuesContainer extends Context { - private _emitter: Emitter; - private _subscription: IDisposable; + private readonly _emitter: Emitter; + private readonly _subscription: IDisposable; + private readonly _configurationService: IConfigurationService; constructor(id: number, configurationService: IConfigurationService, emitter: Emitter) { super(id, null); this._emitter = emitter; - this._subscription = configurationService.onDidUpdateConfiguration(e => this._updateConfigurationContext(configurationService.getConfiguration())); - this._updateConfigurationContext(configurationService.getConfiguration()); + this._configurationService = configurationService; + this._subscription = configurationService.onDidChangeConfiguration(this._onConfigurationUpdated, this); + this._initFromConfiguration(); } public dispose() { this._subscription.dispose(); } - private _updateConfigurationContext(config: any) { + private _onConfigurationUpdated(event: IConfigurationChangeEvent): void { + if (event.source === ConfigurationTarget.DEFAULT) { + // new setting, rebuild everything + this._initFromConfiguration(); + } else { + // update those that we know + for (const configKey of event.affectedKeys) { + const contextKey = `config.${configKey}`; + if (contextKey in this._value) { + this._value[contextKey] = this._configurationService.getValue(configKey); + this._emitter.fire(configKey); + } + } + } + } + + private _initFromConfiguration() { + + const config = this._configurationService.getConfiguration(); // remove old config.xyz values for (let key in this._value) { diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts index dad4ea1cc5f..2dd0c004b87 100644 --- a/src/vs/platform/environment/common/environment.ts +++ b/src/vs/platform/environment/common/environment.ts @@ -44,6 +44,7 @@ export interface ParsedArgs { 'disable-telemetry'?: boolean; 'export-default-configuration'?: string; 'install-source'?: string; + 'disable-updates'?: string; } export const IEnvironmentService = createDecorator('environmentService'); @@ -106,4 +107,5 @@ export interface IEnvironmentService { nodeCachedDataDir: string; installSource: string; + disableUpdates: boolean; } diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index b0ee81d6fc6..fa7450947ca 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -49,7 +49,8 @@ const options: minimist.Opts = { 'nolazy', 'skip-getting-started', 'sticky-quickopen', - 'disable-telemetry' + 'disable-telemetry', + 'disable-updates' ], alias: { add: 'a', diff --git a/src/vs/platform/environment/node/environmentService.ts b/src/vs/platform/environment/node/environmentService.ts index d442c7ee2d5..607ec578b63 100644 --- a/src/vs/platform/environment/node/environmentService.ts +++ b/src/vs/platform/environment/node/environmentService.ts @@ -125,6 +125,8 @@ export class EnvironmentService implements IEnvironmentService { @memoize get nodeCachedDataDir(): string { return this.isBuilt ? path.join(this.userDataPath, 'CachedData', product.commit || new Array(41).join('0')) : undefined; } + get disableUpdates(): boolean { return !!this._args['disable-updates']; } + readonly machineUUID: string; readonly installSource: string; diff --git a/src/vs/platform/files/common/files.ts b/src/vs/platform/files/common/files.ts index abd11cd556a..543bc5045f3 100644 --- a/src/vs/platform/files/common/files.ts +++ b/src/vs/platform/files/common/files.ts @@ -588,6 +588,9 @@ export const HotExitConfiguration = { export const CONTENT_CHANGE_EVENT_BUFFER_DELAY = 1000; +export const FILES_ASSOCIATIONS_CONFIG = 'files.associations'; +export const FILES_EXCLUDE_CONFIG = 'files.exclude'; + export interface IFilesConfiguration { files: { associations: { [filepattern: string]: string }; diff --git a/src/vs/platform/instantiation/test/common/instantiationServiceMock.ts b/src/vs/platform/instantiation/test/common/instantiationServiceMock.ts index 68c03f70da4..fc7287a3167 100644 --- a/src/vs/platform/instantiation/test/common/instantiationServiceMock.ts +++ b/src/vs/platform/instantiation/test/common/instantiationServiceMock.ts @@ -50,7 +50,7 @@ export class TestInstantiationService extends InstantiationService { let property = typeof arg2 === 'string' ? arg2 : arg3; let value = typeof arg2 === 'string' ? arg3 : arg4; - let stubObject = this._create(serviceMock, { stub: true }); + let stubObject = this._create(serviceMock, { stub: true }, service && !property); if (property) { if (stubObject[property]) { if (stubObject[property].hasOwnProperty('restore')) { @@ -85,20 +85,20 @@ export class TestInstantiationService extends InstantiationService { return spy; } - private _create(serviceMock: IServiceMock, options: SinonOptions): any + private _create(serviceMock: IServiceMock, options: SinonOptions, reset?: boolean): any private _create(ctor: any, options: SinonOptions): any - private _create(arg1: any, options: SinonOptions): any { + private _create(arg1: any, options: SinonOptions, reset: boolean = false): any { if (this.isServiceMock(arg1)) { - let service = this._getOrCreateService(arg1, options); + let service = this._getOrCreateService(arg1, options, reset); this._serviceCollection.set(arg1.id, service); return service; } return options.mock ? sinon.mock(arg1) : this._createStub(arg1); } - private _getOrCreateService(serviceMock: IServiceMock, opts: SinonOptions): any { + private _getOrCreateService(serviceMock: IServiceMock, opts: SinonOptions, reset?: boolean): any { let service: any = this._serviceCollection.get(serviceMock.id); - if (service) { + if (!reset && service) { if (opts.mock && service['sinonOptions'] && !!service['sinonOptions'].mock) { return service; } diff --git a/src/vs/platform/quickOpen/common/quickOpen.ts b/src/vs/platform/quickOpen/common/quickOpen.ts index c0611d3dde4..0b3bf170855 100644 --- a/src/vs/platform/quickOpen/common/quickOpen.ts +++ b/src/vs/platform/quickOpen/common/quickOpen.ts @@ -123,6 +123,7 @@ export interface IInputOptions { export interface IShowOptions { quickNavigateConfiguration?: IQuickNavigateConfiguration; inputSelection?: { start: number; end: number; }; + autoFocus?: IAutoFocus; } export const IQuickOpenService = createDecorator('quickOpenService'); diff --git a/src/vs/platform/request/node/requestService.ts b/src/vs/platform/request/node/requestService.ts index de5f75fbbd6..a6e58846724 100644 --- a/src/vs/platform/request/node/requestService.ts +++ b/src/vs/platform/request/node/requestService.ts @@ -29,7 +29,7 @@ export class RequestService implements IRequestService { @IConfigurationService configurationService: IConfigurationService ) { this.configure(configurationService.getConfiguration()); - configurationService.onDidUpdateConfiguration(() => this.configure(configurationService.getConfiguration()), this, this.disposables); + configurationService.onDidChangeConfiguration(() => this.configure(configurationService.getConfiguration()), this, this.disposables); } private configure(config: IHTTPConfiguration) { diff --git a/src/vs/platform/search/common/search.ts b/src/vs/platform/search/common/search.ts index bb2a8df08e4..1abcff42a87 100644 --- a/src/vs/platform/search/common/search.ts +++ b/src/vs/platform/search/common/search.ts @@ -44,11 +44,18 @@ export interface ICommonQueryOptions { filePattern?: string; // file search only fileEncoding?: string; maxResults?: number; + /** + * If true no results will be returned. Instead `limitHit` will indicate if at least one result exists or not. + * + * Currently does not work with queries including a 'siblings clause'. + */ + exists?: boolean; sortByScore?: boolean; cacheKey?: string; useRipgrep?: boolean; disregardIgnoreFiles?: boolean; disregardExcludeSettings?: boolean; + ignoreSymlinks?: boolean; } export interface IQueryOptions extends ICommonQueryOptions { @@ -167,6 +174,7 @@ export interface ISearchConfiguration extends IFilesConfiguration { exclude: glob.IExpression; useRipgrep: boolean; useIgnoreFilesByDefault: boolean; + followSymlinks: boolean; }; editor: { wordSeparators: string; diff --git a/src/vs/platform/telemetry/common/telemetryService.ts b/src/vs/platform/telemetry/common/telemetryService.ts index 0ebdb46ff38..45c89aa945a 100644 --- a/src/vs/platform/telemetry/common/telemetryService.ts +++ b/src/vs/platform/telemetry/common/telemetryService.ts @@ -64,7 +64,7 @@ export class TelemetryService implements ITelemetryService { if (this._configurationService) { this._updateUserOptIn(); - this._configurationService.onDidUpdateConfiguration(this._updateUserOptIn, this, this._disposables); + this._configurationService.onDidChangeConfiguration(this._updateUserOptIn, this, this._disposables); /* __GDPR__ "optInStatus" : { "optIn" : { "classification": "SystemMetaData", "purpose": "BusinessInsight" } diff --git a/src/vs/platform/telemetry/common/telemetryUtils.ts b/src/vs/platform/telemetry/common/telemetryUtils.ts index a910df65983..cb438e51f4f 100644 --- a/src/vs/platform/telemetry/common/telemetryUtils.ts +++ b/src/vs/platform/telemetry/common/telemetryUtils.ts @@ -9,7 +9,7 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import { guessMimeTypes } from 'vs/base/common/mime'; import paths = require('vs/base/common/paths'); import URI from 'vs/base/common/uri'; -import { ConfigurationSource, IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { IKeybindingService, KeybindingSource } from 'vs/platform/keybinding/common/keybinding'; import { ILifecycleService, ShutdownReason } from 'vs/platform/lifecycle/common/lifecycle'; import { ITelemetryService, ITelemetryInfo, ITelemetryData } from 'vs/platform/telemetry/common/telemetry'; @@ -41,47 +41,20 @@ export const NullAppender: ITelemetryAppender = { log: () => null }; // --- util -export function anonymize(input: string): string { - if (!input) { - return input; - } - - let r = ''; - for (let i = 0; i < input.length; i++) { - let ch = input[i]; - if (ch >= '0' && ch <= '9') { - r += '0'; - continue; - } - if (ch >= 'a' && ch <= 'z') { - r += 'a'; - continue; - } - if (ch >= 'A' && ch <= 'Z') { - r += 'A'; - continue; - } - r += ch; - } - return r; -} - /* __GDPR__FRAGMENT__ "URIDescriptor" : { "mimeType" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "ext": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "path": { "classification": "CustomerContent", "purpose": "FeatureInsight" } + "ext": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } } */ export interface URIDescriptor { mimeType?: string; ext?: string; - path?: string; } export function telemetryURIDescriptor(uri: URI): URIDescriptor { const fsPath = uri && uri.fsPath; - return fsPath ? { mimeType: guessMimeTypes(fsPath).join(', '), ext: paths.extname(fsPath), path: anonymize(fsPath) } : {}; + return fsPath ? { mimeType: guessMimeTypes(fsPath).join(', '), ext: paths.extname(fsPath) } : {}; } /** @@ -185,8 +158,8 @@ const configurationValueWhitelist = [ ]; export function configurationTelemetry(telemetryService: ITelemetryService, configurationService: IConfigurationService): IDisposable { - return configurationService.onDidUpdateConfiguration(event => { - if (event.source !== ConfigurationSource.Default) { + return configurationService.onDidChangeConfiguration(event => { + if (event.source !== ConfigurationTarget.DEFAULT) { /* __GDPR__ "updateConfiguration" : { "configurationSource" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, @@ -194,7 +167,7 @@ export function configurationTelemetry(telemetryService: ITelemetryService, conf } */ telemetryService.publicLog('updateConfiguration', { - configurationSource: ConfigurationSource[event.source], + configurationSource: ConfigurationTarget[event.source], configurationKeys: flattenKeys(event.sourceConfig) }); /* __GDPR__ @@ -204,7 +177,7 @@ export function configurationTelemetry(telemetryService: ITelemetryService, conf } */ telemetryService.publicLog('updateConfigurationValues', { - configurationSource: ConfigurationSource[event.source], + configurationSource: ConfigurationTarget[event.source], configurationValues: flattenValues(event.sourceConfig, configurationValueWhitelist) }); } diff --git a/src/vs/platform/telemetry/test/electron-browser/telemetryService.test.ts b/src/vs/platform/telemetry/test/electron-browser/telemetryService.test.ts index 494159a5f56..69d2f789bb6 100644 --- a/src/vs/platform/telemetry/test/electron-browser/telemetryService.test.ts +++ b/src/vs/platform/telemetry/test/electron-browser/telemetryService.test.ts @@ -681,24 +681,25 @@ suite('TelemetryService', () => { enableTelemetry: enableTelemetry } as any; }, - getConfigurationData(): any { + getValue(key) { + return getConfigurationValue(this.getConfiguration(), key); + }, + updateValue() { return null; }, - reloadConfiguration() { - return TPromise.as(this.getConfiguration()); - }, - lookup(key: string) { + inspect(key: string) { return { value: getConfigurationValue(this.getConfiguration(), key), default: getConfigurationValue(this.getConfiguration(), key), user: getConfigurationValue(this.getConfiguration(), key), workspace: null, - folder: null + workspaceFolder: null }; }, - keys() { return { default: [], user: [], workspace: [], folder: [] }; }, - values() { return {}; }, - onDidUpdateConfiguration: emitter.event + keys() { return { default: [], user: [], workspace: [], workspaceFolder: [] }; }, + onDidChangeConfiguration: emitter.event, + reloadConfiguration() { return null; }, + getConfigurationData() { return null; } }); assert.equal(service.isOptedIn, false); diff --git a/src/vs/platform/theme/common/colorExtensionPoint.ts b/src/vs/platform/theme/common/colorExtensionPoint.ts index 51a47fdb4bb..d9e077db578 100644 --- a/src/vs/platform/theme/common/colorExtensionPoint.ts +++ b/src/vs/platform/theme/common/colorExtensionPoint.ts @@ -42,7 +42,7 @@ const configurationExtPoint = ExtensionsRegistry.registerExtensionPoint void): string { + + function normalizePath(path: string): string { + if (path && isMacintosh) { + path = normalizeNFC(path); // normalize paths returned from the OS + } + + return path; + } + if (callback) { - return remote.dialog.showSaveDialog(remote.getCurrentWindow(), options, callback); + return remote.dialog.showSaveDialog(remote.getCurrentWindow(), options, path => callback(normalizePath(path))); } - let path = remote.dialog.showSaveDialog(remote.getCurrentWindow(), options); // https://github.com/electron/electron/issues/4936 - - if (path && isMacintosh) { - path = normalizeNFC(path); // normalize paths returned from the OS - } - - return path; + return normalizePath(remote.dialog.showSaveDialog(remote.getCurrentWindow(), options)); // https://github.com/electron/electron/issues/4936 } showOpenDialog(options: Electron.OpenDialogOptions, callback?: (fileNames: string[]) => void): string[] { + + function normalizePaths(paths: string[]): string[] { + if (paths && paths.length > 0 && isMacintosh) { + paths = paths.map(path => normalizeNFC(path)); // normalize paths returned from the OS + } + + return paths; + } + if (callback) { - return remote.dialog.showOpenDialog(remote.getCurrentWindow(), options, callback); + return remote.dialog.showOpenDialog(remote.getCurrentWindow(), options, paths => callback(normalizePaths(paths))); } - let paths = remote.dialog.showOpenDialog(remote.getCurrentWindow(), options); // https://github.com/electron/electron/issues/4936 - - if (paths && paths.length > 0 && isMacintosh) { - paths = paths.map(path => normalizeNFC(path)); // normalize paths returned from the OS - } - - return paths; + return normalizePaths(remote.dialog.showOpenDialog(remote.getCurrentWindow(), options)); // https://github.com/electron/electron/issues/4936 } updateTouchBar(items: ICommandAction[][]): TPromise { diff --git a/src/vs/platform/windows/electron-main/windows.ts b/src/vs/platform/windows/electron-main/windows.ts index ba04b4766e7..7ce06d4a2f0 100644 --- a/src/vs/platform/windows/electron-main/windows.ts +++ b/src/vs/platform/windows/electron-main/windows.ts @@ -60,7 +60,7 @@ export interface IWindowsMainService { // methods ready(initialUserEnv: IProcessEnvironment): void; reload(win: ICodeWindow, cli?: ParsedArgs): void; - openWorkspace(win?: ICodeWindow): void; + openWorkspace(win?: ICodeWindow, options?: { forceNewWindow?: boolean }): void; createAndEnterWorkspace(win: ICodeWindow, folderPaths?: string[], path?: string): TPromise; saveAndEnterWorkspace(win: ICodeWindow, path: string): TPromise; closeWorkspace(win: ICodeWindow): void; diff --git a/src/vs/platform/workspace/common/workspace.ts b/src/vs/platform/workspace/common/workspace.ts index 04e273ff14c..78875f25837 100644 --- a/src/vs/platform/workspace/common/workspace.ts +++ b/src/vs/platform/workspace/common/workspace.ts @@ -13,6 +13,7 @@ import Event from 'vs/base/common/event'; 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'; +import { TPromise } from 'vs/base/common/winjs.base'; export const IWorkspaceContextService = createDecorator('contextService'); @@ -31,21 +32,6 @@ export interface IWorkspaceFoldersChangeEvent { export interface IWorkspaceContextService { _serviceBrand: any; - /** - * Provides access to the workspace object the platform is running with. This may be null if the workbench was opened - * without workspace (empty); - */ - getWorkspace(): IWorkspace; - - /** - * Return the state of the workbench. - * - * WorkbenchState.EMPTY - if the workbench was opened with empty window or file - * WorkbenchState.FOLDER - if the workbench was opened with a folder - * WorkbenchState.WORKSPACE - if the workbench was opened with a workspace - */ - getWorkbenchState(): WorkbenchState; - /** * An event which fires on workbench state changes. */ @@ -61,6 +47,31 @@ export interface IWorkspaceContextService { */ onDidChangeWorkspaceFolders: Event; + /** + * Provides access to the workspace object the platform is running with. This may be null if the workbench was opened + * without workspace (empty); + */ + getWorkspace(): IWorkspace; + + /** + * add folders to the existing workspace + */ + addFolders(folders: URI[]): TPromise; + + /** + * remove folders from the existing workspace + */ + removeFolders(folders: URI[]): TPromise; + + /** + * Return the state of the workbench. + * + * WorkbenchState.EMPTY - if the workbench was opened with empty window or file + * WorkbenchState.FOLDER - if the workbench was opened with a folder + * WorkbenchState.WORKSPACE - if the workbench was opened with a workspace + */ + getWorkbenchState(): WorkbenchState; + /** * 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. 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 3adce81a073..28c28f189d9 100644 --- a/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts +++ b/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts @@ -17,9 +17,10 @@ import { WorkspacesMainService, IStoredWorkspace } from 'vs/platform/workspaces/ import { WORKSPACE_EXTENSION, IWorkspaceSavedEvent, IWorkspaceIdentifier, IRawFileWorkspaceFolder, IRawUriWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces'; import { LogMainService } from 'vs/platform/log/common/log'; import URI from 'vs/base/common/uri'; +import { getRandomTestPath } from 'vs/workbench/test/workbenchTestServices'; suite('WorkspacesMainService', () => { - const parentDir = path.join(os.tmpdir(), 'vsctests', 'service'); + const parentDir = getRandomTestPath(os.tmpdir(), 'vsctests', 'workspacesservice'); const workspacesHome = path.join(parentDir, 'Workspaces'); class TestEnvironmentService extends EnvironmentService { @@ -359,14 +360,16 @@ suite('WorkspacesMainService', () => { assert.equal(1, untitled.length); assert.equal(untitledOne.id, untitled[0].id); - return service.createWorkspace([process.cwd(), os.tmpdir()]).then(untitledTwo => { + return service.createWorkspace([os.tmpdir(), process.cwd()]).then(untitledTwo => { untitled = service.getUntitledWorkspacesSync(); assert.equal(2, untitled.length); service.deleteUntitledWorkspaceSync(untitledOne); - service.deleteUntitledWorkspaceSync(untitledTwo); + untitled = service.getUntitledWorkspacesSync(); + assert.equal(1, untitled.length); + service.deleteUntitledWorkspaceSync(untitledTwo); untitled = service.getUntitledWorkspacesSync(); assert.equal(0, untitled.length); diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index dcf0d7b8852..1080c5e46e7 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -2996,6 +2996,133 @@ declare module 'vscode' { resolveDocumentLink?(link: DocumentLink, token: CancellationToken): ProviderResult; } + /** + * Represents a color in RGBA space. + */ + export class Color { + + /** + * The red component of this color in the range [0-1]. + */ + readonly red: number; + + /** + * The green component of this color in the range [0-1]. + */ + readonly green: number; + + /** + * The blue component of this color in the range [0-1]. + */ + readonly blue: number; + + /** + * The alpha component of this color in the range [0-1]. + */ + readonly alpha: number; + + /** + * Creates a new color instance. + * + * @param red The red component. + * @param green The green component. + * @param blue The bluew component. + * @param alpha The alpha component. + */ + constructor(red: number, green: number, blue: number, alpha: number); + } + + /** + * Represents a color range from a document. + */ + export class ColorInformation { + + /** + * The range in the document where this color appers. + */ + range: Range; + + /** + * The actual color value for this color range. + */ + color: Color; + + /** + * Creates a new color range. + * + * @param range The range the color appears in. Must not be empty. + * @param color The value of the color. + * @param format The format in which this color is currently formatted. + */ + constructor(range: Range, color: Color); + } + + /** + * A color presentation object describes how a [`color`](#Color) should be represented as text and what + * edits are required to refer to it from source code. + * + * For some languages one color can have multiple presentations, e.g. css can represent the color red with + * the constant `Red`, the hex-value `#ff0000`, or in rgba and hsla forms. In csharp other representations + * apply, e.g `System.Drawing.Color.Red`. + */ + export class ColorPresentation { + + /** + * The label of this color presentation. It will be shown on the color + * picker header. By default this is also the text that is inserted when selecting + * this color presentation. + */ + label: string; + + /** + * An [edit](#TextEdit) which is applied to a document when selecting + * this presentation for the color. When `falsy` the [label](#ColorPresentation.label) + * is used. + */ + textEdit?: TextEdit; + + /** + * An optional array of additional [text edits](#TextEdit) that are applied when + * 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); + } + + /** + * The document color provider defines the contract between extensions and feature of + * picking and modifying colors in the editor. + */ + export interface DocumentColorProvider { + + /** + * Provide colors for the given document. + * + * @param document The document in which the command was invoked. + * @param token A cancellation token. + * @return An array of [color informations](#ColorInformation) or a thenable that resolves to such. The lack of a result + * can be signaled by returning `undefined`, `null`, or an empty array. + */ + provideDocumentColors(document: TextDocument, token: CancellationToken): ProviderResult; + + /** + * Provide [representations](#ColorPresentation) for a color. + * + * @param color The color to show and insert. + * @param context A context object with additional information + * @param token A cancellation token. + * @return An array of color presentations or a thenable that resolves to such. The lack of a result + * can be signaled by returning `undefined`, `null`, or an empty array. + */ + provideColorPresentations(color: Color, context: { document: TextDocument, range: Range }, token: CancellationToken): ProviderResult; + } + /** * A tuple of two characters, like a pair of * opening and closing brackets. @@ -5589,6 +5716,19 @@ declare module 'vscode' { */ export function registerDocumentLinkProvider(selector: DocumentSelector, provider: DocumentLinkProvider): Disposable; + /** + * Register a color provider. + * + * Multiple providers can be registered for a language. In that case providers are asked in + * parallel and the results are merged. A failing provider (rejected promise or exception) will + * not cause a failure of the whole operation. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider A color provider. + * @return A [disposable](#Disposable) that unregisters this provider when being disposed. + */ + export function registerColorProvider(selector: DocumentSelector, provider: DocumentColorProvider): Disposable; + /** * Set a [language configuration](#LanguageConfiguration) for a language. * @@ -5659,12 +5799,6 @@ declare module 'vscode' { */ readonly tooltip?: string; - /** - * A color for a specific - * [source control resource state](#SourceControlResourceState). - */ - readonly color?: ThemeColor; - /** * The light theme decorations. */ @@ -5902,8 +6036,8 @@ declare module 'vscode' { /** * A debug configuration provider allows to add the initial debug configurations to a newly created launch.json - * and allows to resolve a launch configuration before it is used to start a new debug session. - * A debug configuration provider is registered via #workspace.registerDebugConfigurationProvider. + * and to resolve a launch configuration before it is used to start a new debug session. + * A debug configuration provider is registered via #debug.registerDebugConfigurationProvider. */ export interface DebugConfigurationProvider { /** @@ -5920,11 +6054,12 @@ declare module 'vscode' { * Resolves a [debug configuration](#DebugConfiguration) by filling in missing values or by adding/changing/removing attributes. * If more than one debug configuration provider is registered for the same type, the resolveDebugConfiguration calls are chained * in arbitrary order and the initial debug configuration is piped through the chain. + * Returning the value 'undefined' prevents the debug session from starting. * * @param folder The workspace folder from which the configuration originates from or undefined for a folderless setup. * @param debugConfiguration The [debug configuration](#DebugConfiguration) to resolve. * @param token A cancellation token. - * @return The resolved debug configuration. + * @return The resolved debug configuration or undefined. */ resolveDebugConfiguration?(folder: WorkspaceFolder | undefined, debugConfiguration: DebugConfiguration, token?: CancellationToken): ProviderResult; } diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index fd6f8cf68e3..3ff70ed91db 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -169,107 +169,25 @@ declare module 'vscode' { export function registerDiffInformationCommand(command: string, callback: (diff: LineChange[], ...args: any[]) => any, thisArg?: any): Disposable; } - /** - * Represents a color in RGBA space. - */ - export class Color { + //#region decorations - /** - * The red component of this color in the range [0-1]. - */ - readonly red: number; - - /** - * The green component of this color in the range [0-1]. - */ - readonly green: number; - - /** - * The blue component of this color in the range [0-1]. - */ - readonly blue: number; - - /** - * The alpha component of this color in the range [0-1]. - */ - readonly alpha: number; - - constructor(red: number, green: number, blue: number, alpha: number); + //todo@joh -> make class + export interface DecorationData { + priority?: number; + title?: string; + bubble?: boolean; + abbreviation?: string; + color?: ThemeColor; } - /** - * Represents a color range from a document. - */ - export class ColorInformation { - - /** - * The range in the document where this color appers. - */ - range: Range; - - /** - * The actual color value for this color range. - */ - color: Color; - - /** - * Creates a new color range. - * - * @param range The range the color appears in. Must not be empty. - * @param color The value of the color. - * @param format The format in which this color is currently formatted. - */ - constructor(range: Range, color: Color); + export interface DecorationProvider { + onDidChangeDecorations: Event; + provideDecoration(uri: Uri, token: CancellationToken): ProviderResult; } - export class ColorPresentation { - /** - * The label of this color presentation. It will be shown on the color - * picker header. By default this is also the text that is inserted when selecting - * this color presentation. - */ - label: string; - /** - * An [edit](#TextEdit) which is applied to a document when selecting - * this presentation for the color. When `falsy` the [label](#ColorPresentation.label) - * is used. - */ - textEdit?: TextEdit; - /** - * An optional array of additional [text edits](#TextEdit) that are applied when - * 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); + export namespace window { + export function registerDecorationProvider(provider: DecorationProvider): Disposable; } - /** - * The document color provider defines the contract between extensions and feature of - * picking and modifying colors in the editor. - */ - export interface DocumentColorProvider { - /** - * Provide colors for the given document. - * - * @param document The document in which the command was invoked. - * @param token A cancellation token. - * @return An array of [color informations](#ColorInformation) or a thenable that resolves to such. The lack of a result - * can be signaled by returning `undefined`, `null`, or an empty array. - */ - provideDocumentColors(document: TextDocument, token: CancellationToken): ProviderResult; - /** - * Provide representations for a color. - */ - provideColorPresentations(document: TextDocument, colorInfo: ColorInformation, token: CancellationToken): ProviderResult; - } - - export namespace languages { - export function registerColorProvider(selector: DocumentSelector, provider: DocumentColorProvider): Disposable; - } + //#endregion } diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts index d23f88532b1..0ebf0e9893d 100644 --- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts @@ -108,7 +108,8 @@ ExtensionsRegistry.registerExtensionPoint<{ [loc: string]: schema.IUserFriendlyV ctor: TreeView, location, when: ContextKeyExpr.deserialize(item.when), - canToggleVisibility: true + canToggleVisibility: true, + collapsed: true }; // validate diff --git a/src/vs/workbench/api/electron-browser/extensionHost.contribution.ts b/src/vs/workbench/api/electron-browser/extensionHost.contribution.ts index be065d33308..2940cda2e51 100644 --- a/src/vs/workbench/api/electron-browser/extensionHost.contribution.ts +++ b/src/vs/workbench/api/electron-browser/extensionHost.contribution.ts @@ -19,6 +19,7 @@ import './mainThreadCommands'; import './mainThreadConfiguration'; import './mainThreadCredentials'; import './mainThreadDebugService'; +import './mainThreadDecorations'; import './mainThreadDiagnostics'; import './mainThreadDialogs'; import './mainThreadDocumentContentProviders'; diff --git a/src/vs/workbench/api/electron-browser/mainThreadConfiguration.ts b/src/vs/workbench/api/electron-browser/mainThreadConfiguration.ts index 71f54113cfd..5133d7125a9 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadConfiguration.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadConfiguration.ts @@ -11,9 +11,9 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; -import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; -import { MainThreadConfigurationShape, MainContext, ExtHostContext, IExtHostContext } from '../node/extHost.protocol'; +import { MainThreadConfigurationShape, MainContext, ExtHostContext, IExtHostContext, IWorkspaceConfigurationChangeEventData } from '../node/extHost.protocol'; import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; +import { ConfigurationTarget, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; @extHostNamedCustomer(MainContext.MainThreadConfiguration) export class MainThreadConfiguration implements MainThreadConfigurationShape { @@ -22,14 +22,13 @@ export class MainThreadConfiguration implements MainThreadConfigurationShape { constructor( extHostContext: IExtHostContext, - @IConfigurationEditingService private readonly _configurationEditingService: IConfigurationEditingService, @IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService, - @IWorkspaceConfigurationService configurationService: IWorkspaceConfigurationService + @IWorkspaceConfigurationService private configurationService: IWorkspaceConfigurationService ) { const proxy = extHostContext.get(ExtHostContext.ExtHostConfiguration); - this._configurationListener = configurationService.onDidUpdateConfiguration(() => { - proxy.$acceptConfigurationChanged(configurationService.getConfigurationData()); + this._configurationListener = configurationService.onDidChangeConfiguration(e => { + proxy.$acceptConfigurationChanged(configurationService.getConfigurationData(), this.toConfigurationChangeEventData(e)); }); } @@ -47,16 +46,26 @@ export class MainThreadConfiguration implements MainThreadConfigurationShape { private writeConfiguration(target: ConfigurationTarget, key: string, value: any, resource: URI): TPromise { target = target !== null && target !== undefined ? target : this.deriveConfigurationTarget(key, resource); - return this._configurationEditingService.writeConfiguration(target, { key, value }, { donotNotifyError: true, scopes: { resource } }); + return this.configurationService.updateValue(key, value, { resource }, target, true); } private deriveConfigurationTarget(key: string, resource: URI): ConfigurationTarget { if (resource && this._workspaceContextService.getWorkbenchState() === WorkbenchState.WORKSPACE) { const configurationProperties = Registry.as(ConfigurationExtensions.Configuration).getConfigurationProperties(); if (configurationProperties[key] && configurationProperties[key].scope === ConfigurationScope.RESOURCE) { - return ConfigurationTarget.FOLDER; + return ConfigurationTarget.WORKSPACE_FOLDER; } } return ConfigurationTarget.WORKSPACE; } + + private toConfigurationChangeEventData(event: IConfigurationChangeEvent): IWorkspaceConfigurationChangeEventData { + return { + changedConfiguration: event.changedConfiguration, + changedConfigurationByResource: event.changedConfigurationByResource.keys().reduce((result, resource) => { + result[resource.toString()] = event.changedConfigurationByResource.get(resource); + return result; + }, Object.create({})) + }; + } } diff --git a/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts b/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts index 3776bf439c0..af2d72759b5 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts @@ -80,22 +80,6 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape { }); } - public $startDebugSession(folderUri: uri | undefined, configuration: IConfig): TPromise { - if (configuration.request !== 'launch' && configuration.request !== 'attach') { - return TPromise.wrapError(new Error(`only 'launch' or 'attach' allowed for 'request' attribute`)); - } - - const folder = folderUri ? this.contextService.getWorkspace().folders.filter(wf => wf.uri.toString() === folderUri.toString()).pop() : undefined; - return this.debugService.createProcess(folder, configuration).then(process => { - if (process) { - return process.getId(); - } - return TPromise.wrapError(new Error('cannot create debug session')); - }, err => { - return TPromise.wrapError(err && err.message ? err.message : 'cannot start debug session'); - }); - } - public $customDebugAdapterRequest(sessionId: DebugSessionUUID, request: string, args: any): TPromise { const process = this.debugService.findProcessByUUID(sessionId); if (process) { diff --git a/src/vs/workbench/api/electron-browser/mainThreadDecorations.ts b/src/vs/workbench/api/electron-browser/mainThreadDecorations.ts new file mode 100644 index 00000000000..7ac2b8c92ef --- /dev/null +++ b/src/vs/workbench/api/electron-browser/mainThreadDecorations.ts @@ -0,0 +1,67 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import URI from 'vs/base/common/uri'; +import { Emitter } from 'vs/base/common/event'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { ExtHostContext, MainContext, IExtHostContext, MainThreadDecorationsShape, ExtHostDecorationsShape } from '../node/extHost.protocol'; +import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; +import { IDecorationsService } from 'vs/workbench/services/decorations/browser/decorations'; + +@extHostNamedCustomer(MainContext.MainThreadDecorations) +export class MainThreadDecorations implements MainThreadDecorationsShape { + + private readonly _provider = new Map, IDisposable]>(); + private readonly _proxy: ExtHostDecorationsShape; + + constructor( + context: IExtHostContext, + @IDecorationsService private readonly _decorationsService: IDecorationsService + ) { + this._proxy = context.get(ExtHostContext.ExtHostDecorations); + } + + dispose() { + this._provider.forEach(value => dispose(value)); + this._provider.clear(); + } + + $registerDecorationProvider(handle: number): void { + let emitter = new Emitter(); + let registration = this._decorationsService.registerDecorationsProvider({ + label: 'extension-provider', + onDidChange: emitter.event, + provideDecorations: (uri) => { + return this._proxy.$providerDecorations(handle, uri).then(data => { + if (!data) { + return undefined; + } + const [weight, bubble, title, letter, themeColor] = data; + return { + weight: weight || 0, + bubble: bubble || false, + title, + letter, + color: themeColor && themeColor.id + }; + }); + } + }); + this._provider.set(handle, [emitter, registration]); + } + + $onDidChange(handle: number, resources: URI[]): void { + const [emitter] = this._provider.get(handle); + emitter.fire(resources); + } + + $unregisterDecorationProvider(handle: number): void { + if (this._provider.has(handle)) { + dispose(this._provider.get(handle)); + this._provider.delete(handle); + } + } +} diff --git a/src/vs/workbench/api/electron-browser/mainThreadEditor.ts b/src/vs/workbench/api/electron-browser/mainThreadEditor.ts index 0da01725b17..bc23649dbe8 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadEditor.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadEditor.ts @@ -244,6 +244,17 @@ export class MainThreadTextEditor { this._codeEditor.setDecorations(key, ranges); } + public setDecorationsFast(key: string, _ranges: number[]): void { + if (!this._codeEditor) { + return; + } + let ranges: Range[] = []; + for (let i = 0, len = Math.floor(_ranges.length / 4); i < len; i++) { + ranges[i] = new Range(_ranges[4 * i], _ranges[4 * i + 1], _ranges[4 * i + 2], _ranges[4 * i + 3]); + } + this._codeEditor.setDecorationsFast(key, ranges); + } + public revealRange(range: IRange, revealType: TextEditorRevealType): void { if (!this._codeEditor) { return; diff --git a/src/vs/workbench/api/electron-browser/mainThreadEditors.ts b/src/vs/workbench/api/electron-browser/mainThreadEditors.ts index df3174ae67c..6787006461b 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadEditors.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadEditors.ts @@ -195,6 +195,14 @@ export class MainThreadEditors implements MainThreadEditorsShape { return TPromise.as(null); } + $trySetDecorationsFast(id: string, key: string, ranges: string): TPromise { + if (!this._documentsAndEditors.getEditor(id)) { + return TPromise.wrapError(disposed(`TextEditor(${id})`)); + } + this._documentsAndEditors.getEditor(id).setDecorationsFast(key, /*TODO: marshaller is too slow*/JSON.parse(ranges)); + return TPromise.as(null); + } + $tryRevealRange(id: string, range: IRange, revealType: TextEditorRevealType): TPromise { if (!this._documentsAndEditors.getEditor(id)) { return TPromise.wrapError(disposed(`TextEditor(${id})`)); diff --git a/src/vs/workbench/api/electron-browser/mainThreadFileSystem.ts b/src/vs/workbench/api/electron-browser/mainThreadFileSystem.ts index 5b6ea8b6f3a..a786896b834 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadFileSystem.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadFileSystem.ts @@ -12,8 +12,8 @@ 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'; import { ISearchResultProvider, ISearchQuery, ISearchComplete, ISearchProgressItem, QueryType, IFileMatch, ISearchService } from 'vs/platform/search/common/search'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; @extHostNamedCustomer(MainContext.MainThreadFileSystem) export class MainThreadFileSystem implements MainThreadFileSystemShape { @@ -26,7 +26,7 @@ export class MainThreadFileSystem implements MainThreadFileSystemShape { extHostContext: IExtHostContext, @IFileService private readonly _fileService: IFileService, @ISearchService private readonly _searchService: ISearchService, - @IWorkspaceEditingService private readonly _workspaceEditService: IWorkspaceEditingService + @IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService ) { this._proxy = extHostContext.get(ExtHostContext.ExtHostFileSystem); } @@ -45,7 +45,7 @@ export class MainThreadFileSystem implements MainThreadFileSystemShape { } $onDidAddFileSystemRoot(uri: URI): void { - this._workspaceEditService.addFolders([uri]); + this._workspaceContextService.addFolders([uri]); } $onFileSystemChange(handle: number, changes: IFileChange[]): void { diff --git a/src/vs/workbench/api/electron-browser/mainThreadSCM.ts b/src/vs/workbench/api/electron-browser/mainThreadSCM.ts index 69b253930b7..64253cfa2d4 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadSCM.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadSCM.ts @@ -182,7 +182,7 @@ class MainThreadSCMProvider implements ISCMProvider { for (const [start, deleteCount, rawResources] of groupSlices) { const resources = rawResources.map(rawResource => { - const [handle, sourceUri, icons, tooltip, strikeThrough, faded, color] = rawResource; + const [handle, sourceUri, icons, tooltip, strikeThrough, faded] = rawResource; const icon = icons[0]; const iconDark = icons[1] || icon; const decorations = { @@ -190,8 +190,7 @@ class MainThreadSCMProvider implements ISCMProvider { iconDark: iconDark && URI.parse(iconDark), tooltip, strikeThrough, - faded, - color: color && color.id + faded }; return new MainThreadSCMResource( diff --git a/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts b/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts index 777ec4918af..d46aff79813 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts @@ -41,7 +41,7 @@ class TrimWhitespaceParticipant implements INamedSaveParticpant { } public participate(model: ITextFileEditorModel, env: { reason: SaveReason }): void { - if (this.configurationService.lookup('files.trimTrailingWhitespace', { overrideIdentifier: model.textEditorModel.getLanguageIdentifier().language, resource: model.getResource() }).value) { + if (this.configurationService.getValue('files.trimTrailingWhitespace', { overrideIdentifier: model.textEditorModel.getLanguageIdentifier().language, resource: model.getResource() })) { this.doTrimTrailingWhitespace(model.textEditorModel, env.reason === SaveReason.AUTO); } } @@ -99,7 +99,7 @@ export class FinalNewLineParticipant implements INamedSaveParticpant { } public participate(model: ITextFileEditorModel, env: { reason: SaveReason }): void { - if (this.configurationService.lookup('files.insertFinalNewline', { overrideIdentifier: model.textEditorModel.getLanguageIdentifier().language, resource: model.getResource() }).value) { + if (this.configurationService.getValue('files.insertFinalNewline', { overrideIdentifier: model.textEditorModel.getLanguageIdentifier().language, resource: model.getResource() })) { this.doInsertFinalNewLine(model.textEditorModel); } } @@ -139,7 +139,7 @@ export class TrimFinalNewLinesParticipant implements INamedSaveParticpant { } public participate(model: ITextFileEditorModel, env: { reason: SaveReason }): void { - if (this.configurationService.lookup('files.trimFinalNewlines', { overrideIdentifier: model.textEditorModel.getLanguageIdentifier().language, resource: model.getResource() }).value) { + if (this.configurationService.getValue('files.trimFinalNewlines', { overrideIdentifier: model.textEditorModel.getLanguageIdentifier().language, resource: model.getResource() })) { this.doTrimFinalNewLines(model.textEditorModel); } } @@ -189,7 +189,7 @@ class FormatOnSaveParticipant implements INamedSaveParticpant { const model = editorModel.textEditorModel; if (env.reason === SaveReason.AUTO - || !this._configurationService.lookup('editor.formatOnSave', { overrideIdentifier: model.getLanguageIdentifier().language, resource: editorModel.getResource() }).value) { + || !this._configurationService.getValue('editor.formatOnSave', { overrideIdentifier: model.getLanguageIdentifier().language, resource: editorModel.getResource() })) { return undefined; } diff --git a/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts b/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts index 69d00f909db..69e09705743 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts @@ -68,7 +68,7 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape { } const useRipgrep = folderQueries.every(folderQuery => { - const folderConfig = this._configurationService.getConfiguration(undefined, { resource: folderQuery.folder }); + const folderConfig = this._configurationService.getConfiguration({ resource: folderQuery.folder }); return folderConfig.search.useRipgrep; }); diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index 0df38e8773f..128eb4a907e 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -55,6 +55,7 @@ import { ProxyIdentifier } from 'vs/workbench/services/thread/common/threadServi 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'; +import { ExtHostDecorations } from 'vs/workbench/api/node/extHostDecorations'; export interface IExtensionApiFactory { (extension: IExtensionDescription): typeof vscode; @@ -83,6 +84,7 @@ export function createApiFactory( // Addressable instances const extHostHeapService = threadService.set(ExtHostContext.ExtHostHeapService, new ExtHostHeapService()); + const extHostDecorations = threadService.set(ExtHostContext.ExtHostDecorations, new ExtHostDecorations(threadService)); const extHostDocumentsAndEditors = threadService.set(ExtHostContext.ExtHostDocumentsAndEditors, new ExtHostDocumentsAndEditors(threadService)); const extHostDocuments = threadService.set(ExtHostContext.ExtHostDocuments, new ExtHostDocuments(threadService, extHostDocumentsAndEditors)); const extHostDocumentContentProviders = threadService.set(ExtHostContext.ExtHostDocumentContentProviders, new ExtHostDocumentContentProvider(threadService, extHostDocumentsAndEditors)); @@ -269,13 +271,12 @@ export function createApiFactory( registerDocumentLinkProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentLinkProvider): vscode.Disposable { return languageFeatures.registerDocumentLinkProvider(selector, provider); }, + registerColorProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentColorProvider): vscode.Disposable { + return languageFeatures.registerColorProvider(selector, provider); + }, setLanguageConfiguration: (language: string, configuration: vscode.LanguageConfiguration): vscode.Disposable => { return languageFeatures.setLanguageConfiguration(language, configuration); - }, - // proposed API - registerColorProvider: proposedApiFunction(extension, (selector: vscode.DocumentSelector, provider: vscode.DocumentColorProvider) => { - return languageFeatures.registerColorProvider(selector, provider); - }) + } }; // namespace: window @@ -377,6 +378,9 @@ export function createApiFactory( sampleFunction: proposedApiFunction(extension, () => { return extHostMessageService.showMessage(extension, Severity.Info, 'Hello Proposed Api!', {}, []); }), + registerDecorationProvider: proposedApiFunction(extension, (provider: vscode.DecorationProvider) => { + return extHostDecorations.registerDecorationProvider(provider, extension.id); + }) }; // namespace: workspace diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index e14bb827cf8..ff386cb652b 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -28,8 +28,7 @@ import * as editorCommon from 'vs/editor/common/editorCommon'; import * as modes from 'vs/editor/common/modes'; import { ITextSource } from 'vs/editor/common/model/textSource'; -import { ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; -import { IConfigurationData } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationData, ConfigurationTarget, IConfigurationModel } from 'vs/platform/configuration/common/configuration'; import { IPickOpenEntry, IPickOptions } from 'vs/platform/quickOpen/common/quickOpen'; import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles'; @@ -74,10 +73,15 @@ export interface IInitData { environment: IEnvironment; workspace: IWorkspaceData; extensions: IExtensionDescription[]; - configuration: IConfigurationData; + configuration: IConfigurationData; telemetryInfo: ITelemetryInfo; } +export interface IWorkspaceConfigurationChangeEventData { + changedConfiguration: IConfigurationModel; + changedConfigurationByResource: { [folder: string]: IConfigurationModel }; +} + export interface IExtHostContext { /** * Returns a proxy to an object addressable/named in the extension host process. @@ -136,6 +140,12 @@ export interface MainThreadDiaglogsShape extends IDisposable { $showSaveDialog(options: MainThreadDialogSaveOptions): TPromise; } +export interface MainThreadDecorationsShape extends IDisposable { + $registerDecorationProvider(handle: number, label: string): void; + $unregisterDecorationProvider(handle: number): void; + $onDidChange(handle: number, resources: URI[]): void; +} + export interface MainThreadDocumentContentProvidersShape extends IDisposable { $registerTextContentProvider(handle: number, scheme: string): void; $unregisterTextContentProvider(handle: number): void; @@ -210,6 +220,7 @@ export interface MainThreadEditorsShape extends IDisposable { $tryHideEditor(id: string): TPromise; $trySetOptions(id: string, options: ITextEditorConfigurationUpdate): TPromise; $trySetDecorations(id: string, key: string, ranges: editorCommon.IDecorationOptions[]): TPromise; + $trySetDecorationsFast(id: string, key: string, ranges: string): TPromise; $tryRevealRange(id: string, range: IRange, revealType: TextEditorRevealType): TPromise; $trySetSelections(id: string, selections: ISelection[]): TPromise; $tryApplyEdits(id: string, modelVersionId: number, edits: editorCommon.ISingleEditOperation[], opts: IApplyEditsOptions): TPromise; @@ -357,8 +368,7 @@ export type SCMRawResource = [ string[] /*icons: light, dark*/, string /*tooltip*/, boolean /*strike through*/, - boolean /*faded*/, - { id: string } /*ThemeColor*/ + boolean /*faded*/ ]; export type SCMRawResourceSplice = [ @@ -393,7 +403,6 @@ export interface MainThreadDebugServiceShape extends IDisposable { $registerDebugConfigurationProvider(type: string, hasProvideMethod: boolean, hasResolveMethod: boolean, handle: number): TPromise; $unregisterDebugConfigurationProvider(handle: number): TPromise; $startDebugging(folder: URI | undefined, nameOrConfig: string | vscode.DebugConfiguration): TPromise; - $startDebugSession(folder: URI | undefined, config: vscode.DebugConfiguration): TPromise; $customDebugAdapterRequest(id: DebugSessionUUID, command: string, args: any): TPromise; } @@ -415,7 +424,7 @@ export interface ExtHostCommandsShape { } export interface ExtHostConfigurationShape { - $acceptConfigurationChanged(data: IConfigurationData): void; + $acceptConfigurationChanged(data: IConfigurationData, eventData: IWorkspaceConfigurationChangeEventData): void; } export interface ExtHostDiagnosticsShape { @@ -598,6 +607,13 @@ export interface ExtHostDebugServiceShape { $acceptDebugSessionCustomEvent(id: DebugSessionUUID, type: string, name: string, event: any): void; } + +export type DecorationData = [number, boolean, string, string, ThemeColor]; + +export interface ExtHostDecorationsShape { + $providerDecorations(handle: number, uri: URI): TPromise; +} + export interface ExtHostCredentialsShape { } @@ -611,6 +627,7 @@ export const MainContext = { MainThreadCommands: createMainId('MainThreadCommands'), MainThreadConfiguration: createMainId('MainThreadConfiguration'), MainThreadDebugService: createMainId('MainThreadDebugService'), + MainThreadDecorations: createMainId('MainThreadDecorations'), MainThreadDiagnostics: createMainId('MainThreadDiagnostics'), MainThreadDialogs: createMainId('MainThreadDiaglogs'), MainThreadDocuments: createMainId('MainThreadDocuments'), @@ -642,6 +659,7 @@ export const ExtHostContext = { ExtHostConfiguration: createExtId('ExtHostConfiguration'), ExtHostDiagnostics: createExtId('ExtHostDiagnostics'), ExtHostDebugService: createExtId('ExtHostDebugService'), + ExtHostDecorations: createExtId('ExtHostDecorations'), ExtHostDocumentsAndEditors: createExtId('ExtHostDocumentsAndEditors'), ExtHostDocuments: createExtId('ExtHostDocuments'), ExtHostDocumentContentProviders: createExtId('ExtHostDocumentContentProviders'), diff --git a/src/vs/workbench/api/node/extHostConfiguration.ts b/src/vs/workbench/api/node/extHostConfiguration.ts index 931b01fc875..f7b42446d55 100644 --- a/src/vs/workbench/api/node/extHostConfiguration.ts +++ b/src/vs/workbench/api/node/extHostConfiguration.ts @@ -7,12 +7,14 @@ import { mixin } from 'vs/base/common/objects'; import URI from 'vs/base/common/uri'; import Event, { Emitter } from 'vs/base/common/event'; -import { WorkspaceConfiguration } from 'vscode'; +import * as vscode from 'vscode'; import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace'; -import { ExtHostConfigurationShape, MainThreadConfigurationShape } from './extHost.protocol'; +import { ExtHostConfigurationShape, MainThreadConfigurationShape, IWorkspaceConfigurationChangeEventData } from './extHost.protocol'; import { ConfigurationTarget as ExtHostConfigurationTarget } from './extHostTypes'; -import { IConfigurationData, Configuration } from 'vs/platform/configuration/common/configuration'; -import { ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; +import { IConfigurationData, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; +import { Configuration, ConfigurationModel, ConfigurationChangeEvent } from 'vs/platform/configuration/common/configurationModels'; +import { WorkspaceConfigurationChangeEvent } from 'vs/workbench/services/configuration/common/configurationModels'; +import { StrictResourceMap } from 'vs/base/common/map'; function lookUp(tree: any, key: string) { if (key) { @@ -38,27 +40,27 @@ export class ExtHostConfiguration implements ExtHostConfigurationShape { private readonly _onDidChangeConfiguration = new Emitter(); private readonly _proxy: MainThreadConfigurationShape; private readonly _extHostWorkspace: ExtHostWorkspace; - private _configuration: Configuration; + private _configuration: Configuration; - constructor(proxy: MainThreadConfigurationShape, extHostWorkspace: ExtHostWorkspace, data: IConfigurationData) { + constructor(proxy: MainThreadConfigurationShape, extHostWorkspace: ExtHostWorkspace, data: IConfigurationData) { this._proxy = proxy; this._extHostWorkspace = extHostWorkspace; - this._configuration = Configuration.parse(data, extHostWorkspace.workspace); + this._configuration = Configuration.parse(data); } get onDidChangeConfiguration(): Event { return this._onDidChangeConfiguration && this._onDidChangeConfiguration.event; } - $acceptConfigurationChanged(data: IConfigurationData) { - this._configuration = Configuration.parse(data, this._extHostWorkspace.workspace); + $acceptConfigurationChanged(data: IConfigurationData, eventData: IWorkspaceConfigurationChangeEventData) { + this._configuration = Configuration.parse(data); this._onDidChangeConfiguration.fire(undefined); } - getConfiguration(section?: string, resource?: URI): WorkspaceConfiguration { + getConfiguration(section?: string, resource?: URI): vscode.WorkspaceConfiguration { const config = section - ? lookUp(this._configuration.getValue(null, { resource }), section) - : this._configuration.getValue(null, { resource }); + ? lookUp(this._configuration.getSection(null, { resource }, this._extHostWorkspace.workspace), section) + : this._configuration.getSection(null, { resource }, this._extHostWorkspace.workspace); function parseConfigurationTarget(arg: boolean | ExtHostConfigurationTarget): ConfigurationTarget { if (arg === void 0 || arg === null) { @@ -71,11 +73,11 @@ export class ExtHostConfiguration implements ExtHostConfigurationShape { switch (arg) { case ExtHostConfigurationTarget.Global: return ConfigurationTarget.USER; case ExtHostConfigurationTarget.Workspace: return ConfigurationTarget.WORKSPACE; - case ExtHostConfigurationTarget.WorkspaceFolder: return ConfigurationTarget.FOLDER; + case ExtHostConfigurationTarget.WorkspaceFolder: return ConfigurationTarget.WORKSPACE_FOLDER; } } - const result: WorkspaceConfiguration = { + const result: vscode.WorkspaceConfiguration = { has(key: string): boolean { return typeof lookUp(config, key) !== 'undefined'; }, @@ -97,14 +99,14 @@ export class ExtHostConfiguration implements ExtHostConfigurationShape { }, inspect: (key: string): ConfigurationInspect => { key = section ? `${section}.${key}` : key; - const config = this._configuration.lookup(key, { resource }); + const config = this._configuration.lookup(key, { resource }, this._extHostWorkspace.workspace); if (config) { return { key, defaultValue: config.default, globalValue: config.user, workspaceValue: config.workspace, - workspaceFolderValue: config.folder + workspaceFolderValue: config.workspaceFolder }; } return undefined; @@ -115,6 +117,18 @@ export class ExtHostConfiguration implements ExtHostConfigurationShape { mixin(result, config, false); } - return Object.freeze(result); + return Object.freeze(result); + } + + protected toConfigurationChangeEvent(data: IWorkspaceConfigurationChangeEventData): WorkspaceConfigurationChangeEvent { + const changedConfiguration = new ConfigurationModel(data.changedConfiguration.contents, data.changedConfiguration.keys, data.changedConfiguration.overrides); + const changedConfigurationByResource: StrictResourceMap = new StrictResourceMap(); + for (const key of Object.keys(data.changedConfigurationByResource)) { + const resource = URI.parse(key); + const model = data.changedConfigurationByResource[key]; + changedConfigurationByResource.set(resource, new ConfigurationModel(model.contents, model.keys, model.overrides)); + } + const event = new ConfigurationChangeEvent(changedConfiguration, changedConfigurationByResource); + return new WorkspaceConfigurationChangeEvent(event, this._extHostWorkspace.workspace); } } diff --git a/src/vs/workbench/api/node/extHostDebugService.ts b/src/vs/workbench/api/node/extHostDebugService.ts index 0e27ea89109..71727e711d5 100644 --- a/src/vs/workbench/api/node/extHostDebugService.ts +++ b/src/vs/workbench/api/node/extHostDebugService.ts @@ -98,14 +98,6 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { return this._debugServiceProxy.$startDebugging(folder ? folder.uri : undefined, nameOrConfig); } - public startDebugSession(folder: vscode.WorkspaceFolder | undefined, config: vscode.DebugConfiguration): TPromise { - return this._debugServiceProxy.$startDebugSession(folder ? folder.uri : undefined, config).then((id: DebugSessionUUID) => { - const debugSession = new ExtHostDebugSession(this._debugServiceProxy, id, config.type, config.name); - this._debugSessions.set(id, debugSession); - return debugSession; - }); - } - public $acceptDebugSessionStarted(id: DebugSessionUUID, type: string, name: string): void { let debugSession = this._debugSessions.get(id); diff --git a/src/vs/workbench/api/node/extHostDecorations.ts b/src/vs/workbench/api/node/extHostDecorations.ts new file mode 100644 index 00000000000..e327dbe4995 --- /dev/null +++ b/src/vs/workbench/api/node/extHostDecorations.ts @@ -0,0 +1,47 @@ +/*--------------------------------------------------------------------------------------------- + * 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 vscode from 'vscode'; +import URI from 'vs/base/common/uri'; +import { MainContext, IMainContext, ExtHostDecorationsShape, MainThreadDecorationsShape, DecorationData } from 'vs/workbench/api/node/extHost.protocol'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { Disposable } from 'vs/workbench/api/node/extHostTypes'; +import { asWinJsPromise } from 'vs/base/common/async'; + +export class ExtHostDecorations implements ExtHostDecorationsShape { + + private static _handlePool = 0; + + private readonly _provider = new Map(); + private readonly _proxy: MainThreadDecorationsShape; + + constructor(mainContext: IMainContext) { + this._proxy = mainContext.get(MainContext.MainThreadDecorations); + } + + registerDecorationProvider(provider: vscode.DecorationProvider, label: string): vscode.Disposable { + const handle = ExtHostDecorations._handlePool++; + this._provider.set(handle, provider); + this._proxy.$registerDecorationProvider(handle, label); + + const listener = provider.onDidChangeDecorations(e => { + this._proxy.$onDidChange(handle, !e ? null : Array.isArray(e) ? e : [e]); + }); + + return new Disposable(() => { + listener.dispose(); + this._proxy.$unregisterDecorationProvider(handle); + this._provider.delete(handle); + }); + } + + $providerDecorations(handle: number, uri: URI): TPromise { + const provider = this._provider.get(handle); + return asWinJsPromise(token => provider.provideDecoration(uri, token)).then(data => { + return data && [data.priority, data.bubble, data.title, data.abbreviation, data.color]; + }); + } +} diff --git a/src/vs/workbench/api/node/extHostLanguageFeatures.ts b/src/vs/workbench/api/node/extHostLanguageFeatures.ts index 4a6a22e86f0..7718b667dd4 100644 --- a/src/vs/workbench/api/node/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/node/extHostLanguageFeatures.ts @@ -9,7 +9,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { mixin } from 'vs/base/common/objects'; import * as vscode from 'vscode'; import * as TypeConverters from 'vs/workbench/api/node/extHostTypeConverters'; -import { Range, Disposable, CompletionList, SnippetString } from 'vs/workbench/api/node/extHostTypes'; +import { Range, Disposable, CompletionList, SnippetString, Color } from 'vs/workbench/api/node/extHostTypes'; import { ISingleEditOperation } from 'vs/editor/common/editorCommon'; import * as modes from 'vs/editor/common/modes'; import { ExtHostHeapService } from 'vs/workbench/api/node/extHostHeapService'; @@ -721,19 +721,12 @@ class ColorProviderAdapter { }); } - provideColorPresentations(resource: URI, rawColorInfo: IRawColorInfo): TPromise { - let colorInfo: vscode.ColorInformation = { - range: TypeConverters.toRange(rawColorInfo.range), - color: { - red: rawColorInfo.color[0], - green: rawColorInfo.color[1], - blue: rawColorInfo.color[2], - alpha: rawColorInfo.color[3] - } - }; - 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)); + provideColorPresentations(resource: URI, raw: IRawColorInfo): TPromise { + const document = this._documents.getDocumentData(resource).document; + const range = TypeConverters.toRange(raw.range); + const color = new Color(raw.color[0], raw.color[1], raw.color[2], raw.color[3]); + return asWinJsPromise(token => this._provider.provideColorPresentations(color, { document, range }, token)).then(value => { + return value.map(TypeConverters.ColorPresentation.from); }); } } diff --git a/src/vs/workbench/api/node/extHostSCM.ts b/src/vs/workbench/api/node/extHostSCM.ts index a9c1e63af2e..310ea752f91 100644 --- a/src/vs/workbench/api/node/extHostSCM.ts +++ b/src/vs/workbench/api/node/extHostSCM.ts @@ -204,7 +204,7 @@ class ExtHostSourceControlResourceGroup implements vscode.SourceControlResourceG return; } - this._commands.executeCommand(command.command, ...command.arguments); + await this._commands.executeCommand(command.command, ...command.arguments); } _takeResourceStateSnapshot(): SCMRawResourceSplice[] { @@ -243,9 +243,8 @@ class ExtHostSourceControlResourceGroup implements vscode.SourceControlResourceG const tooltip = (r.decorations && r.decorations.tooltip) || ''; const strikeThrough = r.decorations && !!r.decorations.strikeThrough; const faded = r.decorations && !!r.decorations.faded; - const color = r.decorations && r.decorations.color; - return [handle, sourceUri, icons, tooltip, strikeThrough, faded, color] as SCMRawResource; + return [handle, sourceUri, icons, tooltip, strikeThrough, faded] as SCMRawResource; }); handlesToDelete.push(...this._handlesSnapshot.splice(start, deleteCount, ...handles)); @@ -532,6 +531,6 @@ export class ExtHostSCM { return; } - group.$executeResourceCommand(handle); + await group.$executeResourceCommand(handle); } } diff --git a/src/vs/workbench/api/node/extHostTextEditor.ts b/src/vs/workbench/api/node/extHostTextEditor.ts index 763969acc96..9872ac721fd 100644 --- a/src/vs/workbench/api/node/extHostTextEditor.ts +++ b/src/vs/workbench/api/node/extHostTextEditor.ts @@ -416,11 +416,29 @@ export class ExtHostTextEditor implements vscode.TextEditor { setDecorations(decorationType: vscode.TextEditorDecorationType, ranges: Range[] | vscode.DecorationOptions[]): void { this._runOnProxy( - () => this._proxy.$trySetDecorations( - this._id, - decorationType.key, - TypeConverters.fromRangeOrRangeWithMessage(ranges) - ) + () => { + if (TypeConverters.isDecorationOptionsArr(ranges)) { + return this._proxy.$trySetDecorations( + this._id, + decorationType.key, + TypeConverters.fromRangeOrRangeWithMessage(ranges) + ); + } else { + let _ranges: number[] = new Array(4 * ranges.length); + for (let i = 0, len = ranges.length; i < len; i++) { + const range = ranges[i]; + _ranges[4 * i] = range.start.line + 1; + _ranges[4 * i + 1] = range.start.character + 1; + _ranges[4 * i + 2] = range.end.line + 1; + _ranges[4 * i + 3] = range.end.character + 1; + } + return this._proxy.$trySetDecorationsFast( + this._id, + decorationType.key, + /*TODO: marshaller is too slow*/JSON.stringify(_ranges) + ); + } + } ); } diff --git a/src/vs/workbench/api/node/extHostTypeConverters.ts b/src/vs/workbench/api/node/extHostTypeConverters.ts index 54c80ba84c4..ca9561d8d25 100644 --- a/src/vs/workbench/api/node/extHostTypeConverters.ts +++ b/src/vs/workbench/api/node/extHostTypeConverters.ts @@ -139,7 +139,7 @@ function isDecorationOptions(something: any): something is vscode.DecorationOpti return (typeof something.range !== 'undefined'); } -function isDecorationOptionsArr(something: vscode.Range[] | vscode.DecorationOptions[]): something is vscode.DecorationOptions[] { +export function isDecorationOptionsArr(something: vscode.Range[] | vscode.DecorationOptions[]): something is vscode.DecorationOptions[] { if (something.length === 0) { return true; } diff --git a/src/vs/workbench/browser/actions/toggleActivityBarVisibility.ts b/src/vs/workbench/browser/actions/toggleActivityBarVisibility.ts index bd78ed75df5..9ae30a6d59b 100644 --- a/src/vs/workbench/browser/actions/toggleActivityBarVisibility.ts +++ b/src/vs/workbench/browser/actions/toggleActivityBarVisibility.ts @@ -10,7 +10,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { Action } from 'vs/base/common/actions'; import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; -import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; +import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { IPartService, Parts } from 'vs/workbench/services/part/common/partService'; export class ToggleActivityBarVisibilityAction extends Action { @@ -24,7 +24,7 @@ export class ToggleActivityBarVisibilityAction extends Action { id: string, label: string, @IPartService private partService: IPartService, - @IConfigurationEditingService private configurationEditingService: IConfigurationEditingService + @IConfigurationService private configurationService: IConfigurationService ) { super(id, label); @@ -35,9 +35,7 @@ export class ToggleActivityBarVisibilityAction extends Action { const visibility = this.partService.isVisible(Parts.ACTIVITYBAR_PART); const newVisibilityValue = !visibility; - this.configurationEditingService.writeConfiguration(ConfigurationTarget.USER, { key: ToggleActivityBarVisibilityAction.activityBarVisibleKey, value: newVisibilityValue }); - - return TPromise.as(null); + return this.configurationService.updateValue(ToggleActivityBarVisibilityAction.activityBarVisibleKey, newVisibilityValue, ConfigurationTarget.USER); } } diff --git a/src/vs/workbench/browser/actions/toggleSidebarPosition.ts b/src/vs/workbench/browser/actions/toggleSidebarPosition.ts index 6e62c166319..fc94e997424 100644 --- a/src/vs/workbench/browser/actions/toggleSidebarPosition.ts +++ b/src/vs/workbench/browser/actions/toggleSidebarPosition.ts @@ -10,8 +10,8 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { Action } from 'vs/base/common/actions'; import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; -import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; import { IPartService, Position } from 'vs/workbench/services/part/common/partService'; +import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; export class ToggleSidebarPositionAction extends Action { @@ -24,20 +24,18 @@ export class ToggleSidebarPositionAction extends Action { id: string, label: string, @IPartService private partService: IPartService, - @IConfigurationEditingService private configurationEditingService: IConfigurationEditingService + @IConfigurationService private configurationService: IConfigurationService ) { super(id, label); - this.enabled = !!this.partService && !!this.configurationEditingService; + this.enabled = !!this.partService && !!this.configurationService; } public run(): TPromise { const position = this.partService.getSideBarPosition(); const newPositionValue = (position === Position.LEFT) ? 'right' : 'left'; - this.configurationEditingService.writeConfiguration(ConfigurationTarget.USER, { key: ToggleSidebarPositionAction.sidebarPositionConfigurationKey, value: newPositionValue }); - - return TPromise.as(null); + return this.configurationService.updateValue(ToggleSidebarPositionAction.sidebarPositionConfigurationKey, newPositionValue, ConfigurationTarget.USER); } } diff --git a/src/vs/workbench/browser/actions/toggleStatusbarVisibility.ts b/src/vs/workbench/browser/actions/toggleStatusbarVisibility.ts index ddaadd0e277..5949ea28756 100644 --- a/src/vs/workbench/browser/actions/toggleStatusbarVisibility.ts +++ b/src/vs/workbench/browser/actions/toggleStatusbarVisibility.ts @@ -10,7 +10,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { Action } from 'vs/base/common/actions'; import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; -import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; +import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { IPartService, Parts } from 'vs/workbench/services/part/common/partService'; export class ToggleStatusbarVisibilityAction extends Action { @@ -24,7 +24,7 @@ export class ToggleStatusbarVisibilityAction extends Action { id: string, label: string, @IPartService private partService: IPartService, - @IConfigurationEditingService private configurationEditingService: IConfigurationEditingService + @IConfigurationService private configurationService: IConfigurationService ) { super(id, label); @@ -35,9 +35,7 @@ export class ToggleStatusbarVisibilityAction extends Action { const visibility = this.partService.isVisible(Parts.STATUSBAR_PART); const newVisibilityValue = !visibility; - this.configurationEditingService.writeConfiguration(ConfigurationTarget.USER, { key: ToggleStatusbarVisibilityAction.statusbarVisibleKey, value: newVisibilityValue }); - - return TPromise.as(null); + return this.configurationService.updateValue(ToggleStatusbarVisibilityAction.statusbarVisibleKey, newVisibilityValue, ConfigurationTarget.USER); } } diff --git a/src/vs/workbench/browser/labels.ts b/src/vs/workbench/browser/labels.ts index 963815fa275..5d2192810e8 100644 --- a/src/vs/workbench/browser/labels.ts +++ b/src/vs/workbench/browser/labels.ts @@ -20,9 +20,9 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; -import { IResourceDecorationsService, IResourceDecorationChangeEvent } from 'vs/workbench/services/decorations/browser/decorations'; +import { IDecorationsService, IResourceDecorationChangeEvent } from 'vs/workbench/services/decorations/browser/decorations'; import { Schemas } from 'vs/base/common/network'; -import { FileKind } from 'vs/platform/files/common/files'; +import { FileKind, FILES_ASSOCIATIONS_CONFIG } from 'vs/platform/files/common/files'; import { IModel } from 'vs/editor/common/editorCommon'; import { IThemeService } from 'vs/platform/theme/common/themeService'; @@ -53,7 +53,7 @@ export class ResourceLabel extends IconLabel { @IModeService private modeService: IModeService, @IModelService private modelService: IModelService, @IEnvironmentService protected environmentService: IEnvironmentService, - @IResourceDecorationsService protected decorationsService: IResourceDecorationsService, + @IDecorationsService protected decorationsService: IDecorationsService, @IThemeService private themeService: IThemeService ) { super(container, options); @@ -64,11 +64,25 @@ export class ResourceLabel extends IconLabel { } private registerListeners(): void { - this.extensionService.onReady().then(() => this.render(true /* clear cache */)); // update when extensions are loaded with potentially new languages - this.toDispose.push(this.configurationService.onDidUpdateConfiguration(() => this.render(true /* clear cache */))); // update when file.associations change - this.toDispose.push(this.modelService.onModelModeChanged(e => this.onModelModeChanged(e))); // react to model mode changes - this.toDispose.push(this.decorationsService.onDidChangeDecorations(this.onFileDecorationsChanges, this)); // react to file decoration changes + + // update when extensions are loaded with potentially new languages + this.extensionService.onReady().then(() => this.render(true /* clear cache */)); + + // react to model mode changes + this.toDispose.push(this.modelService.onModelModeChanged(e => this.onModelModeChanged(e))); + + // react to file decoration changes + this.toDispose.push(this.decorationsService.onDidChangeDecorations(this.onFileDecorationsChanges, this)); + + // react to theme changes this.toDispose.push(this.themeService.onThemeChange(() => this.render(false))); + + // react to files.associations changes + this.toDispose.push(this.configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration(FILES_ASSOCIATIONS_CONFIG)) { + this.render(true /* clear cache */); + } + })); } private onModelModeChanged(e: { model: IModel; oldModeId: string; }): void { @@ -95,6 +109,7 @@ export class ResourceLabel extends IconLabel { if (!this.options || !this.label || !this.label.resource) { return; } + if (this.options.fileDecorations && e.affectsResource(this.label.resource)) { this.render(false); } @@ -164,8 +179,7 @@ export class ResourceLabel extends IconLabel { }; const resource = this.label.resource; - let label = this.label.name; - + const label = this.label.name; if (this.options && typeof this.options.title === 'string') { iconLabelOptions.title = this.options.title; @@ -182,18 +196,19 @@ export class ResourceLabel extends IconLabel { iconLabelOptions.extraClasses.push(...this.options.extraClasses); } - if (this.options && this.options.fileDecorations) { - let deco = this.decorationsService.getTopDecoration( + if (this.options && this.options.fileDecorations && resource) { + let deco = this.decorationsService.getDecoration( resource, this.options.fileKind !== FileKind.FILE ); + if (deco && this.options.fileDecorations.colors) { iconLabelOptions.extraClasses.push(deco.labelClassName); } - if (deco && deco.letter && this.options.fileDecorations.badges) { + + if (deco && deco.badgeClassName && this.options.fileDecorations.badges) { iconLabelOptions.badge = { - letter: deco.letter, - title: deco.tooltip, + title: deco.title, className: deco.badgeClassName, }; } @@ -241,7 +256,7 @@ export class FileLabel extends ResourceLabel { @IModeService modeService: IModeService, @IModelService modelService: IModelService, @IEnvironmentService environmentService: IEnvironmentService, - @IResourceDecorationsService decorationsService: IResourceDecorationsService, + @IDecorationsService decorationsService: IDecorationsService, @IThemeService themeService: IThemeService, @IUntitledEditorService private untitledEditorService: IUntitledEditorService, ) { @@ -289,7 +304,6 @@ 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']; - if (resource) { const name = cssEscape(resources.basenameOrAuthority(resource).toLowerCase()); @@ -319,6 +333,7 @@ export function getIconClasses(modelService: IModelService, modeService: IModeSe } } } + return classes; } diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts index 8add806cda5..dcb5207ab72 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts @@ -6,75 +6,22 @@ 'use strict'; import 'vs/css!./media/activityaction'; -import nls = require('vs/nls'); import DOM = require('vs/base/browser/dom'); +import { EventType as TouchEventType, GestureEvent } from 'vs/base/browser/touch'; import { TPromise } from 'vs/base/common/winjs.base'; -import { Builder, $ } from 'vs/base/browser/builder'; -import { DelayedDragHandler } from 'vs/base/browser/dnd'; import { Action } from 'vs/base/common/actions'; -import { BaseActionItem, Separator, IBaseActionItemOptions } from 'vs/base/browser/ui/actionbar/actionbar'; -import { IActivityBarService, ProgressBadge, TextBadge, NumberBadge, IconBadge, IBadge } from 'vs/workbench/services/activity/common/activityBarService'; -import Event, { Emitter } from 'vs/base/common/event'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { ICommandService } from 'vs/platform/commands/common/commands'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { ViewletDescriptor } from 'vs/workbench/browser/viewlet'; import { IActivity, IGlobalActivity } from 'vs/workbench/common/activity'; import { dispose } from 'vs/base/common/lifecycle'; import { IViewletService, } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IPartService, Parts } from 'vs/workbench/services/part/common/partService'; import { IThemeService, ITheme, registerThemingParticipant, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; -import { ACTIVITY_BAR_BADGE_FOREGROUND, ACTIVITY_BAR_BADGE_BACKGROUND, ACTIVITY_BAR_DRAG_AND_DROP_BACKGROUND, ACTIVITY_BAR_FOREGROUND } from 'vs/workbench/common/theme'; -import { contrastBorder, activeContrastBorder, focusBorder } from 'vs/platform/theme/common/colorRegistry'; +import { activeContrastBorder, focusBorder } from 'vs/platform/theme/common/colorRegistry'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; - -export interface IViewletActivity { - badge: IBadge; - clazz: string; -} - -export class ActivityAction extends Action { - private badge: IBadge; - private _onDidChangeBadge = new Emitter(); - - constructor(private _activity: IActivity) { - super(_activity.id, _activity.name, _activity.cssClass); - - this.badge = null; - } - - public get activity(): IActivity { - return this._activity; - } - - public get onDidChangeBadge(): Event { - return this._onDidChangeBadge.event; - } - - public activate(): void { - if (!this.checked) { - this._setChecked(true); - } - } - - public deactivate(): void { - if (this.checked) { - this._setChecked(false); - } - } - - public getBadge(): IBadge { - return this.badge; - } - - public setBadge(badge: IBadge): void { - this.badge = badge; - this._onDidChangeBadge.fire(this); - } -} +import { ActivityAction, ActivityActionItem, ICompositeBarColors } from 'vs/workbench/browser/parts/compositebar/compositeBarActions'; export class ViewletActivityAction extends ActivityAction { @@ -83,15 +30,11 @@ export class ViewletActivityAction extends ActivityAction { private lastRun: number = 0; constructor( - private viewlet: ViewletDescriptor, + activity: IActivity, @IViewletService private viewletService: IViewletService, @IPartService private partService: IPartService ) { - super(viewlet); - } - - public get descriptor(): ViewletDescriptor { - return this.viewlet; + super(activity); } public run(event: any): TPromise { @@ -110,495 +53,15 @@ export class ViewletActivityAction extends ActivityAction { const activeViewlet = this.viewletService.getActiveViewlet(); // Hide sidebar if selected viewlet already visible - if (sideBarVisible && activeViewlet && activeViewlet.getId() === this.viewlet.id) { + if (sideBarVisible && activeViewlet && activeViewlet.getId() === this.activity.id) { return this.partService.setSideBarHidden(true); } - return this.viewletService.openViewlet(this.viewlet.id, true).then(() => this.activate()); + return this.viewletService.openViewlet(this.activity.id, true).then(() => this.activate()); } } -export class ActivityActionItem extends BaseActionItem { - protected $container: Builder; - protected $label: Builder; - protected $badge: Builder; - - private $badgeContent: Builder; - private mouseUpTimeout: number; - - constructor( - action: ActivityAction, - options: IBaseActionItemOptions, - @IThemeService protected themeService: IThemeService - ) { - super(null, action, options); - - this.themeService.onThemeChange(this.onThemeChange, this, this._callOnDispose); - action.onDidChangeBadge(this.handleBadgeChangeEvenet, this, this._callOnDispose); - } - - protected get activity(): IActivity { - return (this._action as ActivityAction).activity; - } - - protected updateStyles(): void { - const theme = this.themeService.getTheme(); - - // Label - if (this.$label) { - const background = theme.getColor(ACTIVITY_BAR_FOREGROUND); - - this.$label.style('background-color', background ? background.toString() : null); - } - - // Badge - if (this.$badgeContent) { - const badgeForeground = theme.getColor(ACTIVITY_BAR_BADGE_FOREGROUND); - const badgeBackground = theme.getColor(ACTIVITY_BAR_BADGE_BACKGROUND); - const contrastBorderColor = theme.getColor(contrastBorder); - - this.$badgeContent.style('color', badgeForeground ? badgeForeground.toString() : null); - this.$badgeContent.style('background-color', badgeBackground ? badgeBackground.toString() : null); - - this.$badgeContent.style('border-style', contrastBorderColor ? 'solid' : null); - this.$badgeContent.style('border-width', contrastBorderColor ? '1px' : null); - this.$badgeContent.style('border-color', contrastBorderColor ? contrastBorderColor.toString() : null); - } - } - - public render(container: HTMLElement): void { - super.render(container); - - // Make the container tab-able for keyboard navigation - this.$container = $(container).attr({ - tabIndex: '0', - role: 'button', - title: this.activity.name - }); - - // Try hard to prevent keyboard only focus feedback when using mouse - this.$container.on(DOM.EventType.MOUSE_DOWN, () => { - this.$container.addClass('clicked'); - }); - - this.$container.on(DOM.EventType.MOUSE_UP, () => { - if (this.mouseUpTimeout) { - clearTimeout(this.mouseUpTimeout); - } - - this.mouseUpTimeout = setTimeout(() => { - this.$container.removeClass('clicked'); - }, 800); // delayed to prevent focus feedback from showing on mouse up - }); - - // Label - this.$label = $('a.action-label').appendTo(this.builder); - if (this.activity.cssClass) { - this.$label.addClass(this.activity.cssClass); - } - - this.$badge = this.builder.clone().div({ 'class': 'badge' }, (badge: Builder) => { - this.$badgeContent = badge.div({ 'class': 'badge-content' }); - }); - - this.$badge.hide(); - - this.updateStyles(); - } - - private onThemeChange(theme: ITheme): void { - this.updateStyles(); - } - - public setBadge(badge: IBadge): void { - this.updateBadge(badge); - } - - protected updateBadge(badge: IBadge): void { - this.$badgeContent.empty(); - this.$badge.hide(); - - if (badge) { - - // Number - if (badge instanceof NumberBadge) { - if (badge.number) { - this.$badgeContent.text(badge.number > 99 ? '99+' : badge.number.toString()); - this.$badge.show(); - } - } - - // Text - else if (badge instanceof TextBadge) { - this.$badgeContent.text(badge.text); - this.$badge.show(); - } - - // Text - else if (badge instanceof IconBadge) { - this.$badge.show(); - } - - // Progress - else if (badge instanceof ProgressBadge) { - this.$badge.show(); - } - } - - // Title - let title: string; - if (badge && badge.getDescription()) { - if (this.activity.name) { - title = nls.localize('badgeTitle', "{0} - {1}", this.activity.name, badge.getDescription()); - } else { - title = badge.getDescription(); - } - } else { - title = this.activity.name; - } - - [this.$label, this.$badge, this.$container].forEach(b => { - if (b) { - b.attr('aria-label', title); - b.title(title); - } - }); - } - - private handleBadgeChangeEvenet(): void { - const action = this.getAction(); - if (action instanceof ActivityAction) { - this.updateBadge(action.getBadge()); - } - } - - public dispose(): void { - super.dispose(); - - if (this.mouseUpTimeout) { - clearTimeout(this.mouseUpTimeout); - } - - this.$badge.destroy(); - } -} - -export class ViewletActionItem extends ActivityActionItem { - - private static manageExtensionAction: ManageExtensionAction; - private static toggleViewletPinnedAction: ToggleViewletPinnedAction; - private static draggedViewlet: ViewletDescriptor; - - private viewletActivity: IActivity; - private cssClass: string; - - constructor( - private action: ViewletActivityAction, - @IContextMenuService private contextMenuService: IContextMenuService, - @IActivityBarService private activityBarService: IActivityBarService, - @IKeybindingService private keybindingService: IKeybindingService, - @IInstantiationService instantiationService: IInstantiationService, - @IThemeService themeService: IThemeService - ) { - super(action, { draggable: true }, themeService); - - this.cssClass = action.class; - - if (!ViewletActionItem.manageExtensionAction) { - ViewletActionItem.manageExtensionAction = instantiationService.createInstance(ManageExtensionAction); - } - - if (!ViewletActionItem.toggleViewletPinnedAction) { - ViewletActionItem.toggleViewletPinnedAction = instantiationService.createInstance(ToggleViewletPinnedAction, void 0); - } - } - - protected get activity(): IActivity { - if (!this.viewletActivity) { - let activityName: string; - - const keybinding = this.getKeybindingLabel(this.viewlet.id); - if (keybinding) { - activityName = nls.localize('titleKeybinding', "{0} ({1})", this.viewlet.name, keybinding); - } else { - activityName = this.viewlet.name; - } - - this.viewletActivity = { - id: this.viewlet.id, - cssClass: this.cssClass, - name: activityName - }; - } - - return this.viewletActivity; - } - - private get viewlet(): ViewletDescriptor { - return this.action.descriptor; - } - - private getKeybindingLabel(id: string): string { - const kb = this.keybindingService.lookupKeybinding(id); - if (kb) { - return kb.getLabel(); - } - - return null; - } - - public render(container: HTMLElement): void { - super.render(container); - - this.$container.on('contextmenu', e => { - DOM.EventHelper.stop(e, true); - - this.showContextMenu(container); - }); - - // Allow to drag - this.$container.on(DOM.EventType.DRAG_START, (e: DragEvent) => { - e.dataTransfer.effectAllowed = 'move'; - this.setDraggedViewlet(this.viewlet); - - // Trigger the action even on drag start to prevent clicks from failing that started a drag - if (!this.getAction().checked) { - this.getAction().run(); - } - }); - - // Drag enter - let counter = 0; // see https://github.com/Microsoft/vscode/issues/14470 - this.$container.on(DOM.EventType.DRAG_ENTER, (e: DragEvent) => { - const draggedViewlet = ViewletActionItem.getDraggedViewlet(); - if (draggedViewlet && draggedViewlet.id !== this.viewlet.id) { - counter++; - this.updateFromDragging(container, true); - } - }); - - // Drag leave - this.$container.on(DOM.EventType.DRAG_LEAVE, (e: DragEvent) => { - const draggedViewlet = ViewletActionItem.getDraggedViewlet(); - if (draggedViewlet) { - counter--; - if (counter === 0) { - this.updateFromDragging(container, false); - } - } - }); - - // Drag end - this.$container.on(DOM.EventType.DRAG_END, (e: DragEvent) => { - const draggedViewlet = ViewletActionItem.getDraggedViewlet(); - if (draggedViewlet) { - counter = 0; - this.updateFromDragging(container, false); - - ViewletActionItem.clearDraggedViewlet(); - } - }); - - // Drop - this.$container.on(DOM.EventType.DROP, (e: DragEvent) => { - DOM.EventHelper.stop(e, true); - - const draggedViewlet = ViewletActionItem.getDraggedViewlet(); - if (draggedViewlet && draggedViewlet.id !== this.viewlet.id) { - this.updateFromDragging(container, false); - ViewletActionItem.clearDraggedViewlet(); - - this.activityBarService.move(draggedViewlet.id, this.viewlet.id); - } - }); - - // Activate on drag over to reveal targets - [this.$badge, this.$label].forEach(b => new DelayedDragHandler(b.getHTMLElement(), () => { - if (!ViewletActionItem.getDraggedViewlet() && !this.getAction().checked) { - this.getAction().run(); - } - })); - - this.updateStyles(); - } - - private updateFromDragging(element: HTMLElement, isDragging: boolean): void { - const theme = this.themeService.getTheme(); - const dragBackground = theme.getColor(ACTIVITY_BAR_DRAG_AND_DROP_BACKGROUND); - - element.style.backgroundColor = isDragging && dragBackground ? dragBackground.toString() : null; - } - - public static getDraggedViewlet(): ViewletDescriptor { - return ViewletActionItem.draggedViewlet; - } - - private setDraggedViewlet(viewlet: ViewletDescriptor): void { - ViewletActionItem.draggedViewlet = viewlet; - } - - public static clearDraggedViewlet(): void { - ViewletActionItem.draggedViewlet = void 0; - } - - private showContextMenu(container: HTMLElement): void { - const actions: Action[] = [ViewletActionItem.toggleViewletPinnedAction]; - if (this.viewlet.extensionId) { - actions.push(new Separator()); - actions.push(ViewletActionItem.manageExtensionAction); - } - - const isPinned = this.activityBarService.isPinned(this.viewlet.id); - if (isPinned) { - ViewletActionItem.toggleViewletPinnedAction.label = nls.localize('removeFromActivityBar', "Hide from Activity Bar"); - } else { - ViewletActionItem.toggleViewletPinnedAction.label = nls.localize('keepInActivityBar', "Keep in Activity Bar"); - } - - this.contextMenuService.showContextMenu({ - getAnchor: () => container, - getActionsContext: () => this.viewlet, - getActions: () => TPromise.as(actions) - }); - } - - public focus(): void { - this.$container.domFocus(); - } - - protected _updateClass(): void { - if (this.cssClass) { - this.$badge.removeClass(this.cssClass); - } - - this.cssClass = this.getAction().class; - this.$badge.addClass(this.cssClass); - } - - protected _updateChecked(): void { - if (this.getAction().checked) { - this.$container.addClass('checked'); - } else { - this.$container.removeClass('checked'); - } - } - - protected _updateEnabled(): void { - if (this.getAction().enabled) { - this.builder.removeClass('disabled'); - } else { - this.builder.addClass('disabled'); - } - } - - public dispose(): void { - super.dispose(); - - ViewletActionItem.clearDraggedViewlet(); - - this.$label.destroy(); - } -} - -export class ViewletOverflowActivityAction extends ActivityAction { - - constructor( - private showMenu: () => void - ) { - super({ - id: 'activitybar.additionalViewlets.action', - name: nls.localize('additionalViews', "Additional Views"), - cssClass: 'toggle-more' - }); - } - - public run(event: any): TPromise { - this.showMenu(); - - return TPromise.as(true); - } -} - -export class ViewletOverflowActivityActionItem extends ActivityActionItem { - private name: string; - private cssClass: string; - private actions: OpenViewletAction[]; - - constructor( - action: ActivityAction, - private getOverflowingViewlets: () => ViewletDescriptor[], - private getBadge: (viewlet: ViewletDescriptor) => IBadge, - @IInstantiationService private instantiationService: IInstantiationService, - @IViewletService private viewletService: IViewletService, - @IContextMenuService private contextMenuService: IContextMenuService, - @IThemeService themeService: IThemeService - ) { - super(action, null, themeService); - - this.cssClass = action.class; - this.name = action.label; - } - - public showMenu(): void { - if (this.actions) { - dispose(this.actions); - } - - this.actions = this.getActions(); - - this.contextMenuService.showContextMenu({ - getAnchor: () => this.builder.getHTMLElement(), - getActions: () => TPromise.as(this.actions), - onHide: () => dispose(this.actions) - }); - } - - private getActions(): OpenViewletAction[] { - const activeViewlet = this.viewletService.getActiveViewlet(); - - return this.getOverflowingViewlets().map(viewlet => { - const action = this.instantiationService.createInstance(OpenViewletAction, viewlet); - action.radio = activeViewlet && activeViewlet.getId() === action.id; - - const badge = this.getBadge(action.viewlet); - let suffix: string | number; - if (badge instanceof NumberBadge) { - suffix = badge.number; - } else if (badge instanceof TextBadge) { - suffix = badge.text; - } - - if (suffix) { - action.label = nls.localize('numberBadge', "{0} ({1})", action.viewlet.name, suffix); - } else { - action.label = action.viewlet.name; - } - - return action; - }); - } - - public dispose(): void { - super.dispose(); - - this.actions = dispose(this.actions); - } -} - -class ManageExtensionAction extends Action { - - constructor( - @ICommandService private commandService: ICommandService - ) { - super('activitybar.manage.extension', nls.localize('manageExtension', "Manage Extension")); - } - - public run(viewlet: ViewletDescriptor): TPromise { - return this.commandService.executeCommand('_extensions.manage', viewlet.extensionId); - } -} - -class OpenViewletAction extends Action { +export class ToggleViewletAction extends Action { constructor( private _viewlet: ViewletDescriptor, @@ -608,44 +71,16 @@ class OpenViewletAction extends Action { super(_viewlet.id, _viewlet.name); } - public get viewlet(): ViewletDescriptor { - return this._viewlet; - } - public run(): TPromise { const sideBarVisible = this.partService.isVisible(Parts.SIDEBAR_PART); const activeViewlet = this.viewletService.getActiveViewlet(); // Hide sidebar if selected viewlet already visible - if (sideBarVisible && activeViewlet && activeViewlet.getId() === this.viewlet.id) { + if (sideBarVisible && activeViewlet && activeViewlet.getId() === this._viewlet.id) { return this.partService.setSideBarHidden(true); } - return this.viewletService.openViewlet(this.viewlet.id, true); - } -} - -export class ToggleViewletPinnedAction extends Action { - - constructor( - private viewlet: ViewletDescriptor, - @IActivityBarService private activityBarService: IActivityBarService - ) { - super('activitybar.show.toggleViewletPinned', viewlet ? viewlet.name : nls.localize('toggle', "Toggle View Pinned")); - - this.checked = this.viewlet && this.activityBarService.isPinned(this.viewlet.id); - } - - public run(context?: ViewletDescriptor): TPromise { - const viewlet = this.viewlet || context; - - if (this.activityBarService.isPinned(viewlet.id)) { - this.activityBarService.unpin(viewlet.id); - } else { - this.activityBarService.pin(viewlet.id); - } - - return TPromise.as(true); + return this.viewletService.openViewlet(this._viewlet.id, true); } } @@ -660,10 +95,11 @@ export class GlobalActivityActionItem extends ActivityActionItem { constructor( action: GlobalActivityAction, + colors: ICompositeBarColors, @IThemeService themeService: IThemeService, @IContextMenuService protected contextMenuService: IContextMenuService ) { - super(action, { draggable: false }, themeService); + super(action, { draggable: false, colors, icon: true }, themeService); } public render(container: HTMLElement): void { @@ -672,30 +108,27 @@ 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) => { - this.onClick(e); + DOM.EventHelper.stop(e, true); + + const event = new StandardMouseEvent(e); + this.showContextMenu({ x: event.posx, y: event.posy }); }); - // 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)) { - this.onClick(e); + DOM.EventHelper.stop(e, true); + + this.showContextMenu(this.$container.getHTMLElement()); } }); - } - public onClick(event?: MouseEvent | KeyboardEvent): void { - DOM.EventHelper.stop(event, true); + this.$container.on(TouchEventType.Tap, (e: GestureEvent) => { + DOM.EventHelper.stop(e, 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); + const event = new StandardMouseEvent(e); + this.showContextMenu({ x: event.posx, y: event.posy }); + }); } private showContextMenu(location: HTMLElement | { x: number, y: number }): void { diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts index 5afcced6e5c..0046b57dc47 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts @@ -8,37 +8,39 @@ import 'vs/css!./media/activitybarpart'; import nls = require('vs/nls'); import { TPromise } from 'vs/base/common/winjs.base'; -import DOM = require('vs/base/browser/dom'); -import * as arrays from 'vs/base/common/arrays'; import { illegalArgument } from 'vs/base/common/errors'; import { Builder, $, Dimension } from 'vs/base/browser/builder'; import { Action } from 'vs/base/common/actions'; -import { ActionsOrientation, ActionBar, IActionItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; -import { ViewletDescriptor } from 'vs/workbench/browser/viewlet'; +import { ActionsOrientation, ActionBar, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { GlobalActivityExtensions, IGlobalActivityRegistry } from 'vs/workbench/common/activity'; import { Registry } from 'vs/platform/registry/common/platform'; import { Part } from 'vs/workbench/browser/part'; -import { IViewlet } from 'vs/workbench/common/viewlet'; -import { ToggleViewletPinnedAction, ViewletActivityAction, ActivityAction, GlobalActivityActionItem, ViewletActionItem, ViewletOverflowActivityAction, ViewletOverflowActivityActionItem, GlobalActivityAction, IViewletActivity } from 'vs/workbench/browser/parts/activitybar/activitybarActions'; +import { GlobalActivityActionItem, GlobalActivityAction, ViewletActivityAction, ToggleViewletAction } from 'vs/workbench/browser/parts/activitybar/activitybarActions'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; -import { IActivityBarService, IBadge } from 'vs/workbench/services/activity/common/activityBarService'; +import { IBadge } from 'vs/workbench/services/activity/common/activity'; import { IPartService, Position as SideBarPosition } from 'vs/workbench/services/part/common/partService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IExtensionService } from 'vs/platform/extensions/common/extensions'; import { IStorageService } from 'vs/platform/storage/common/storage'; -import { Scope as MementoScope } from 'vs/workbench/common/memento'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { ToggleActivityBarVisibilityAction } from 'vs/workbench/browser/actions/toggleActivityBarVisibility'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { ACTIVITY_BAR_BACKGROUND, ACTIVITY_BAR_BORDER } from 'vs/workbench/common/theme'; +import { ACTIVITY_BAR_BACKGROUND, ACTIVITY_BAR_BORDER, ACTIVITY_BAR_FOREGROUND, ACTIVITY_BAR_BADGE_BACKGROUND, ACTIVITY_BAR_BADGE_FOREGROUND, ACTIVITY_BAR_DRAG_AND_DROP_BACKGROUND } from 'vs/workbench/common/theme'; import { contrastBorder } from 'vs/platform/theme/common/colorRegistry'; +import { CompositeBar } from 'vs/workbench/browser/parts/compositebar/compositeBar'; +import { ToggleCompositePinnedAction } from 'vs/workbench/browser/parts/compositebar/compositeBarActions'; -export class ActivitybarPart extends Part implements IActivityBarService { +export class ActivitybarPart extends Part { - private static readonly ACTIVITY_ACTION_HEIGHT = 50; private static readonly PINNED_VIEWLETS = 'workbench.activity.pinnedViewlets'; + private static COLORS = { + backgroundColor: ACTIVITY_BAR_FOREGROUND, + badgeBackground: ACTIVITY_BAR_BADGE_BACKGROUND, + badgeForeground: ACTIVITY_BAR_BADGE_FOREGROUND, + dragAndDropBackground: ACTIVITY_BAR_DRAG_AND_DROP_BACKGROUND + }; public _serviceBrand: any; @@ -47,17 +49,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { private globalActionBar: ActionBar; private globalActivityIdToActions: { [globalActivityId: string]: GlobalActivityAction; }; - private viewletSwitcherBar: ActionBar; - private viewletOverflowAction: ViewletOverflowActivityAction; - private viewletOverflowActionItem: ViewletOverflowActivityActionItem; - - private viewletIdToActions: { [viewletId: string]: ActivityAction; }; - private viewletIdToActionItems: { [viewletId: string]: IActionItem; }; - private viewletIdToActivityStack: { [viewletId: string]: IViewletActivity[]; }; - - private memento: object; - private pinnedViewlets: string[]; - private activeUnpinnedViewlet: ViewletDescriptor; + private compositeBar: CompositeBar; constructor( id: string, @@ -72,58 +64,36 @@ export class ActivitybarPart extends Part implements IActivityBarService { super(id, { hasTitle: false }, themeService); this.globalActivityIdToActions = Object.create(null); - - this.viewletIdToActionItems = Object.create(null); - this.viewletIdToActions = Object.create(null); - this.viewletIdToActivityStack = Object.create(null); - - this.memento = this.getMemento(this.storageService, MementoScope.GLOBAL); - - const pinnedViewlets = this.memento[ActivitybarPart.PINNED_VIEWLETS] as string[]; - - if (pinnedViewlets) { - this.pinnedViewlets = pinnedViewlets; - } else { - this.pinnedViewlets = this.viewletService.getViewlets().map(v => v.id); - } - + this.compositeBar = this.instantiationService.createInstance(CompositeBar, { + icon: true, + storageId: ActivitybarPart.PINNED_VIEWLETS, + orientation: ActionsOrientation.VERTICAL, + composites: this.viewletService.getViewlets(), + openComposite: (compositeId: string) => this.viewletService.openViewlet(compositeId, true), + getActivityAction: (compositeId: string) => this.instantiationService.createInstance(ViewletActivityAction, this.viewletService.getViewlet(compositeId)), + getCompositePinnedAction: (compositeId: string) => new ToggleCompositePinnedAction(this.viewletService.getViewlet(compositeId), this.compositeBar), + getOnCompositeClickAction: (compositeId: string) => this.instantiationService.createInstance(ToggleViewletAction, this.viewletService.getViewlet(compositeId)), + getDefaultCompositeId: () => this.viewletService.getDefaultViewletId(), + hidePart: () => this.partService.setSideBarHidden(true), + colors: ActivitybarPart.COLORS, + overflowActionSize: 50 + }); this.registerListeners(); } private registerListeners(): void { // Activate viewlet action on opening of a viewlet - this.toUnbind.push(this.viewletService.onDidViewletOpen(viewlet => this.onDidViewletOpen(viewlet))); + this.toUnbind.push(this.viewletService.onDidViewletOpen(viewlet => this.compositeBar.activateComposite(viewlet.getId()))); // Deactivate viewlet action on close - this.toUnbind.push(this.viewletService.onDidViewletClose(viewlet => this.onDidViewletClose(viewlet))); - } - - private onDidViewletOpen(viewlet: IViewlet): void { - const id = viewlet.getId(); - - if (this.viewletIdToActions[id]) { - this.viewletIdToActions[id].activate(); - } - - const activeUnpinnedViewletShouldClose = this.activeUnpinnedViewlet && this.activeUnpinnedViewlet.id !== viewlet.getId(); - const activeUnpinnedViewletShouldShow = !this.getPinnedViewlets().some(v => v.id === viewlet.getId()); - if (activeUnpinnedViewletShouldShow || activeUnpinnedViewletShouldClose) { - this.updateViewletSwitcher(); - } - } - - private onDidViewletClose(viewlet: IViewlet): void { - const id = viewlet.getId(); - - if (this.viewletIdToActions[id]) { - this.viewletIdToActions[id].deactivate(); - } + this.toUnbind.push(this.viewletService.onDidViewletClose(viewlet => this.compositeBar.deactivateComposite(viewlet.getId()))); + this.toUnbind.push(this.compositeBar.onDidContextMenu(e => this.showContextMenu(e))); } public showActivity(viewletOrActionId: string, badge: IBadge, clazz?: string): IDisposable { if (this.viewletService.getViewlet(viewletOrActionId)) { - return this.showViewletActivity(viewletOrActionId, badge, clazz); + return this.compositeBar.showActivity(viewletOrActionId, badge, clazz); } return this.showGlobalActivity(viewletOrActionId, badge); @@ -144,93 +114,15 @@ export class ActivitybarPart extends Part implements IActivityBarService { return toDisposable(() => action.setBadge(undefined)); } - private showViewletActivity(viewletId: string, badge: IBadge, clazz?: string): IDisposable { - if (!badge) { - throw illegalArgument('badge'); - } - - const activity = { badge, clazz }; - const stack = this.viewletIdToActivityStack[viewletId] || (this.viewletIdToActivityStack[viewletId] = []); - stack.unshift(activity); - - this.updateViewletActivity(viewletId); - - return { - dispose: () => { - const stack = this.viewletIdToActivityStack[viewletId]; - if (!stack) { - return; - } - - const idx = stack.indexOf(activity); - if (idx < 0) { - return; - } - - stack.splice(idx, 1); - if (stack.length === 0) { - delete this.viewletIdToActivityStack[viewletId]; - } - - this.updateViewletActivity(viewletId); - } - }; - } - - private updateViewletActivity(viewletId: string) { - const action = this.viewletIdToActions[viewletId]; - if (!action) { - return; - } - - const stack = this.viewletIdToActivityStack[viewletId]; - - // reset - if (!stack || !stack.length) { - action.setBadge(undefined); - } - - // update - else { - const [{ badge, clazz }] = stack; - action.setBadge(badge); - if (clazz) { - action.class = clazz; - } - } - } - public createContentArea(parent: Builder): Builder { const $el = $(parent); const $result = $('.content').appendTo($el); // Top Actionbar with action items for each viewlet action - this.createViewletSwitcher($result.clone()); + this.compositeBar.create($result.getHTMLElement()); // Top Actionbar with action items for each viewlet action - this.createGlobalActivityActionBar($result.getHTMLElement()); - - // Contextmenu for viewlets - $(parent).on('contextmenu', (e: MouseEvent) => { - DOM.EventHelper.stop(e, true); - - this.showContextMenu(e); - }, this.toUnbind); - - // Allow to drop at the end to move viewlet to the end - $(parent).on(DOM.EventType.DROP, (e: DragEvent) => { - const draggedViewlet = ViewletActionItem.getDraggedViewlet(); - if (draggedViewlet) { - DOM.EventHelper.stop(e, true); - - ViewletActionItem.clearDraggedViewlet(); - - const targetId = this.pinnedViewlets[this.pinnedViewlets.length - 1]; - if (targetId !== draggedViewlet.id) { - this.move(draggedViewlet.id, this.pinnedViewlets[this.pinnedViewlets.length - 1]); - } - } - }); + this.createGlobalActivityActionBar($('.global-activity').appendTo($result).getHTMLElement()); return $result; } @@ -257,7 +149,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { private showContextMenu(e: MouseEvent): void { const event = new StandardMouseEvent(e); - const actions: Action[] = this.viewletService.getViewlets().map(viewlet => this.instantiationService.createInstance(ToggleViewletPinnedAction, viewlet)); + const actions: Action[] = this.viewletService.getViewlets().map(viewlet => this.instantiationService.createInstance(ToggleCompositePinnedAction, viewlet, this.compositeBar)); actions.push(new Separator()); actions.push(this.instantiationService.createInstance(ToggleActivityBarVisibilityAction, ToggleActivityBarVisibilityAction.ID, nls.localize('hideActivitBar', "Hide Activity Bar"))); @@ -268,20 +160,6 @@ export class ActivitybarPart extends Part implements IActivityBarService { }); } - private createViewletSwitcher(div: Builder): void { - this.viewletSwitcherBar = new ActionBar(div, { - actionItemProvider: (action: Action) => action instanceof ViewletOverflowActivityAction ? this.viewletOverflowActionItem : this.viewletIdToActionItems[action.id], - orientation: ActionsOrientation.VERTICAL, - ariaLabel: nls.localize('activityBarAriaLabel', "Active View Switcher"), - animated: false - }); - - this.updateViewletSwitcher(); - - // Update viewlet switcher when external viewlets become ready - this.extensionService.onReady().then(() => this.updateViewletSwitcher()); - } - private createGlobalActivityActionBar(container: HTMLElement): void { const activityRegistry = Registry.as(GlobalActivityExtensions); const descriptors = activityRegistry.getActivities(); @@ -290,7 +168,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { .map(a => new GlobalActivityAction(a)); this.globalActionBar = new ActionBar(container, { - actionItemProvider: a => this.instantiationService.createInstance(GlobalActivityActionItem, a), + actionItemProvider: a => this.instantiationService.createInstance(GlobalActivityActionItem, a, ActivitybarPart.COLORS), orientation: ActionsOrientation.VERTICAL, ariaLabel: nls.localize('globalActions', "Global Actions"), animated: false @@ -302,234 +180,8 @@ export class ActivitybarPart extends Part implements IActivityBarService { }); } - private updateViewletSwitcher() { - if (!this.viewletSwitcherBar) { - return; // We have not been rendered yet so there is nothing to update. - } - - let viewletsToShow = this.getPinnedViewlets(); - - // Always show the active viewlet even if it is marked to be hidden - const activeViewlet = this.viewletService.getActiveViewlet(); - if (activeViewlet && !viewletsToShow.some(viewlet => viewlet.id === activeViewlet.getId())) { - this.activeUnpinnedViewlet = this.viewletService.getViewlet(activeViewlet.getId()); - viewletsToShow.push(this.activeUnpinnedViewlet); - } else { - this.activeUnpinnedViewlet = void 0; - } - - // Ensure we are not showing more viewlets than we have height for - let overflows = false; - if (this.dimension) { - let availableHeight = this.dimension.height; - if (this.globalActionBar) { - availableHeight -= (this.globalActionBar.items.length * ActivitybarPart.ACTIVITY_ACTION_HEIGHT); // adjust for global actions showing - } - - const maxVisible = Math.floor(availableHeight / ActivitybarPart.ACTIVITY_ACTION_HEIGHT); - overflows = viewletsToShow.length > maxVisible; - - if (overflows) { - viewletsToShow = viewletsToShow.slice(0, maxVisible - 1 /* make room for overflow action */); - } - } - - const visibleViewlets = Object.keys(this.viewletIdToActions); - const visibleViewletsChange = !arrays.equals(viewletsToShow.map(viewlet => viewlet.id), visibleViewlets); - - // Pull out overflow action if there is a viewlet change so that we can add it to the end later - if (this.viewletOverflowAction && visibleViewletsChange) { - this.viewletSwitcherBar.pull(this.viewletSwitcherBar.length() - 1); - - this.viewletOverflowAction.dispose(); - this.viewletOverflowAction = null; - - this.viewletOverflowActionItem.dispose(); - this.viewletOverflowActionItem = null; - } - - // Pull out viewlets that overflow or got hidden - const viewletIdsToShow = viewletsToShow.map(v => v.id); - visibleViewlets.forEach(viewletId => { - if (viewletIdsToShow.indexOf(viewletId) === -1) { - this.pullViewlet(viewletId); - } - }); - - // Built actions for viewlets to show - const newViewletsToShow = viewletsToShow - .filter(viewlet => !this.viewletIdToActions[viewlet.id]) - .map(viewlet => this.toAction(viewlet)); - - // Update when we have new viewlets to show - if (newViewletsToShow.length) { - - // Add to viewlet switcher - this.viewletSwitcherBar.push(newViewletsToShow, { label: true, icon: true }); - - // Make sure to activate the active one - const activeViewlet = this.viewletService.getActiveViewlet(); - if (activeViewlet) { - const activeViewletEntry = this.viewletIdToActions[activeViewlet.getId()]; - if (activeViewletEntry) { - activeViewletEntry.activate(); - } - } - - // Make sure to restore activity - Object.keys(this.viewletIdToActions).forEach(viewletId => { - this.updateViewletActivity(viewletId); - }); - } - - // Add overflow action as needed - if (visibleViewletsChange && overflows) { - this.viewletOverflowAction = this.instantiationService.createInstance(ViewletOverflowActivityAction, () => this.viewletOverflowActionItem.showMenu()); - this.viewletOverflowActionItem = this.instantiationService.createInstance(ViewletOverflowActivityActionItem, this.viewletOverflowAction, () => this.getOverflowingViewlets(), (viewlet: ViewletDescriptor) => this.viewletIdToActivityStack[viewlet.id] && this.viewletIdToActivityStack[viewlet.id][0].badge); - - this.viewletSwitcherBar.push(this.viewletOverflowAction, { label: true, icon: true }); - } - } - - private getOverflowingViewlets(): ViewletDescriptor[] { - const viewlets = this.getPinnedViewlets(); - if (this.activeUnpinnedViewlet) { - viewlets.push(this.activeUnpinnedViewlet); - } - const visibleViewlets = Object.keys(this.viewletIdToActions); - - return viewlets.filter(viewlet => visibleViewlets.indexOf(viewlet.id) === -1); - } - - private getVisibleViewlets(): ViewletDescriptor[] { - const viewlets = this.viewletService.getViewlets(); - const visibleViewlets = Object.keys(this.viewletIdToActions); - - return viewlets.filter(viewlet => visibleViewlets.indexOf(viewlet.id) >= 0); - } - - private getPinnedViewlets(): ViewletDescriptor[] { - return this.pinnedViewlets.map(viewletId => this.viewletService.getViewlet(viewletId)).filter(v => !!v); // ensure to remove those that might no longer exist - } - - private pullViewlet(viewletId: string): void { - const index = Object.keys(this.viewletIdToActions).indexOf(viewletId); - if (index >= 0) { - this.viewletSwitcherBar.pull(index); - - const action = this.viewletIdToActions[viewletId]; - action.dispose(); - delete this.viewletIdToActions[viewletId]; - - const actionItem = this.viewletIdToActionItems[action.id]; - actionItem.dispose(); - delete this.viewletIdToActionItems[action.id]; - } - } - - private toAction(viewlet: ViewletDescriptor): ActivityAction { - const action = this.instantiationService.createInstance(ViewletActivityAction, viewlet); - - this.viewletIdToActionItems[action.id] = this.instantiationService.createInstance(ViewletActionItem, action); - this.viewletIdToActions[viewlet.id] = action; - - return action; - } - public getPinned(): string[] { - return this.pinnedViewlets; - } - - public unpin(viewletId: string): void { - if (!this.isPinned(viewletId)) { - return; - } - - const activeViewlet = this.viewletService.getActiveViewlet(); - const defaultViewletId = this.viewletService.getDefaultViewletId(); - const visibleViewlets = this.getVisibleViewlets(); - - let unpinPromise: TPromise; - - // Case: viewlet is not the active one or the active one is a different one - // Solv: we do nothing - if (!activeViewlet || activeViewlet.getId() !== viewletId) { - unpinPromise = TPromise.as(null); - } - - // Case: viewlet is not the default viewlet and default viewlet is still showing - // Solv: we open the default viewlet - else if (defaultViewletId !== viewletId && this.isPinned(defaultViewletId)) { - unpinPromise = this.viewletService.openViewlet(defaultViewletId, true); - } - - // Case: we closed the last visible viewlet - // Solv: we hide the sidebar - else if (visibleViewlets.length === 1) { - unpinPromise = this.partService.setSideBarHidden(true); - } - - // Case: we closed the default viewlet - // Solv: we open the next visible viewlet from top - else { - unpinPromise = this.viewletService.openViewlet(visibleViewlets.filter(viewlet => viewlet.id !== viewletId)[0].id, true); - } - - unpinPromise.then(() => { - - // then remove from pinned and update switcher - const index = this.pinnedViewlets.indexOf(viewletId); - this.pinnedViewlets.splice(index, 1); - - this.updateViewletSwitcher(); - }); - } - - public isPinned(viewletId: string): boolean { - return this.pinnedViewlets.indexOf(viewletId) >= 0; - } - - public pin(viewletId: string, update = true): void { - if (this.isPinned(viewletId)) { - return; - } - - // first open that viewlet - this.viewletService.openViewlet(viewletId, true).then(() => { - - // then update - this.pinnedViewlets.push(viewletId); - this.pinnedViewlets = arrays.distinct(this.pinnedViewlets); - - if (update) { - this.updateViewletSwitcher(); - } - }); - } - - public move(viewletId: string, toViewletId: string): void { - - // Make sure a moved viewlet gets pinned - if (!this.isPinned(viewletId)) { - this.pin(viewletId, false /* defer update, we take care of it */); - } - - const fromIndex = this.pinnedViewlets.indexOf(viewletId); - const toIndex = this.pinnedViewlets.indexOf(toViewletId); - - this.pinnedViewlets.splice(fromIndex, 1); - this.pinnedViewlets.splice(toIndex, 0, viewletId); - - // Clear viewlets that are impacted by the move - const visibleViewlets = Object.keys(this.viewletIdToActions); - for (let i = Math.min(fromIndex, toIndex); i < visibleViewlets.length; i++) { - this.pullViewlet(visibleViewlets[i]); - } - - // timeout helps to prevent artifacts from showing up - setTimeout(() => { - this.updateViewletSwitcher(); - }, 0); + return this.viewletService.getViewlets().map(v => v.id).filter(id => this.compositeBar.isPinned(id));; } /** @@ -542,16 +194,20 @@ export class ActivitybarPart extends Part implements IActivityBarService { this.dimension = sizes[1]; - // Update switcher to handle overflow issues - this.updateViewletSwitcher(); + let availableHeight = this.dimension.height; + if (this.globalActionBar) { + // adjust height for global actions showing + availableHeight -= (this.globalActionBar.items.length * this.globalActionBar.domNode.clientHeight); + } + this.compositeBar.layout(new Dimension(dimension.width, availableHeight)); return sizes; } public dispose(): void { - if (this.viewletSwitcherBar) { - this.viewletSwitcherBar.dispose(); - this.viewletSwitcherBar = null; + if (this.compositeBar) { + this.compositeBar.dispose(); + this.compositeBar = null; } if (this.globalActionBar) { @@ -563,11 +219,10 @@ export class ActivitybarPart extends Part implements IActivityBarService { } public shutdown(): void { - // Persist Hidden State - this.memento[ActivitybarPart.PINNED_VIEWLETS] = this.pinnedViewlets; + this.compositeBar.store(); // Pass to super super.shutdown(); } -} \ No newline at end of file +} diff --git a/src/vs/workbench/browser/parts/activitybar/media/activityaction.css b/src/vs/workbench/browser/parts/activitybar/media/activityaction.css index f9bb260bdbe..3f07bfcb0d0 100644 --- a/src/vs/workbench/browser/parts/activitybar/media/activityaction.css +++ b/src/vs/workbench/browser/parts/activitybar/media/activityaction.css @@ -41,10 +41,6 @@ right: 1px; } -.monaco-workbench > .activitybar > .content .monaco-action-bar .action-label.toggle-more { - -webkit-mask: url('ellipsis-global.svg') no-repeat 50% 50%; -} - .monaco-workbench > .activitybar > .content .monaco-action-bar .badge { position: absolute; top: 5px; diff --git a/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css b/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css index 50061325e7e..2e8ceb738cc 100644 --- a/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css +++ b/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css @@ -21,4 +21,8 @@ .monaco-workbench > .activitybar [tabindex="0"]:focus { outline: 0 !important; /* activity bar indicates focus custom */ +} + +.monaco-workbench .activitybar > .content > .composite-bar > .monaco-action-bar .action-label.toggle-more { + -webkit-mask: url('ellipsis-global.svg') no-repeat 50% 50%; } \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/compositePart.ts b/src/vs/workbench/browser/parts/compositePart.ts index 2c4ee35d07c..7906d1d0da7 100644 --- a/src/vs/workbench/browser/parts/compositePart.ts +++ b/src/vs/workbench/browser/parts/compositePart.ts @@ -62,7 +62,7 @@ export abstract class CompositePart extends Part { private lastActiveCompositeId: string; private instantiatedComposites: Composite[]; private titleLabel: ICompositeTitleLabel; - private toolBar: ToolBar; + protected toolBar: ToolBar; private progressBar: ProgressBar; private contentAreaSize: Dimension; private telemetryActionsListener: IDisposable; @@ -74,7 +74,7 @@ export abstract class CompositePart extends Part { private messageService: IMessageService, private storageService: IStorageService, private telemetryService: ITelemetryService, - private contextMenuService: IContextMenuService, + protected contextMenuService: IContextMenuService, protected partService: IPartService, private keybindingService: IKeybindingService, protected instantiationService: IInstantiationService, diff --git a/src/vs/workbench/browser/parts/compositebar/compositeBar.ts b/src/vs/workbench/browser/parts/compositebar/compositeBar.ts new file mode 100644 index 00000000000..bbc33698401 --- /dev/null +++ b/src/vs/workbench/browser/parts/compositebar/compositeBar.ts @@ -0,0 +1,461 @@ +/*--------------------------------------------------------------------------------------------- + * 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 nls = require('vs/nls'); +import { Action } from 'vs/base/common/actions'; +import { illegalArgument } from 'vs/base/common/errors'; +import * as dom from 'vs/base/browser/dom'; +import * as arrays from 'vs/base/common/arrays'; +import { Dimension } from 'vs/base/browser/builder'; +import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { IBadge } from 'vs/workbench/services/activity/common/activity'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ActionBar, IActionItem, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar'; +import Event, { Emitter } from 'vs/base/common/event'; +import { CompositeActionItem, CompositeOverflowActivityAction, ICompositeActivity, CompositeOverflowActivityActionItem, ActivityAction, ICompositeBar, ICompositeBarColors } from 'vs/workbench/browser/parts/compositebar/compositeBarActions'; +import { TPromise } from 'vs/base/common/winjs.base'; + +export interface ICompositeBarOptions { + icon: boolean; + storageId: string; + orientation: ActionsOrientation; + composites: { id: string, name: string }[]; + colors: ICompositeBarColors; + overflowActionSize: number; + getActivityAction: (compositeId: string) => ActivityAction; + getCompositePinnedAction: (compositeId: string) => Action; + getOnCompositeClickAction: (compositeId: string) => Action; + openComposite: (compositeId: string) => TPromise; + getDefaultCompositeId: () => string; + hidePart: () => TPromise; +} + +export class CompositeBar implements ICompositeBar { + + private _onDidContextMenu: Emitter; + + private dimension: Dimension; + private toDispose: IDisposable[]; + + private compositeSwitcherBar: ActionBar; + private compositeOverflowAction: CompositeOverflowActivityAction; + private compositeOverflowActionItem: CompositeOverflowActivityActionItem; + + private compositeIdToActions: { [compositeId: string]: ActivityAction; }; + private compositeIdToActionItems: { [compositeId: string]: IActionItem; }; + private compositeIdToActivityStack: { [compositeId: string]: ICompositeActivity[]; }; + private compositeSizeInBar: Map; + + private pinnedComposites: string[]; + private activeCompositeId: string; + private activeUnpinnedCompositeId: string; + + constructor( + private options: ICompositeBarOptions, + @IInstantiationService private instantiationService: IInstantiationService, + @IStorageService private storageService: IStorageService, + ) { + this.toDispose = []; + this.compositeIdToActionItems = Object.create(null); + this.compositeIdToActions = Object.create(null); + this.compositeIdToActivityStack = Object.create(null); + this.compositeSizeInBar = new Map(); + + this._onDidContextMenu = new Emitter(); + + const pinnedComposites = JSON.parse(this.storageService.get(this.options.storageId, StorageScope.GLOBAL, null)) as string[]; + if (pinnedComposites) { + this.pinnedComposites = pinnedComposites; + } else { + this.pinnedComposites = this.options.composites.map(c => c.id); + } + } + + public get onDidContextMenu(): Event { + return this._onDidContextMenu.event; + } + + public activateComposite(id: string): void { + if (this.compositeIdToActions[id]) { + this.compositeIdToActions[id].activate(); + } + this.activeCompositeId = id; + + const activeUnpinnedCompositeShouldClose = this.activeUnpinnedCompositeId && this.activeUnpinnedCompositeId !== id; + const activeUnpinnedCompositeShouldShow = !this.pinnedComposites.some(pid => pid === id); + if (activeUnpinnedCompositeShouldShow || activeUnpinnedCompositeShouldClose) { + this.updateCompositeSwitcher(); + } + } + + public deactivateComposite(id: string): void { + if (this.compositeIdToActions[id]) { + this.compositeIdToActions[id].deactivate(); + } + } + + public showActivity(compositeId: string, badge: IBadge, clazz?: string): IDisposable { + if (!badge) { + throw illegalArgument('badge'); + } + + const activity = { badge, clazz }; + const stack = this.compositeIdToActivityStack[compositeId] || (this.compositeIdToActivityStack[compositeId] = []); + stack.unshift(activity); + + this.updateActivity(compositeId); + + return { + dispose: () => { + const stack = this.compositeIdToActivityStack[compositeId]; + if (!stack) { + return; + } + + const idx = stack.indexOf(activity); + if (idx < 0) { + return; + } + + stack.splice(idx, 1); + if (stack.length === 0) { + delete this.compositeIdToActivityStack[compositeId]; + } + + this.updateActivity(compositeId); + } + }; + } + + private updateActivity(compositeId: string) { + const action = this.compositeIdToActions[compositeId]; + if (!action) { + return; + } + + const stack = this.compositeIdToActivityStack[compositeId]; + + // reset + if (!stack || !stack.length) { + action.setBadge(undefined); + } + + // update + else { + const [{ badge, clazz }] = stack; + action.setBadge(badge); + if (clazz) { + action.class = clazz; + } + } + } + + public create(parent: HTMLElement): HTMLElement { + const actionBarDiv = parent.appendChild(dom.$('.composite-bar')); + this.compositeSwitcherBar = new ActionBar(actionBarDiv, { + actionItemProvider: (action: Action) => action instanceof CompositeOverflowActivityAction ? this.compositeOverflowActionItem : this.compositeIdToActionItems[action.id], + orientation: this.options.orientation, + ariaLabel: nls.localize('activityBarAriaLabel', "Active View Switcher"), + animated: false, + }); + + // Contextmenu for composites + this.toDispose.push(dom.addDisposableListener(parent, dom.EventType.CONTEXT_MENU, (e: MouseEvent) => { + dom.EventHelper.stop(e, true); + this._onDidContextMenu.fire(e); + })); + + // Allow to drop at the end to move composites to the end + this.toDispose.push(dom.addDisposableListener(parent, dom.EventType.DROP, (e: DragEvent) => { + const draggedCompositeId = CompositeActionItem.getDraggedCompositeId(); + if (draggedCompositeId) { + dom.EventHelper.stop(e, true); + CompositeActionItem.clearDraggedComposite(); + + const targetId = this.pinnedComposites[this.pinnedComposites.length - 1]; + if (targetId !== draggedCompositeId) { + this.move(draggedCompositeId, this.pinnedComposites[this.pinnedComposites.length - 1]); + } + } + })); + + return actionBarDiv; + } + + public getAction(compositeId): ActivityAction { + return this.compositeIdToActions[compositeId]; + } + + private updateCompositeSwitcher(): void { + if (!this.compositeSwitcherBar) { + return; // We have not been rendered yet so there is nothing to update. + } + + let compositesToShow = this.pinnedComposites; + + // Always show the active composite even if it is marked to be hidden + if (this.activeCompositeId && !compositesToShow.some(id => id === this.activeCompositeId)) { + this.activeUnpinnedCompositeId = this.activeCompositeId; + compositesToShow = compositesToShow.concat(this.activeUnpinnedCompositeId); + } else { + this.activeUnpinnedCompositeId = void 0; + } + + // Ensure we are not showing more composites than we have height for + let overflows = false; + if (this.dimension) { + let maxVisible = compositesToShow.length; + let size = 0; + const limit = this.options.orientation === ActionsOrientation.VERTICAL ? this.dimension.height : this.dimension.width; + for (let i = 0; i < compositesToShow.length && size <= limit; i++) { + size += this.compositeSizeInBar.get(compositesToShow[i]); + if (size > limit) { + maxVisible = i; + } + } + overflows = compositesToShow.length > maxVisible; + + if (overflows) { + size -= this.compositeSizeInBar.get(compositesToShow[maxVisible]); + compositesToShow = compositesToShow.slice(0, maxVisible); + } + // Check if we need to make extra room for the overflow action + if (overflows && (size + this.options.overflowActionSize > limit)) { + compositesToShow.pop(); + } + if (this.activeCompositeId && compositesToShow.length && compositesToShow.indexOf(this.activeCompositeId) === -1) { + compositesToShow.pop(); + compositesToShow.push(this.activeCompositeId); + } + } + + const visibleComposites = Object.keys(this.compositeIdToActions); + const visibleCompositesChange = !arrays.equals(compositesToShow, visibleComposites); + + // Pull out overflow action if there is a composite change so that we can add it to the end later + if (this.compositeOverflowAction && visibleCompositesChange) { + this.compositeSwitcherBar.pull(this.compositeSwitcherBar.length() - 1); + + this.compositeOverflowAction.dispose(); + this.compositeOverflowAction = null; + + this.compositeOverflowActionItem.dispose(); + this.compositeOverflowActionItem = null; + } + + // Pull out composites that overflow, got hidden or changed position + visibleComposites.forEach((compositeId, index) => { + if (compositesToShow.indexOf(compositeId) !== index) { + this.pullComposite(compositeId); + } + }); + + // Built actions for composites to show + const newCompositesToShow = compositesToShow + .filter(compositeId => !this.compositeIdToActions[compositeId]) + .map(compositeId => this.toAction(compositeId)); + + // Update when we have new composites to show + if (newCompositesToShow.length) { + + // Add to composite switcher + this.compositeSwitcherBar.push(newCompositesToShow, { label: true, icon: this.options.icon }); + + // Make sure to activate the active one + if (this.activeCompositeId) { + const activeCompositeEntry = this.compositeIdToActions[this.activeCompositeId]; + if (activeCompositeEntry) { + activeCompositeEntry.activate(); + } + } + + // Make sure to restore activity + Object.keys(this.compositeIdToActions).forEach(compositeId => { + this.updateActivity(compositeId); + }); + } + + // Add overflow action as needed + if (visibleCompositesChange && overflows) { + this.compositeOverflowAction = this.instantiationService.createInstance(CompositeOverflowActivityAction, () => this.compositeOverflowActionItem.showMenu()); + this.compositeOverflowActionItem = this.instantiationService.createInstance( + CompositeOverflowActivityActionItem, + this.compositeOverflowAction, + () => this.getOverflowingComposites(), + () => this.activeCompositeId, + (compositeId: string) => this.compositeIdToActivityStack[compositeId] && this.compositeIdToActivityStack[compositeId][0].badge, + this.options.getOnCompositeClickAction, + this.options.colors + ); + + this.compositeSwitcherBar.push(this.compositeOverflowAction, { label: false, icon: true }); + } + } + + private getOverflowingComposites(): { id: string, name: string }[] { + let overflowingIds = this.pinnedComposites; + if (this.activeUnpinnedCompositeId) { + overflowingIds = overflowingIds.concat(this.activeUnpinnedCompositeId); + } + const visibleComposites = Object.keys(this.compositeIdToActions); + + overflowingIds = overflowingIds.filter(compositeId => visibleComposites.indexOf(compositeId) === -1); + return this.options.composites.filter(c => overflowingIds.indexOf(c.id) !== -1); + } + + private getVisibleComposites(): string[] { + return Object.keys(this.compositeIdToActions); + } + + private pullComposite(compositeId: string): void { + const index = Object.keys(this.compositeIdToActions).indexOf(compositeId); + if (index >= 0) { + this.compositeSwitcherBar.pull(index); + + const action = this.compositeIdToActions[compositeId]; + action.dispose(); + delete this.compositeIdToActions[compositeId]; + + const actionItem = this.compositeIdToActionItems[action.id]; + actionItem.dispose(); + delete this.compositeIdToActionItems[action.id]; + } + } + + private toAction(compositeId: string): ActivityAction { + if (this.compositeIdToActions[compositeId]) { + return this.compositeIdToActions[compositeId]; + } + + const compositeActivityAction = this.options.getActivityAction(compositeId); + const pinnedAction = this.options.getCompositePinnedAction(compositeId); + this.compositeIdToActionItems[compositeId] = this.instantiationService.createInstance(CompositeActionItem, compositeActivityAction, pinnedAction, this.options.colors, this.options.icon, this); + this.compositeIdToActions[compositeId] = compositeActivityAction; + + return compositeActivityAction; + } + + public unpin(compositeId: string): void { + if (!this.isPinned(compositeId)) { + return; + } + + const defaultCompositeId = this.options.getDefaultCompositeId(); + const visibleComposites = this.getVisibleComposites(); + + let unpinPromise: TPromise; + + // Case: composite is not the active one or the active one is a different one + // Solv: we do nothing + if (!this.activeCompositeId || this.activeCompositeId !== compositeId) { + unpinPromise = TPromise.as(null); + } + + // Case: composite is not the default composite and default composite is still showing + // Solv: we open the default composite + else if (defaultCompositeId !== compositeId && this.isPinned(defaultCompositeId)) { + unpinPromise = this.options.openComposite(defaultCompositeId); + } + + // Case: we closed the last visible composite + // Solv: we hide the part + else if (visibleComposites.length === 1) { + unpinPromise = this.options.hidePart(); + } + + // Case: we closed the default composite + // Solv: we open the next visible composite from top + else { + unpinPromise = this.options.openComposite(visibleComposites.filter(cid => cid !== compositeId)[0]); + } + + unpinPromise.then(() => { + // then remove from pinned and update switcher + const index = this.pinnedComposites.indexOf(compositeId); + this.pinnedComposites.splice(index, 1); + + this.updateCompositeSwitcher(); + }); + } + + public isPinned(compositeId: string): boolean { + return this.pinnedComposites.indexOf(compositeId) >= 0; + } + + public pin(compositeId: string, update = true): void { + if (this.isPinned(compositeId)) { + return; + } + + this.options.openComposite(compositeId).then(() => { + this.pinnedComposites.push(compositeId); + this.pinnedComposites = arrays.distinct(this.pinnedComposites); + + if (update) { + this.updateCompositeSwitcher(); + } + }); + } + + public move(compositeId: string, toCompositeId: string): void { + // Make sure both composites are known to this composite bar + if (this.options.composites.filter(c => c.id === compositeId || c.id === toCompositeId).length !== 2) { + return; + } + // Make sure a moved composite gets pinned + if (!this.isPinned(compositeId)) { + this.pin(compositeId, false /* defer update, we take care of it */); + } + + const fromIndex = this.pinnedComposites.indexOf(compositeId); + const toIndex = this.pinnedComposites.indexOf(toCompositeId); + + this.pinnedComposites.splice(fromIndex, 1); + this.pinnedComposites.splice(toIndex, 0, compositeId); + + // Clear composites that are impacted by the move + const visibleComposites = Object.keys(this.compositeIdToActions); + for (let i = Math.min(fromIndex, toIndex); i < visibleComposites.length; i++) { + this.pullComposite(visibleComposites[i]); + } + + // timeout helps to prevent artifacts from showing up + setTimeout(() => { + this.updateCompositeSwitcher(); + }, 0); + } + + public layout(dimension: Dimension): void { + this.dimension = dimension; + if (dimension.height === 0 || dimension.width === 0) { + // Do not layout if not visible. Otherwise the size measurment would be computed wrongly + return; + } + + if (this.compositeSizeInBar.size === 0) { + // Compute size of each composite by getting the size from the css renderer + // Size is later used for overflow computation + this.compositeSwitcherBar.clear(); + this.compositeSwitcherBar.push(this.options.composites.map(c => this.options.getActivityAction(c.id))); + this.options.composites.map((c, index) => this.compositeSizeInBar.set(c.id, this.options.orientation === ActionsOrientation.VERTICAL + ? this.compositeSwitcherBar.getHeight(index) + : this.compositeSwitcherBar.getWidth(index) + )); + this.compositeSwitcherBar.clear(); + } + this.updateCompositeSwitcher(); + } + + public store(): void { + this.storageService.store(this.options.storageId, JSON.stringify(this.pinnedComposites), StorageScope.GLOBAL); + } + + public dispose(): void { + this.toDispose = dispose(this.toDispose); + } +} diff --git a/src/vs/workbench/browser/parts/compositebar/compositeBarActions.ts b/src/vs/workbench/browser/parts/compositebar/compositeBarActions.ts new file mode 100644 index 00000000000..ff0a1d4422b --- /dev/null +++ b/src/vs/workbench/browser/parts/compositebar/compositeBarActions.ts @@ -0,0 +1,606 @@ +/*--------------------------------------------------------------------------------------------- + * 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 nls = require('vs/nls'); +import { Action } from 'vs/base/common/actions'; +import { TPromise } from 'vs/base/common/winjs.base'; +import * as dom from 'vs/base/browser/dom'; +import { Builder, $ } from 'vs/base/browser/builder'; +import { BaseActionItem, IBaseActionItemOptions, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { dispose } from 'vs/base/common/lifecycle'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService'; +import { TextBadge, NumberBadge, IBadge, IconBadge, ProgressBadge } from 'vs/workbench/services/activity/common/activity'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { contrastBorder } from 'vs/platform/theme/common/colorRegistry'; +import { DelayedDragHandler } from 'vs/base/browser/dnd'; +import { IActivity } from 'vs/workbench/common/activity'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import Event, { Emitter } from 'vs/base/common/event'; + +export interface ICompositeActivity { + badge: IBadge; + clazz: string; +} + +export interface ICompositeBar { + /** + * Unpins a composite from the composite bar. + */ + unpin(compositeId: string): void; + + /** + * Pin a composite inside the composite bar. + */ + pin(compositeId: string): void; + + /** + * Find out if a composite is pinned in the composite bar. + */ + isPinned(compositeId: string): boolean; + + /** + * Reorder composite ordering by moving a composite to the location of another composite. + */ + move(compositeId: string, tocompositeId: string): void; +} + +export class ActivityAction extends Action { + private badge: IBadge; + private _onDidChangeBadge = new Emitter(); + + constructor(private _activity: IActivity) { + super(_activity.id, _activity.name, _activity.cssClass); + + this.badge = null; + } + + public get activity(): IActivity { + return this._activity; + } + + public get onDidChangeBadge(): Event { + return this._onDidChangeBadge.event; + } + + public activate(): void { + if (!this.checked) { + this._setChecked(true); + } + } + + public deactivate(): void { + if (this.checked) { + this._setChecked(false); + } + } + + public getBadge(): IBadge { + return this.badge; + } + + public setBadge(badge: IBadge): void { + this.badge = badge; + this._onDidChangeBadge.fire(this); + } +} + +export interface ICompositeBarColors { + backgroundColor: string; + badgeBackground: string; + badgeForeground: string; + dragAndDropBackground: string; +} + +export interface IActivityActionItemOptions extends IBaseActionItemOptions { + icon?: boolean; + colors: ICompositeBarColors; +} + +export class ActivityActionItem extends BaseActionItem { + protected $container: Builder; + protected $label: Builder; + protected $badge: Builder; + protected options: IActivityActionItemOptions; + + private $badgeContent: Builder; + private mouseUpTimeout: number; + + constructor( + action: ActivityAction, + options: IActivityActionItemOptions, + @IThemeService protected themeService: IThemeService + ) { + super(null, action, options); + + this.themeService.onThemeChange(this.onThemeChange, this, this._callOnDispose); + action.onDidChangeBadge(this.handleBadgeChangeEvenet, this, this._callOnDispose); + } + + protected get activity(): IActivity { + return (this._action as ActivityAction).activity; + } + + protected updateStyles(): void { + const theme = this.themeService.getTheme(); + + // Label + if (this.$label && this.options.icon) { + const background = theme.getColor(this.options.colors.backgroundColor); + + this.$label.style('background-color', background ? background.toString() : null); + } + + // Badge + if (this.$badgeContent) { + const badgeForeground = theme.getColor(this.options.colors.badgeForeground); + const badgeBackground = theme.getColor(this.options.colors.badgeBackground); + const contrastBorderColor = theme.getColor(contrastBorder); + + this.$badgeContent.style('color', badgeForeground ? badgeForeground.toString() : null); + this.$badgeContent.style('background-color', badgeBackground ? badgeBackground.toString() : null); + + this.$badgeContent.style('border-style', contrastBorderColor ? 'solid' : null); + this.$badgeContent.style('border-width', contrastBorderColor ? '1px' : null); + this.$badgeContent.style('border-color', contrastBorderColor ? contrastBorderColor.toString() : null); + } + } + + public render(container: HTMLElement): void { + super.render(container); + + // Make the container tab-able for keyboard navigation + this.$container = $(container).attr({ + tabIndex: '0', + role: 'button', + title: this.activity.name + }); + + // Try hard to prevent keyboard only focus feedback when using mouse + this.$container.on(dom.EventType.MOUSE_DOWN, () => { + this.$container.addClass('clicked'); + }); + + this.$container.on(dom.EventType.MOUSE_UP, () => { + if (this.mouseUpTimeout) { + clearTimeout(this.mouseUpTimeout); + } + + this.mouseUpTimeout = setTimeout(() => { + this.$container.removeClass('clicked'); + }, 800); // delayed to prevent focus feedback from showing on mouse up + }); + + // Label + this.$label = $('a.action-label').appendTo(this.builder); + if (this.activity.cssClass) { + this.$label.addClass(this.activity.cssClass); + } + if (!this.options.icon) { + this.$label.text(this.getAction().label); + } + + this.$badge = this.builder.clone().div({ 'class': 'badge' }, (badge: Builder) => { + this.$badgeContent = badge.div({ 'class': 'badge-content' }); + }); + + this.$badge.hide(); + + this.updateStyles(); + } + + private onThemeChange(theme: ITheme): void { + this.updateStyles(); + } + + public setBadge(badge: IBadge): void { + this.updateBadge(badge); + } + + protected updateBadge(badge: IBadge): void { + this.$badgeContent.empty(); + this.$badge.hide(); + + if (badge) { + + // Number + if (badge instanceof NumberBadge) { + if (badge.number) { + this.$badgeContent.text(badge.number > 99 ? '99+' : badge.number.toString()); + this.$badge.show(); + } + } + + // Text + else if (badge instanceof TextBadge) { + this.$badgeContent.text(badge.text); + this.$badge.show(); + } + + // Text + else if (badge instanceof IconBadge) { + this.$badge.show(); + } + + // Progress + else if (badge instanceof ProgressBadge) { + this.$badge.show(); + } + } + + // Title + let title: string; + if (badge && badge.getDescription()) { + if (this.activity.name) { + title = nls.localize('badgeTitle', "{0} - {1}", this.activity.name, badge.getDescription()); + } else { + title = badge.getDescription(); + } + } else { + title = this.activity.name; + } + + [this.$label, this.$badge, this.$container].forEach(b => { + if (b) { + b.attr('aria-label', title); + b.title(title); + } + }); + } + + private handleBadgeChangeEvenet(): void { + const action = this.getAction(); + if (action instanceof ActivityAction) { + this.updateBadge(action.getBadge()); + } + } + + public dispose(): void { + super.dispose(); + + if (this.mouseUpTimeout) { + clearTimeout(this.mouseUpTimeout); + } + + this.$badge.destroy(); + } +} + +export class CompositeOverflowActivityAction extends ActivityAction { + + constructor( + private showMenu: () => void + ) { + super({ + id: 'additionalComposites.action', + name: nls.localize('additionalViews', "Additional Views"), + cssClass: 'toggle-more' + }); + } + + public run(event: any): TPromise { + this.showMenu(); + + return TPromise.as(true); + } +} + +export class CompositeOverflowActivityActionItem extends ActivityActionItem { + private name: string; + private cssClass: string; + private actions: Action[]; + + constructor( + action: ActivityAction, + private getOverflowingComposites: () => { id: string, name: string }[], + private getActiveCompositeId: () => string, + private getBadge: (compositeId: string) => IBadge, + private getCompositeOpenAction: (compositeId: string) => Action, + colors: ICompositeBarColors, + @IInstantiationService private instantiationService: IInstantiationService, + @IContextMenuService private contextMenuService: IContextMenuService, + @IThemeService themeService: IThemeService + ) { + super(action, { icon: true, colors }, themeService); + + this.cssClass = action.class; + this.name = action.label; + } + + public showMenu(): void { + if (this.actions) { + dispose(this.actions); + } + + this.actions = this.getActions(); + + this.contextMenuService.showContextMenu({ + getAnchor: () => this.builder.getHTMLElement(), + getActions: () => TPromise.as(this.actions), + onHide: () => dispose(this.actions) + }); + } + + private getActions(): Action[] { + return this.getOverflowingComposites().map(composite => { + const action = this.getCompositeOpenAction(composite.id); + action.radio = this.getActiveCompositeId() === action.id; + + const badge = this.getBadge(composite.id); + let suffix: string | number; + if (badge instanceof NumberBadge) { + suffix = badge.number; + } else if (badge instanceof TextBadge) { + suffix = badge.text; + } + + if (suffix) { + action.label = nls.localize('numberBadge', "{0} ({1})", composite.name, suffix); + } else { + action.label = composite.name; + } + + return action; + }); + } + + public dispose(): void { + super.dispose(); + + this.actions = dispose(this.actions); + } +} + +class ManageExtensionAction extends Action { + + constructor( + @ICommandService private commandService: ICommandService + ) { + super('activitybar.manage.extension', nls.localize('manageExtension', "Manage Extension")); + } + + public run(id: string): TPromise { + return this.commandService.executeCommand('_extensions.manage', id); + } +} + +export class CompositeActionItem extends ActivityActionItem { + + private static manageExtensionAction: ManageExtensionAction; + private static draggedCompositeId: string; + + private compositeActivity: IActivity; + private cssClass: string; + + constructor( + private compositeActivityAction: ActivityAction, + private toggleCompositePinnedAction: Action, + colors: ICompositeBarColors, + icon: boolean, + private compositeBar: ICompositeBar, + @IContextMenuService private contextMenuService: IContextMenuService, + @IKeybindingService private keybindingService: IKeybindingService, + @IInstantiationService instantiationService: IInstantiationService, + @IThemeService themeService: IThemeService + ) { + super(compositeActivityAction, { draggable: true, colors, icon }, themeService); + + this.cssClass = compositeActivityAction.class; + + if (!CompositeActionItem.manageExtensionAction) { + CompositeActionItem.manageExtensionAction = instantiationService.createInstance(ManageExtensionAction); + } + } + + protected get activity(): IActivity { + if (!this.compositeActivity) { + let activityName: string; + + const keybinding = this.getKeybindingLabel(this.compositeActivityAction.activity.id); + if (keybinding) { + activityName = nls.localize('titleKeybinding', "{0} ({1})", this.compositeActivityAction.activity.name, keybinding); + } else { + activityName = this.compositeActivityAction.activity.name; + } + + this.compositeActivity = { + id: this.compositeActivityAction.activity.id, + cssClass: this.cssClass, + name: activityName + }; + } + + return this.compositeActivity; + } + + private getKeybindingLabel(id: string): string { + const kb = this.keybindingService.lookupKeybinding(id); + if (kb) { + return kb.getLabel(); + } + + return null; + } + + public render(container: HTMLElement): void { + super.render(container); + + this.$container.on('contextmenu', e => { + dom.EventHelper.stop(e, true); + + this.showContextMenu(container); + }); + + // Allow to drag + this.$container.on(dom.EventType.DRAG_START, (e: DragEvent) => { + e.dataTransfer.effectAllowed = 'move'; + this.setDraggedComposite(this.activity.id); + + // Trigger the action even on drag start to prevent clicks from failing that started a drag + if (!this.getAction().checked) { + this.getAction().run(); + } + }); + + // Drag enter + let counter = 0; // see https://github.com/Microsoft/vscode/issues/14470 + this.$container.on(dom.EventType.DRAG_ENTER, (e: DragEvent) => { + const draggedCompositeId = CompositeActionItem.getDraggedCompositeId(); + if (draggedCompositeId && draggedCompositeId !== this.activity.id) { + counter++; + this.updateFromDragging(container, true); + } + }); + + // Drag leave + this.$container.on(dom.EventType.DRAG_LEAVE, (e: DragEvent) => { + const draggedCompositeId = CompositeActionItem.getDraggedCompositeId(); + if (draggedCompositeId) { + counter--; + if (counter === 0) { + this.updateFromDragging(container, false); + } + } + }); + + // Drag end + this.$container.on(dom.EventType.DRAG_END, (e: DragEvent) => { + const draggedCompositeId = CompositeActionItem.getDraggedCompositeId(); + if (draggedCompositeId) { + counter = 0; + this.updateFromDragging(container, false); + + CompositeActionItem.clearDraggedComposite(); + } + }); + + // Drop + this.$container.on(dom.EventType.DROP, (e: DragEvent) => { + dom.EventHelper.stop(e, true); + + const draggedCompositeId = CompositeActionItem.getDraggedCompositeId(); + if (draggedCompositeId && draggedCompositeId !== this.activity.id) { + this.updateFromDragging(container, false); + CompositeActionItem.clearDraggedComposite(); + + this.compositeBar.move(draggedCompositeId, this.activity.id); + } + }); + + // Activate on drag over to reveal targets + [this.$badge, this.$label].forEach(b => new DelayedDragHandler(b.getHTMLElement(), () => { + if (!CompositeActionItem.getDraggedCompositeId() && !this.getAction().checked) { + this.getAction().run(); + } + })); + + this.updateStyles(); + } + + private updateFromDragging(element: HTMLElement, isDragging: boolean): void { + const theme = this.themeService.getTheme(); + const dragBackground = theme.getColor(this.options.colors.dragAndDropBackground); + + element.style.backgroundColor = isDragging && dragBackground ? dragBackground.toString() : null; + } + + public static getDraggedCompositeId(): string { + return CompositeActionItem.draggedCompositeId; + } + + private setDraggedComposite(compositeId: string): void { + CompositeActionItem.draggedCompositeId = compositeId; + } + + public static clearDraggedComposite(): void { + CompositeActionItem.draggedCompositeId = void 0; + } + + private showContextMenu(container: HTMLElement): void { + const actions: Action[] = [this.toggleCompositePinnedAction]; + if ((this.compositeActivityAction.activity).extensionId) { + actions.push(new Separator()); + actions.push(CompositeActionItem.manageExtensionAction); + } + + const isPinned = this.compositeBar.isPinned(this.activity.id); + if (isPinned) { + this.toggleCompositePinnedAction.label = nls.localize('hide', "Hide"); + this.toggleCompositePinnedAction.checked = false; + } else { + this.toggleCompositePinnedAction.label = nls.localize('keep', "Keep"); + } + + this.contextMenuService.showContextMenu({ + getAnchor: () => container, + getActionsContext: () => this.activity.id, + getActions: () => TPromise.as(actions) + }); + } + + public focus(): void { + this.$container.domFocus(); + } + + protected _updateClass(): void { + if (this.cssClass) { + this.$badge.removeClass(this.cssClass); + } + + this.cssClass = this.getAction().class; + this.$badge.addClass(this.cssClass); + } + + protected _updateChecked(): void { + if (this.getAction().checked) { + this.$container.addClass('checked'); + } else { + this.$container.removeClass('checked'); + } + } + + protected _updateEnabled(): void { + if (this.getAction().enabled) { + this.builder.removeClass('disabled'); + } else { + this.builder.addClass('disabled'); + } + } + + public dispose(): void { + super.dispose(); + + CompositeActionItem.clearDraggedComposite(); + + this.$label.destroy(); + } +} + +export class ToggleCompositePinnedAction extends Action { + + constructor( + private activity: IActivity, + private compositeBar: ICompositeBar + ) { + super('show.toggleCompositePinned', activity ? activity.name : nls.localize('toggle', "Toggle View Pinned")); + + this.checked = this.activity && this.compositeBar.isPinned(this.activity.id); + } + + public run(context: string): TPromise { + const id = this.activity ? this.activity.id : context; + + if (this.compositeBar.isPinned(id)) { + this.compositeBar.unpin(id); + } else { + this.compositeBar.pin(id); + } + + return TPromise.as(true); + } +} diff --git a/src/vs/workbench/browser/parts/editor/editorGroupsControl.ts b/src/vs/workbench/browser/parts/editor/editorGroupsControl.ts index ff6acd7892e..af1a3ec1a9d 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupsControl.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupsControl.ts @@ -73,6 +73,7 @@ export interface IEditorGroupsControl { getInstantiationService(position: Position): IInstantiationService; getProgressBar(position: Position): ProgressBar; updateProgress(position: Position, state: ProgressState): void; + updateTitleAreas(refreshActive?: boolean): void; layout(dimension: Dimension): void; layout(position: Position): void; @@ -321,7 +322,6 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro public show(editor: BaseEditor, position: Position, preserveActive: boolean, ratio?: number[]): void { const visibleEditorCount = this.getVisibleEditorCount(); - const currentActivePosition = this.getActivePosition(); // Store into editor bucket this.visibleEditors[position] = editor; @@ -392,7 +392,6 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro this.sashOne.layout(); this.layoutContainers(); - this.updateInactiveEditorGroupActions(currentActivePosition); // prevent some ugly flickering when opening a group } // Adjust layout: []|[] -> []|[]|[!] @@ -406,7 +405,6 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro this.sashTwo.layout(); this.layoutContainers(); - this.updateInactiveEditorGroupActions(currentActivePosition); // prevent some ugly flickering when opening a group } // Show editor container @@ -2065,18 +2063,6 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro } } - private updateInactiveEditorGroupActions(position: Position): void { - const activePosition = this.getActivePosition(); - if (activePosition === position) { - return; // this position is actually active - } - - const titleArea = this.getTitleAreaControl(position); - if (titleArea) { - titleArea.updateEditorActionsToolbar(); - } - } - public getInstantiationService(position: Position): IInstantiationService { return this.getFromContainer(position, EditorGroupsControl.INSTANTIATION_SERVICE_KEY); } @@ -2095,6 +2081,32 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro return silo ? silo.child().getProperty(key) : void 0; } + public updateTitleAreas(refreshActive?: boolean): void { + POSITIONS.forEach(position => { + const group = this.stacks.groupAt(position); + if (!group) { + return; + } + + const titleControl = this.getTitleAreaControl(position); + if (!titleControl) { + return; + } + + // Make sure the active group is shown in the title + // and refresh it if we are instructed to refresh it + if (refreshActive && group.isActive) { + titleControl.setContext(group); + titleControl.refresh(true); + } + + // Otherwise, just refresh the toolbar + else { + titleControl.updateEditorActionsToolbar(); + } + }); + } + public updateProgress(position: Position, state: ProgressState): void { const progressbar = this.getProgressBar(position); if (!progressbar) { diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index f64bf88b532..3e15e6118d3 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -21,14 +21,14 @@ import { toErrorMessage } from 'vs/base/common/errorMessage'; import { Scope as MementoScope } from 'vs/workbench/common/memento'; import { Part } from 'vs/workbench/browser/part'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; -import { EditorInput, EditorOptions, ConfirmResult, IWorkbenchEditorConfiguration, TextEditorOptions, SideBySideEditorInput, TextCompareEditorVisible, TEXT_DIFF_EDITOR_ID } from 'vs/workbench/common/editor'; +import { EditorInput, EditorOptions, ConfirmResult, IWorkbenchEditorConfiguration, TextEditorOptions, SideBySideEditorInput, TextCompareEditorVisible, TEXT_DIFF_EDITOR_ID, EditorOpeningEvent, IEditorOpeningEvent } from 'vs/workbench/common/editor'; import { EditorGroupsControl, Rochade, IEditorGroupsControl, ProgressState } from 'vs/workbench/browser/parts/editor/editorGroupsControl'; import { WorkbenchProgressService } from 'vs/workbench/services/progress/browser/progressService'; import { IEditorGroupService, GroupOrientation, GroupArrangement, IEditorTabOptions, IMoveOptions } from 'vs/workbench/services/group/common/groupService'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IEditorPart } from 'vs/workbench/services/editor/browser/editorService'; +import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; +import { IEditorPart } from 'vs/workbench/services/editor/common/editorService'; import { IPartService } from 'vs/workbench/services/part/common/partService'; -import { Position, POSITIONS, Direction } from 'vs/platform/editor/common/editor'; +import { Position, POSITIONS, Direction, IEditor } from 'vs/platform/editor/common/editor'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; @@ -99,6 +99,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService private revealIfOpen: boolean; private _onEditorsChanged: Emitter; + private _onEditorOpening: Emitter; private _onEditorsMoved: Emitter; private _onEditorOpenFail: Emitter; private _onGroupOrientationChanged: Emitter; @@ -132,6 +133,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService super(id, { hasTitle: false }, themeService); this._onEditorsChanged = new Emitter(); + this._onEditorOpening = new Emitter(); this._onEditorsMoved = new Emitter(); this._onEditorOpenFail = new Emitter(); this._onGroupOrientationChanged = new Emitter(); @@ -203,41 +205,44 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService this.toUnbind.push(this.stacks.onEditorClosed(event => this.onEditorClosed(event))); this.toUnbind.push(this.stacks.onGroupOpened(event => this.onEditorGroupOpenedOrClosed())); this.toUnbind.push(this.stacks.onGroupClosed(event => this.onEditorGroupOpenedOrClosed())); - this.toUnbind.push(this.configurationService.onDidUpdateConfiguration(e => this.onConfigurationUpdated(this.configurationService.getConfiguration()))); + this.toUnbind.push(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated(e))); } private onEditorGroupOpenedOrClosed(): void { this.updateStyles(); } - private onConfigurationUpdated(configuration: IWorkbenchEditorConfiguration): void { - if (configuration && configuration.workbench && configuration.workbench.editor) { - const editorConfig = configuration.workbench.editor; + private onConfigurationUpdated(event: IConfigurationChangeEvent): void { + if (event.affectsConfiguration('workbench.editor')) { + const configuration = this.configurationService.getConfiguration(); + if (configuration && configuration.workbench && configuration.workbench.editor) { + const editorConfig = configuration.workbench.editor; - // Pin all preview editors of the user chose to disable preview - const newPreviewEditors = editorConfig.enablePreview; - if (this.tabOptions.previewEditors !== newPreviewEditors && !newPreviewEditors) { - this.stacks.groups.forEach(group => { - if (group.previewEditor) { - this.pinEditor(group, group.previewEditor); - } - }); + // Pin all preview editors of the user chose to disable preview + const newPreviewEditors = editorConfig.enablePreview; + if (this.tabOptions.previewEditors !== newPreviewEditors && !newPreviewEditors) { + this.stacks.groups.forEach(group => { + if (group.previewEditor) { + this.pinEditor(group, group.previewEditor); + } + }); + } + + const oldTabOptions = objects.clone(this.tabOptions); + this.tabOptions = { + previewEditors: newPreviewEditors, + showIcons: editorConfig.showIcons, + tabCloseButton: editorConfig.tabCloseButton, + showTabs: this.forceHideTabs ? false : editorConfig.showTabs, + labelFormat: editorConfig.labelFormat, + }; + + if (!this.doNotFireTabOptionsChanged && !objects.equals(oldTabOptions, this.tabOptions)) { + this._onTabOptionsChanged.fire(this.tabOptions); + } + + this.revealIfOpen = editorConfig.revealIfOpen; } - - const oldTabOptions = objects.clone(this.tabOptions); - this.tabOptions = { - previewEditors: newPreviewEditors, - showIcons: editorConfig.showIcons, - tabCloseButton: editorConfig.tabCloseButton, - showTabs: this.forceHideTabs ? false : editorConfig.showTabs, - labelFormat: editorConfig.labelFormat, - }; - - if (!this.doNotFireTabOptionsChanged && !objects.equals(oldTabOptions, this.tabOptions)) { - this._onTabOptionsChanged.fire(this.tabOptions); - } - - this.revealIfOpen = editorConfig.revealIfOpen; } } @@ -289,6 +294,10 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService return this._onEditorsChanged.event; } + public get onEditorOpening(): Event { + return this._onEditorOpening.event; + } + public get onEditorsMoved(): Event { return this._onEditorsMoved.event; } @@ -309,26 +318,40 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService return this.tabOptions; } - public openEditor(input: EditorInput, options?: EditorOptions, sideBySide?: boolean): TPromise; - public openEditor(input: EditorInput, options?: EditorOptions, position?: Position, ratio?: number[]): TPromise; - public openEditor(input: EditorInput, options?: EditorOptions, arg3?: any, ratio?: number[]): TPromise { - - // Normalize some values - if (!options) { options = null; } + public openEditor(input: EditorInput, options?: EditorOptions, sideBySide?: boolean): TPromise; + public openEditor(input: EditorInput, options?: EditorOptions, position?: Position, ratio?: number[]): TPromise; + public openEditor(input: EditorInput, options?: EditorOptions, arg3?: any, ratio?: number[]): TPromise { + if (!options) { + options = null; + } // Determine position to open editor in (one, two, three) const position = this.findPosition(input, options, arg3, ratio); // Some conditions under which we prevent the request if ( - !input || // no input - position === null || // invalid position - !this.editorGroupsControl || // too early - this.editorGroupsControl.isDragging() // pending editor DND + !input || // no input + position === null || // invalid position + !this.editorGroupsControl || // too early + this.editorGroupsControl.isDragging() // pending editor DND ) { return TPromise.as(null); } + // Editor opening event (can be prevented and overridden) + const event = new EditorOpeningEvent(input, options, position); + this._onEditorOpening.fire(event); + const prevented = event.isPrevented(); + if (prevented) { + return prevented(); + } + + // Open through UI + return this.doOpenEditor(position, input, options, ratio); + } + + private doOpenEditor(position: Position, input: EditorInput, options: EditorOptions, ratio: number[]): TPromise { + // We need an editor descriptor for the input const descriptor = Registry.as(EditorExtensions.Editors).getEditor(input); if (!descriptor) { @@ -345,12 +368,6 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService this.telemetryService.publicLog('workbenchSideEditorOpened', { position: position }); } - // Open through UI - return this.doOpenEditor(position, descriptor, input, options, ratio); - } - - private doOpenEditor(position: Position, descriptor: IEditorDescriptor, input: EditorInput, options: EditorOptions, ratio: number[]): TPromise { - // Update stacks: We do this early on before the UI is there because we want our stacks model to have // a consistent view of the editor world and updating it later async after the UI is there will cause // issues (e.g. when a closeEditor call is made that expects the openEditor call to have updated the @@ -358,7 +375,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService // This can however cause a race condition where the stacks model indicates the opened editor is there // while the UI is not yet ready. Clients have to deal with this fact and we have to make sure that the // stacks model gets updated if any of the UI updating fails with an error. - const group = this.ensureGroup(position, !options || !options.preserveFocus); + const [group, newGroupOpened] = this.ensureGroup(position, !options || !options.preserveFocus); const pinned = !this.tabOptions.previewEditors || (options && (options.pinned || typeof options.index === 'number')) || input.isDirty(); const active = (group.count === 0) || !options || !options.inactive; @@ -387,7 +404,19 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService } // Set input to editor - return this.doSetInput(group, editor, input, options, monitor); + const inputPromise = this.doSetInput(group, editor, input, options, monitor); + + // A new active group got opened. Since this involves updating the title area controls to show + // the new editor and actions we trigger a direct update of title controls from here to avoid + // some UI flickering if we rely on the event handlers that all use schedulers. + // The reason we can trigger this now is that after the input is set to the editor group, the + // resource context is updated and the correct number of actions will be resolved from the title + // area. + if (newGroupOpened && this.stacks.isActive(group)) { + this.editorGroupsControl.updateTitleAreas(true /* refresh new active group */); + } + + return inputPromise; } private doShowEditor(group: EditorGroup, descriptor: IEditorDescriptor, input: EditorInput, options: EditorOptions, ratio: number[], monitor: ProgressMonitor): BaseEditor { @@ -629,6 +658,9 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService // Explicitly trigger the focus changed handler because the side by side control will not trigger it unless // the user is actively changing focus with the mouse from left/top to right/bottom. this.onGroupFocusChanged(); + + // Update title area sync to avoid some flickering with actions + this.editorGroupsControl.updateTitleAreas(); } } @@ -1007,7 +1039,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService } } - public replaceEditors(editors: { toReplace: EditorInput, replaceWith: EditorInput, options?: EditorOptions }[], position?: Position): TPromise { + public replaceEditors(editors: { toReplace: EditorInput, replaceWith: EditorInput, options?: EditorOptions }[], position?: Position): TPromise { const activeReplacements: IEditorReplacement[] = []; const hiddenReplacements: IEditorReplacement[] = []; @@ -1066,9 +1098,9 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService return res; } - public openEditors(editors: { input: EditorInput, position: Position, options?: EditorOptions }[]): TPromise { + public openEditors(editors: { input: EditorInput, position: Position, options?: EditorOptions }[]): TPromise { if (!editors.length) { - return TPromise.as([]); + return TPromise.as([]); } let activePosition: Position; @@ -1085,7 +1117,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService return this.stacks.groups.some(g => g.count > 0); } - public restoreEditors(): TPromise { + public restoreEditors(): TPromise { const editors = this.stacks.groups.map((group, index) => { return { input: group.activeEditor, @@ -1095,7 +1127,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService }); if (!editors.length) { - return TPromise.as([]); + return TPromise.as([]); } let activePosition: Position; @@ -1108,7 +1140,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService return this.doOpenEditors(editors, activePosition, editorState && editorState.ratio); } - private doOpenEditors(editors: { input: EditorInput, position: Position, options?: EditorOptions }[], activePosition?: number, ratio?: number[]): TPromise { + private doOpenEditors(editors: { input: EditorInput, position: Position, options?: EditorOptions }[], activePosition?: number, ratio?: number[]): TPromise { const positionOneEditors = editors.filter(e => e.position === Position.ONE); const positionTwoEditors = editors.filter(e => e.position === Position.TWO); const positionThreeEditors = editors.filter(e => e.position === Position.THREE); @@ -1155,7 +1187,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService // Open each input respecting the options. Since there can only be one active editor in each // position, we have to pick the first input from each position and add the others as inactive - const promises: TPromise[] = []; + const promises: TPromise[] = []; [positionOneEditors.shift(), positionTwoEditors.shift(), positionThreeEditors.shift()].forEach((editor, position) => { if (!editor) { return; // unused position @@ -1343,6 +1375,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService // Emitters this._onEditorsChanged.dispose(); + this._onEditorOpening.dispose(); this._onEditorsMoved.dispose(); this._onEditorOpenFail.dispose(); @@ -1508,9 +1541,11 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService array[from] = empty; } - private ensureGroup(position: Position, activate = true): EditorGroup { + private ensureGroup(position: Position, activate = true): [EditorGroup, boolean /* new group opened */] { + let newGroupOpened = false; let group = this.stacks.groupAt(position); if (!group) { + newGroupOpened = true; // Race condition: it could be that someone quickly opens editors one after // the other and we are asked to open an editor in position 2 before position @@ -1533,7 +1568,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService this.stacks.setActive(group); } - return group; + return [group, newGroupOpened]; } private modifyGroups(modification: () => void) { diff --git a/src/vs/workbench/browser/parts/editor/editorPicker.ts b/src/vs/workbench/browser/parts/editor/editorPicker.ts index f7c63fa7e08..fe160365da8 100644 --- a/src/vs/workbench/browser/parts/editor/editorPicker.ts +++ b/src/vs/workbench/browser/parts/editor/editorPicker.ts @@ -22,7 +22,7 @@ import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/edi import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { EditorInput, toResource, IEditorGroup, IEditorStacksModel } from 'vs/workbench/common/editor'; -import { compareItemsByScore, scoreItem, ScorerCache, massageSearchForScoring } from 'vs/base/parts/quickopen/common/quickOpenScorer'; +import { compareItemsByScore, scoreItem, ScorerCache, prepareQuery } from 'vs/base/parts/quickopen/common/quickOpenScorer'; export class EditorPickerEntry extends QuickOpenEntryGroup { private stacks: IEditorStacksModel; @@ -106,15 +106,15 @@ export abstract class BaseEditorPicker extends QuickOpenHandler { return TPromise.as(null); } - // Massage search for scoring - searchValue = massageSearchForScoring(searchValue); + // Prepare search for scoring + const query = prepareQuery(searchValue); const entries = editorEntries.filter(e => { - if (!searchValue) { + if (!query.value) { return true; } - const itemScore = scoreItem(e, searchValue, true, QuickOpenItemAccessor, this.scorerCache); + const itemScore = scoreItem(e, query, true, QuickOpenItemAccessor, this.scorerCache); if (!itemScore.score) { return false; } @@ -126,13 +126,13 @@ export abstract class BaseEditorPicker extends QuickOpenHandler { // Sorting const stacks = this.editorGroupService.getStacksModel(); - if (searchValue) { + if (query.value) { entries.sort((e1, e2) => { if (e1.group !== e2.group) { return stacks.positionOfGroup(e1.group) - stacks.positionOfGroup(e2.group); } - return compareItemsByScore(e1, e2, searchValue, true, QuickOpenItemAccessor, this.scorerCache); + return compareItemsByScore(e1, e2, query, true, QuickOpenItemAccessor, this.scorerCache); }); } diff --git a/src/vs/workbench/browser/parts/editor/editorStatus.ts b/src/vs/workbench/browser/parts/editor/editorStatus.ts index 28a91b2db63..2ffc03718df 100644 --- a/src/vs/workbench/browser/parts/editor/editorStatus.ts +++ b/src/vs/workbench/browser/parts/editor/editorStatus.ts @@ -23,7 +23,6 @@ import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorIn import { IFileEditorInput, EncodingMode, IEncodingSupport, toResource, SideBySideEditorInput } from 'vs/workbench/common/editor'; import { IDisposable, combinedDisposable, dispose } from 'vs/base/common/lifecycle'; import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; -import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; import { IEditorAction, ICommonCodeEditor, EndOfLineSequence, IModel } from 'vs/editor/common/editorCommon'; import { IModelLanguageChangedEvent, IModelOptionsChangedEvent } from 'vs/editor/common/model/textModelEvents'; import { TrimTrailingWhitespaceAction } from 'vs/editor/contrib/linesOperations/common/linesOperations'; @@ -34,7 +33,7 @@ import { IEditor as IBaseEditor, IEditorInput } from 'vs/platform/editor/common/ import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IQuickOpenService, IPickOpenEntry, IFilePickOpenEntry } from 'vs/platform/quickOpen/common/quickOpen'; import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; -import { SUPPORTED_ENCODINGS, IFileService, IFilesConfiguration } from 'vs/platform/files/common/files'; +import { SUPPORTED_ENCODINGS, IFileService, IFilesConfiguration, FILES_ASSOCIATIONS_CONFIG } from 'vs/platform/files/common/files'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IModelService } from 'vs/editor/common/services/modelService'; @@ -56,6 +55,7 @@ import { widgetShadow, editorWidgetBackground } from 'vs/platform/theme/common/c // TODO@Sandeep layer breaker // tslint:disable-next-line:import-patterns import { IPreferencesService } from 'vs/workbench/parts/preferences/common/preferences'; +import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; function toEditorWithEncodingSupport(input: IEditorInput): IEncodingSupport { if (input instanceof SideBySideEditorInput) { @@ -785,22 +785,18 @@ export class ChangeModeAction extends Action { public static ID = 'workbench.action.editor.changeLanguageMode'; public static LABEL = nls.localize('changeMode', "Change Language Mode"); - private static FILE_ASSOCIATION_KEY = 'files.associations'; - constructor( actionId: string, actionLabel: string, @IModeService private modeService: IModeService, @IModelService private modelService: IModelService, @IWorkbenchEditorService private editorService: IWorkbenchEditorService, - @IConfigurationEditingService private configurationEditingService: IConfigurationEditingService, @IWorkspaceConfigurationService private configurationService: IWorkspaceConfigurationService, @IQuickOpenService private quickOpenService: IQuickOpenService, @IPreferencesService private preferencesService: IPreferencesService, @IInstantiationService private instantiationService: IInstantiationService, @ICommandService private commandService: ICommandService, - @IUntitledEditorService private untitledEditorService: IUntitledEditorService, - @IConfigurationEditingService private configurationEditService: IConfigurationEditingService + @IUntitledEditorService private untitledEditorService: IUntitledEditorService ) { super(actionId, actionLabel); } @@ -966,7 +962,7 @@ export class ChangeModeAction extends Action { TPromise.timeout(50 /* quick open is sensitive to being opened so soon after another */).done(() => { this.quickOpenService.pick(picks, { placeHolder: nls.localize('pickLanguageToConfigure', "Select Language Mode to Associate with '{0}'", extension || basename) }).done(language => { if (language) { - const fileAssociationsConfig = this.configurationService.lookup(ChangeModeAction.FILE_ASSOCIATION_KEY); + const fileAssociationsConfig = this.configurationService.inspect(FILES_ASSOCIATIONS_CONFIG); let associationKey: string; if (extension && basename[0] !== '.') { @@ -989,8 +985,7 @@ export class ChangeModeAction extends Action { currentAssociations[associationKey] = language.id; - // Write config - this.configurationEditingService.writeConfiguration(target, { key: ChangeModeAction.FILE_ASSOCIATION_KEY, value: currentAssociations }); + this.configurationService.updateValue(FILES_ASSOCIATIONS_CONFIG, currentAssociations, target); } }); }); @@ -1229,7 +1224,7 @@ class ScreenReaderDetectedExplanation { anchorElement: HTMLElement, @IThemeService private readonly themeService: IThemeService, @IContextViewService private readonly contextViewService: IContextViewService, - @IConfigurationEditingService private readonly configurationEditingService: IConfigurationEditingService, + @IWorkspaceConfigurationService private readonly configurationService: IWorkspaceConfigurationService, ) { this._isDisposed = false; this._toDispose = []; @@ -1281,20 +1276,14 @@ class ScreenReaderDetectedExplanation { const yesBtn = $('div.button', {}, nls.localize('screenReaderDetectedExplanation.answerYes', "Yes")); this._toDispose.push(addDisposableListener(yesBtn, 'click', () => { - this.configurationEditingService.writeConfiguration(ConfigurationTarget.USER, { - key: 'editor.accessibilitySupport', - value: 'on' - }); + this.configurationService.updateValue('editor.accessibilitySupport', 'on', ConfigurationTarget.USER); this.contextViewService.hideContextView(); })); domNode.appendChild(yesBtn); const noBtn = $('div.button', {}, nls.localize('screenReaderDetectedExplanation.answerNo', "No")); this._toDispose.push(addDisposableListener(noBtn, 'click', () => { - this.configurationEditingService.writeConfiguration(ConfigurationTarget.USER, { - key: 'editor.accessibilitySupport', - value: 'off' - }); + this.configurationService.updateValue('editor.accessibilitySupport', 'off', ConfigurationTarget.USER); this.contextViewService.hideContextView(); })); domNode.appendChild(noBtn); diff --git a/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts index 2c08851ca89..0db19a83116 100644 --- a/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts @@ -13,10 +13,12 @@ import { TitleControl } from 'vs/workbench/browser/parts/editor/titleControl'; import { ResourceLabel } from 'vs/workbench/browser/labels'; import { Verbosity } from 'vs/platform/editor/common/editor'; import { TAB_ACTIVE_FOREGROUND, TAB_UNFOCUSED_ACTIVE_FOREGROUND } from 'vs/workbench/common/theme'; +import { EventType as TouchEventType, GestureEvent, Gesture } from 'vs/base/browser/touch'; export class NoTabsTitleControl extends TitleControl { private titleContainer: HTMLElement; private editorLabel: ResourceLabel; + private titleTouchSupport: Gesture; public setContext(group: IEditorGroup): void { super.setContext(group); @@ -29,12 +31,18 @@ export class NoTabsTitleControl extends TitleControl { this.titleContainer = parent; + // Gesture Support + this.titleTouchSupport = new Gesture(this.titleContainer); + // Pin on double click this.toUnbind.push(DOM.addDisposableListener(this.titleContainer, DOM.EventType.DBLCLICK, (e: MouseEvent) => this.onTitleDoubleClick(e))); // Detect mouse click this.toUnbind.push(DOM.addDisposableListener(this.titleContainer, DOM.EventType.CLICK, (e: MouseEvent) => this.onTitleClick(e))); + // Detect touch + this.toUnbind.push(DOM.addDisposableListener(this.titleContainer, TouchEventType.Tap, (e: GestureEvent) => this.onTitleClick(e))); + // Editor Label this.editorLabel = this.instantiationService.createInstance(ResourceLabel, this.titleContainer, void 0); this.toUnbind.push(this.editorLabel); @@ -50,6 +58,7 @@ export class NoTabsTitleControl extends TitleControl { // Context Menu this.toUnbind.push(DOM.addDisposableListener(this.titleContainer, DOM.EventType.CONTEXT_MENU, (e: Event) => this.onContextMenu({ group: this.context, editor: this.context.activeEditor }, e, this.titleContainer))); + this.toUnbind.push(DOM.addDisposableListener(this.titleContainer, TouchEventType.Contextmenu, (e: Event) => this.onContextMenu({ group: this.context, editor: this.context.activeEditor }, e, this.titleContainer))); } private onTitleLabelClick(e: MouseEvent): void { @@ -70,7 +79,7 @@ export class NoTabsTitleControl extends TitleControl { this.editorGroupService.pinEditor(group, group.activeEditor); } - private onTitleClick(e: MouseEvent): void { + private onTitleClick(e: MouseEvent | GestureEvent): void { if (!this.context) { return; } @@ -78,7 +87,7 @@ export class NoTabsTitleControl extends TitleControl { const group = this.context; // Close editor on middle mouse click - if (e.button === 1 /* Middle Button */) { + if (e instanceof MouseEvent && e.button === 1 /* Middle Button */) { this.closeEditorAction.run({ group, editor: group.activeEditor }).done(null, errors.onUnexpectedError); } @@ -150,4 +159,10 @@ export class NoTabsTitleControl extends TitleControl { default: return Verbosity.MEDIUM; } } + + public dispose(): void { + super.dispose(); + + this.titleTouchSupport.dispose(); + } } \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts index 3a12c2226cf..9b50b863a52 100644 --- a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts @@ -17,10 +17,11 @@ import { ActionRunner, IAction } from 'vs/base/common/actions'; import { Position, IEditorInput, Verbosity, IUntitledResourceInput } from 'vs/platform/editor/common/editor'; import { IEditorGroup, toResource } from 'vs/workbench/common/editor'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { EventType as TouchEventType, GestureEvent, Gesture } from 'vs/base/browser/touch'; import { KeyCode } from 'vs/base/common/keyCodes'; import { ResourceLabel } from 'vs/workbench/browser/labels'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; -import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IWorkbenchEditorService, DelegatingWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; import { IMessageService } from 'vs/platform/message/common/message'; @@ -37,7 +38,6 @@ import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElemen import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import { extractResources } from 'vs/base/browser/dnd'; import { getOrSet } from 'vs/base/common/map'; -import { DelegatingWorkbenchEditorService } from 'vs/workbench/services/editor/browser/editorService'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; import { TAB_INACTIVE_BACKGROUND, TAB_ACTIVE_BACKGROUND, TAB_ACTIVE_FOREGROUND, TAB_INACTIVE_FOREGROUND, TAB_BORDER, EDITOR_DRAG_AND_DROP_BACKGROUND, TAB_UNFOCUSED_ACTIVE_FOREGROUND, TAB_UNFOCUSED_INACTIVE_FOREGROUND, TAB_UNFOCUSED_ACTIVE_BORDER, TAB_ACTIVE_BORDER } from 'vs/workbench/common/theme'; @@ -504,6 +504,9 @@ export class TabsTitleControl extends TitleControl { tabContainer.setAttribute('role', 'presentation'); // cannot use role "tab" here due to https://github.com/Microsoft/vscode/issues/8659 DOM.addClass(tabContainer, 'tab'); + // Gesture Support + const gestureSupport = new Gesture(tabContainer); + // Tab Editor Label const editorLabel = this.instantiationService.createInstance(ResourceLabel, tabContainer, void 0); this.editorLabels.push(editorLabel); @@ -519,7 +522,7 @@ export class TabsTitleControl extends TitleControl { // Eventing const disposable = this.hookTabListeners(tabContainer, index); - this.tabDisposeables.push(combinedDisposable([disposable, bar, editorLabel])); + this.tabDisposeables.push(combinedDisposable([disposable, bar, editorLabel, gestureSupport])); return tabContainer; } @@ -569,15 +572,32 @@ export class TabsTitleControl extends TitleControl { private hookTabListeners(tab: HTMLElement, index: number): IDisposable { const disposables: IDisposable[] = []; - // Open on Click - disposables.push(DOM.addDisposableListener(tab, DOM.EventType.MOUSE_DOWN, (e: MouseEvent) => { + const handleClickOrTouch = (e: MouseEvent | GestureEvent) => { tab.blur(); + if (e instanceof MouseEvent && e.button !== 0) { + return; // only for left mouse click + } + const { editor, position } = this.toTabContext(index); - if (e.button === 0 /* Left Button */ && !this.isTabActionBar((e.target || e.srcElement) as HTMLElement)) { + if (!this.isTabActionBar((e.target || e.srcElement) as HTMLElement)) { setTimeout(() => this.editorService.openEditor(editor, null, position).done(null, errors.onUnexpectedError)); // timeout to keep focus in editor after mouse up } - })); + }; + + const showContextMenu = (e: Event) => { + DOM.EventHelper.stop(e); + + const { group, editor } = this.toTabContext(index); + + this.onContextMenu({ group, editor }, e, tab); + }; + + // Open on Click + disposables.push(DOM.addDisposableListener(tab, DOM.EventType.MOUSE_DOWN, (e: MouseEvent) => handleClickOrTouch(e))); + + // Open on Touch + disposables.push(DOM.addDisposableListener(tab, TouchEventType.Tap, (e: GestureEvent) => handleClickOrTouch(e))); // Close on mouse middle click disposables.push(DOM.addDisposableListener(tab, DOM.EventType.MOUSE_UP, (e: MouseEvent) => { @@ -593,14 +613,15 @@ export class TabsTitleControl extends TitleControl { disposables.push(DOM.addDisposableListener(tab, DOM.EventType.KEY_DOWN, (e: KeyboardEvent) => { const event = new StandardKeyboardEvent(e); if (event.shiftKey && event.keyCode === KeyCode.F10) { - DOM.EventHelper.stop(e); - - const { group, editor } = this.toTabContext(index); - - this.onContextMenu({ group, editor }, e, tab); + showContextMenu(e); } })); + // Context menu on touch context menu gesture + disposables.push(DOM.addDisposableListener(tab, TouchEventType.Contextmenu, (e: GestureEvent) => { + showContextMenu(e); + })); + // Keyboard accessibility disposables.push(DOM.addDisposableListener(tab, DOM.EventType.KEY_UP, (e: KeyboardEvent) => { const event = new StandardKeyboardEvent(e); diff --git a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts index 7143b5a9fa2..641e62414b7 100644 --- a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts @@ -22,14 +22,13 @@ import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import { DiffNavigator } from 'vs/editor/browser/widget/diffNavigator'; import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget'; import { TextDiffEditorModel } from 'vs/workbench/common/editor/textDiffEditorModel'; -import { DelegatingWorkbenchEditorService } from 'vs/workbench/services/editor/browser/editorService'; import { FileOperationError, FileOperationResult } from 'vs/platform/files/common/files'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IWorkbenchEditorService, DelegatingWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; import { IModeService } from 'vs/editor/common/services/modeService'; diff --git a/src/vs/workbench/browser/parts/editor/textEditor.ts b/src/vs/workbench/browser/parts/editor/textEditor.ts index b70eced071c..13522be113f 100644 --- a/src/vs/workbench/browser/parts/editor/textEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textEditor.ts @@ -66,7 +66,7 @@ export abstract class BaseTextEditor extends BaseEditor { ) { super(id, telemetryService, themeService); - this.toUnbind.push(this.configurationService.onDidUpdateConfiguration(e => this.handleConfigurationChangeEvent(this.configurationService.getConfiguration(this.getResource())))); + this.toUnbind.push(this.configurationService.onDidChangeConfiguration(e => this.handleConfigurationChangeEvent(this.configurationService.getConfiguration(this.getResource())))); } protected get instantiationService(): IInstantiationService { diff --git a/src/vs/workbench/browser/parts/panel/media/ellipsis-inverse.svg b/src/vs/workbench/browser/parts/panel/media/ellipsis-inverse.svg new file mode 100644 index 00000000000..1140c11ecc8 --- /dev/null +++ b/src/vs/workbench/browser/parts/panel/media/ellipsis-inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/panel/media/ellipsis.svg b/src/vs/workbench/browser/parts/panel/media/ellipsis.svg new file mode 100644 index 00000000000..b61e2d0c750 --- /dev/null +++ b/src/vs/workbench/browser/parts/panel/media/ellipsis.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/panel/media/panelpart.css b/src/vs/workbench/browser/parts/panel/media/panelpart.css index 10ffa06b7eb..042d150e84f 100644 --- a/src/vs/workbench/browser/parts/panel/media/panelpart.css +++ b/src/vs/workbench/browser/parts/panel/media/panelpart.css @@ -17,6 +17,13 @@ border-top-style: solid; padding-right: 0px; height: 35px; + display: flex; + flex-direction: row; + justify-content: space-between; +} + +.monaco-workbench > .part.panel > .composite.title > .title-actions { + flex: 0; } .monaco-workbench > .part.panel > .title > .title-actions .monaco-action-bar .action-item .action-label { @@ -25,6 +32,15 @@ /** Panel Switcher */ +.monaco-workbench > .part.panel > .title > .panel-switcher-container.composite-bar > .monaco-action-bar .action-label.toggle-more { + background: url('ellipsis.svg') center center no-repeat; + display: block; + height: 35px; + min-width: 28px; + margin-left: 0px; + margin-right: 0px; +} + .monaco-workbench > .part.panel > .title > .panel-switcher-container > .monaco-action-bar { line-height: 35px; } @@ -35,16 +51,28 @@ .monaco-workbench > .part.panel > .title > .panel-switcher-container > .monaco-action-bar .action-item .action-label { text-transform: uppercase; - margin-left: 16px; - margin-right: 16px; + margin-left: 12px; + margin-right: 20px; font-size: 11px; padding-bottom: 4px; /* puts the bottom border down */ } -.monaco-workbench > .part.panel > .title > .panel-switcher-container > .monaco-action-bar .action-item .action-label.checked { +.monaco-workbench > .part.panel > .title > .panel-switcher-container > .monaco-action-bar .action-item.checked .action-label { border-bottom: 1px solid; } +.monaco-workbench > .part.panel > .title > .panel-switcher-container > .monaco-action-bar .badge .badge-content { + top: 8px; + right: 0px; + position: absolute; + font-size: 11px; + min-width: 6px; + line-height: 18px; + padding: 0 5px; + border-radius: 20px; + text-align: center; +} + /** Actions */ .monaco-workbench .panel .monaco-action-bar .action-item.select-container { @@ -53,6 +81,7 @@ .monaco-workbench .panel .monaco-action-bar .action-item .select-box { cursor: pointer; + min-width: 100px; } .monaco-workbench .hide-panel-action { @@ -80,4 +109,9 @@ .vs-dark .monaco-workbench .hide-panel-action, .hc-black .monaco-workbench .hide-panel-action { background: url('close-inverse.svg') center center no-repeat; -} \ No newline at end of file +} + +.vs-dark .monaco-workbench > .part.panel > .title > .panel-switcher-container.composite-bar > .monaco-action-bar .action-label.toggle-more, +.hc-black .monaco-workbench > .part.panel > .title > .panel-switcher-container.composite-bar > .monaco-action-bar .action-label.toggle-more { + background: url('ellipsis-inverse.svg') center center no-repeat; +} diff --git a/src/vs/workbench/browser/parts/panel/panelActions.ts b/src/vs/workbench/browser/parts/panel/panelActions.ts index 238b3b33b11..759cb22cbf6 100644 --- a/src/vs/workbench/browser/parts/panel/panelActions.ts +++ b/src/vs/workbench/browser/parts/panel/panelActions.ts @@ -15,6 +15,8 @@ import { IWorkbenchActionRegistry, Extensions as WorkbenchExtensions } from 'vs/ import { IPanelService, IPanelIdentifier } from 'vs/workbench/services/panel/common/panelService'; import { IPartService, Parts } from 'vs/workbench/services/part/common/partService'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { ActivityAction } from 'vs/workbench/browser/parts/compositebar/compositeBarActions'; +import { IActivity } from 'vs/workbench/common/activity'; export class OpenPanelAction extends Action { @@ -149,6 +151,20 @@ export class ToggleMaximizedPanelAction extends Action { } } +export class PanelActivityAction extends ActivityAction { + + constructor( + activity: IActivity, + @IPanelService private panelService: IPanelService + ) { + super(activity); + } + + public run(event: any): TPromise { + return this.panelService.openPanel(this.activity.id, true).then(() => this.activate()); + } +} + const actionRegistry = Registry.as(WorkbenchExtensions.WorkbenchActions); actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(TogglePanelAction, TogglePanelAction.ID, TogglePanelAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_J }), 'View: Toggle Panel', nls.localize('view', "View")); actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusPanelAction, FocusPanelAction.ID, FocusPanelAction.LABEL), 'View: Focus into Panel', nls.localize('view', "View")); diff --git a/src/vs/workbench/browser/parts/panel/panelPart.ts b/src/vs/workbench/browser/parts/panel/panelPart.ts index 1608bfe44d2..66485230cdf 100644 --- a/src/vs/workbench/browser/parts/panel/panelPart.ts +++ b/src/vs/workbench/browser/parts/panel/panelPart.ts @@ -4,12 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./media/panelpart'; -import nls = require('vs/nls'); import { TPromise } from 'vs/base/common/winjs.base'; -import { IAction } from 'vs/base/common/actions'; +import { IAction, Action } from 'vs/base/common/actions'; import Event from 'vs/base/common/event'; -import { Builder, $ } from 'vs/base/browser/builder'; +import { Builder, Dimension } from 'vs/base/browser/builder'; import { Registry } from 'vs/platform/registry/common/platform'; +import { ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar'; import { Scope } from 'vs/workbench/browser/actions'; import { IPanel } from 'vs/workbench/common/panel'; import { CompositePart, ICompositeTitleLabel } from 'vs/workbench/browser/parts/compositePart'; @@ -22,22 +22,26 @@ import { IMessageService } from 'vs/platform/message/common/message'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { ActionsOrientation, ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; -import { ClosePanelAction, OpenPanelAction, ToggleMaximizedPanelAction } from 'vs/workbench/browser/parts/panel/panelActions'; +import { ClosePanelAction, ToggleMaximizedPanelAction, PanelActivityAction, OpenPanelAction } from 'vs/workbench/browser/parts/panel/panelActions'; import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; -import { PANEL_BACKGROUND, PANEL_BORDER, PANEL_ACTIVE_TITLE_FOREGROUND, PANEL_INACTIVE_TITLE_FOREGROUND, PANEL_ACTIVE_TITLE_BORDER } from 'vs/workbench/common/theme'; -import { activeContrastBorder, focusBorder, contrastBorder, editorBackground } from 'vs/platform/theme/common/colorRegistry'; +import { PANEL_BACKGROUND, PANEL_BORDER, PANEL_ACTIVE_TITLE_FOREGROUND, PANEL_INACTIVE_TITLE_FOREGROUND, PANEL_ACTIVE_TITLE_BORDER, PANEL_DRAG_AND_DROP_BACKGROUND } from 'vs/workbench/common/theme'; +import { activeContrastBorder, focusBorder, contrastBorder, editorBackground, badgeBackground, badgeForeground } from 'vs/platform/theme/common/colorRegistry'; +import { CompositeBar } from 'vs/workbench/browser/parts/compositebar/compositeBar'; +import { ToggleCompositePinnedAction } from 'vs/workbench/browser/parts/compositebar/compositeBarActions'; +import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; +import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { IBadge } from 'vs/workbench/services/activity/common/activity'; export class PanelPart extends CompositePart implements IPanelService { public static activePanelSettingsKey = 'workbench.panelpart.activepanelid'; + private static readonly PINNED_PANELS = 'workbench.panel.pinnedPanels'; public _serviceBrand: any; private blockOpeningPanel: boolean; - private panelSwitcherBar: ActionBar; - - private panelIdToActions: { [panelId: string]: OpenPanelAction; }; + private compositeBar: CompositeBar; + private dimension: Dimension; constructor( id: string, @@ -48,7 +52,7 @@ export class PanelPart extends CompositePart implements IPanelService { @IPartService partService: IPartService, @IKeybindingService keybindingService: IKeybindingService, @IInstantiationService instantiationService: IInstantiationService, - @IThemeService themeService: IThemeService + @IThemeService themeService: IThemeService, ) { super( messageService, @@ -70,7 +74,26 @@ export class PanelPart extends CompositePart implements IPanelService { { hasTitle: true } ); - this.panelIdToActions = Object.create(null); + this.compositeBar = this.instantiationService.createInstance(CompositeBar, { + icon: false, + storageId: PanelPart.PINNED_PANELS, + orientation: ActionsOrientation.HORIZONTAL, + composites: this.getPanels(), + openComposite: (compositeId: string) => this.openPanel(compositeId, true), + getActivityAction: (compositeId: string) => this.instantiationService.createInstance(PanelActivityAction, this.getPanel(compositeId)), + getCompositePinnedAction: (compositeId: string) => new ToggleCompositePinnedAction(this.getPanel(compositeId), this.compositeBar), + getOnCompositeClickAction: (compositeId: string) => this.instantiationService.createInstance(OpenPanelAction, this.getPanel(compositeId)), + getDefaultCompositeId: () => Registry.as(PanelExtensions.Panels).getDefaultPanelId(), + hidePart: () => this.partService.setPanelHidden(true), + overflowActionSize: 28, + colors: { + backgroundColor: PANEL_BACKGROUND, + badgeBackground, + badgeForeground, + dragAndDropBackground: PANEL_DRAG_AND_DROP_BACKGROUND + } + }); + this.toUnbind.push(this.compositeBar); this.registerListeners(); } @@ -78,16 +101,15 @@ export class PanelPart extends CompositePart implements IPanelService { private registerListeners(): void { // Activate panel action on opening of a panel - this.toUnbind.push(this.onDidPanelOpen(panel => this.updatePanelActions(panel.getId(), true))); + this.toUnbind.push(this.onDidPanelOpen(panel => { + this.compositeBar.activateComposite(panel.getId()); + // Need to relayout composite bar since different panels have different action bar width + this.layoutCompositeBar(); + })); // Deactivate panel action on close - this.toUnbind.push(this.onDidPanelClose(panel => this.updatePanelActions(panel.getId(), false))); - } - - private updatePanelActions(id: string, didOpen: boolean): void { - if (this.panelIdToActions[id]) { - didOpen ? this.panelIdToActions[id].activate() : this.panelIdToActions[id].deactivate(); - } + this.toUnbind.push(this.onDidPanelClose(panel => this.compositeBar.deactivateComposite(panel.getId()))); + this.toUnbind.push(this.compositeBar.onDidContextMenu(e => this.showContextMenu(e))); } public get onDidPanelOpen(): Event { @@ -127,6 +149,25 @@ export class PanelPart extends CompositePart implements IPanelService { return promise.then(() => this.openComposite(id, focus)); } + public showActivity(panelId: string, badge: IBadge, clazz?: string): IDisposable { + return this.compositeBar.showActivity(panelId, badge, clazz); + } + + private getPanel(panelId: string): IPanelIdentifier { + return Registry.as(PanelExtensions.Panels).getPanels().filter(p => p.id === panelId).pop(); + } + + private showContextMenu(e: MouseEvent): void { + const event = new StandardMouseEvent(e); + const actions: Action[] = this.getPanels().map(panel => this.instantiationService.createInstance(ToggleCompositePinnedAction, panel, this.compositeBar)); + + this.contextMenuService.showContextMenu({ + getAnchor: () => { return { x: event.posx, y: event.posy }; }, + getActions: () => TPromise.as(actions), + onHide: () => dispose(actions) + }); + } + public getPanels(): IPanelIdentifier[] { return Registry.as(PanelExtensions.Panels).getPanels() .sort((v1, v2) => v1.order - v2.order); @@ -152,23 +193,12 @@ export class PanelPart extends CompositePart implements IPanelService { } protected createTitleLabel(parent: Builder): ICompositeTitleLabel { - let titleArea = $(parent).div({ - 'class': ['panel-switcher-container'] - }); - - // Show a panel switcher - this.panelSwitcherBar = new ActionBar(titleArea, { - orientation: ActionsOrientation.HORIZONTAL, - ariaLabel: nls.localize('panelSwitcherBarAriaLabel', "Active Panel Switcher"), - animated: false - }); - this.toUnbind.push(this.panelSwitcherBar); - - this.fillPanelSwitcher(); + const titleArea = this.compositeBar.create(parent.getHTMLElement()); + titleArea.classList.add('panel-switcher-container'); return { updateTitle: (id, title, keybinding) => { - const action = this.panelIdToActions[id]; + const action = this.compositeBar.getAction(id); if (action) { action.label = title; } @@ -179,17 +209,33 @@ export class PanelPart extends CompositePart implements IPanelService { }; } - private fillPanelSwitcher(): void { - const panels = this.getPanels(); + public layout(dimension: Dimension): Dimension[] { - this.panelSwitcherBar.push(panels.map(panel => { - const action = this.instantiationService.createInstance(OpenPanelAction, panel); + // Pass to super + const sizes = super.layout(dimension); + this.dimension = dimension; + this.layoutCompositeBar(); - this.panelIdToActions[panel.id] = action; - this.toUnbind.push(action); + return sizes; + } - return action; - })); + private layoutCompositeBar(): void { + if (this.dimension) { + let availableWidth = this.dimension.width - 8; // take padding into account + if (this.toolBar) { + // adjust height for global actions showing + availableWidth -= this.toolBar.getContainer().getHTMLElement().offsetWidth; + } + this.compositeBar.layout(new Dimension(availableWidth, this.dimension.height)); + } + } + + public shutdown(): void { + // Persist Hidden State + this.compositeBar.store(); + + // Pass to super + super.shutdown(); } } @@ -215,7 +261,7 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { if (titleActive || titleActiveBorder) { collector.addRule(` .monaco-workbench > .part.panel > .title > .panel-switcher-container > .monaco-action-bar .action-item:hover .action-label, - .monaco-workbench > .part.panel > .title > .panel-switcher-container > .monaco-action-bar .action-item .action-label.checked { + .monaco-workbench > .part.panel > .title > .panel-switcher-container > .monaco-action-bar .action-item.checked .action-label { color: ${titleActive}; border-bottom-color: ${titleActiveBorder}; } @@ -236,7 +282,7 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const focusBorderColor = theme.getColor(focusBorder); if (focusBorderColor) { collector.addRule(` - .monaco-workbench > .part.panel > .title > .panel-switcher-container > .monaco-action-bar .action-item .action-label:focus { + .monaco-workbench > .part.panel > .title > .panel-switcher-container > .monaco-action-bar .action-item:focus { color: ${titleActive}; border-bottom-color: ${focusBorderColor} !important; border-bottom: 1px solid; @@ -251,7 +297,7 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const outline = theme.getColor(activeContrastBorder); collector.addRule(` - .monaco-workbench > .part.panel > .title > .panel-switcher-container > .monaco-action-bar .action-item .action-label.checked, + .monaco-workbench > .part.panel > .title > .panel-switcher-container > .monaco-action-bar .action-item.checked .action-label, .monaco-workbench > .part.panel > .title > .panel-switcher-container > .monaco-action-bar .action-item .action-label:hover { outline-color: ${outline}; outline-width: 1px; @@ -261,7 +307,7 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { outline-offset: 3px; } - .monaco-workbench > .part.panel > .title > .panel-switcher-container > .monaco-action-bar .action-item .action-label:hover:not(.checked) { + .monaco-workbench > .part.panel > .title > .panel-switcher-container > .monaco-action-bar .action-item:not(.checked) .action-label:hover { outline-style: dashed; } `); diff --git a/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts b/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts index 00bd2d8d83a..b9efab5784e 100644 --- a/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts +++ b/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts @@ -36,7 +36,7 @@ import { Component } from 'vs/workbench/common/component'; import Event, { Emitter } from 'vs/base/common/event'; import { IPartService } from 'vs/workbench/services/part/common/partService'; import { KeyMod } from 'vs/base/common/keyCodes'; -import { QuickOpenHandler, QuickOpenHandlerDescriptor, IQuickOpenRegistry, Extensions, EditorQuickOpenEntry, IWorkbenchQuickOpenConfiguration } from 'vs/workbench/browser/quickopen'; +import { QuickOpenHandler, QuickOpenHandlerDescriptor, IQuickOpenRegistry, Extensions, EditorQuickOpenEntry, CLOSE_ON_FOCUS_LOST_CONFIG } from 'vs/workbench/browser/quickopen'; import errors = require('vs/base/common/errors'); import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IPickOpenEntry, IFilePickOpenEntry, IInputOptions, IQuickOpenService, IPickOptions, IShowOptions, IPickOpenItem } from 'vs/platform/quickOpen/common/quickOpen'; @@ -55,7 +55,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' import { ITree, IActionProvider } from 'vs/base/parts/tree/browser/tree'; import { BaseActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { FileKind, IFileService } from 'vs/platform/files/common/files'; -import { scoreItem, ScorerCache, compareItemsByScore, massageSearchForScoring } from 'vs/base/parts/quickopen/common/quickOpenScorer'; +import { scoreItem, ScorerCache, compareItemsByScore, prepareQuery } from 'vs/base/parts/quickopen/common/quickOpenScorer'; const HELP_PREFIX = '?'; @@ -131,22 +131,22 @@ export class QuickOpenController extends Component implements IQuickOpenService this._onShow = new Emitter(); this._onHide = new Emitter(); - this.updateConfiguration(this.configurationService.getConfiguration()); + this.updateConfiguration(); this.registerListeners(); } private registerListeners(): void { - this.toUnbind.push(this.configurationService.onDidUpdateConfiguration(e => this.updateConfiguration(this.configurationService.getConfiguration()))); + this.toUnbind.push(this.configurationService.onDidChangeConfiguration(e => this.updateConfiguration())); this.toUnbind.push(this.partService.onTitleBarVisibilityChange(() => this.positionQuickOpenWidget())); this.toUnbind.push(browser.onDidChangeZoomLevel(() => this.positionQuickOpenWidget())); } - private updateConfiguration(settings: IWorkbenchQuickOpenConfiguration): void { + private updateConfiguration(): void { if (this.environmentService.args['sticky-quickopen']) { this.closeOnFocusLost = false; } else { - this.closeOnFocusLost = settings.workbench && settings.workbench.quickOpen && settings.workbench.quickOpen.closeOnFocusLost; + this.closeOnFocusLost = this.configurationService.getValue(CLOSE_ON_FOCUS_LOST_CONFIG); } } @@ -545,6 +545,7 @@ export class QuickOpenController extends Component implements IQuickOpenService public show(prefix?: string, options?: IShowOptions): TPromise { let quickNavigateConfiguration = options ? options.quickNavigateConfiguration : void 0; let inputSelection = options ? options.inputSelection : void 0; + let autoFocus = options ? options.autoFocus : void 0; this.previousValue = prefix; @@ -565,8 +566,7 @@ export class QuickOpenController extends Component implements IQuickOpenService this.telemetryService.publicLog('quickOpenWidgetShown', { mode: handlerDescriptor.getId(), quickNavigate: quickNavigateConfiguration }); // Trigger onOpen - this.resolveHandler(handlerDescriptor) - .done(null, errors.onUnexpectedError); + this.resolveHandler(handlerDescriptor).done(null, errors.onUnexpectedError); // Create upon first open if (!this.quickOpenWidget) { @@ -601,19 +601,21 @@ export class QuickOpenController extends Component implements IQuickOpenService // Show quick open with prefix or editor history if (!this.quickOpenWidget.isVisible() || quickNavigateConfiguration) { if (prefix) { - this.quickOpenWidget.show(prefix, { quickNavigateConfiguration, inputSelection }); + this.quickOpenWidget.show(prefix, { quickNavigateConfiguration, inputSelection, autoFocus }); } else { const editorHistory = this.getEditorHistoryWithGroupLabel(); if (editorHistory.getEntries().length < 2) { quickNavigateConfiguration = null; // If no entries can be shown, default to normal quick open mode } - let autoFocus: IAutoFocus; - if (!quickNavigateConfiguration) { - autoFocus = { autoFocusFirstEntry: true }; - } else { - const visibleEditorCount = this.editorService.getVisibleEditors().length; - autoFocus = { autoFocusFirstEntry: visibleEditorCount === 0, autoFocusSecondEntry: visibleEditorCount !== 0 }; + // Compute auto focus + if (!autoFocus) { + if (!quickNavigateConfiguration) { + autoFocus = { autoFocusFirstEntry: true }; + } else { + const visibleEditorCount = this.editorService.getVisibleEditors().length; + autoFocus = { autoFocusFirstEntry: visibleEditorCount === 0, autoFocusSecondEntry: visibleEditorCount !== 0 }; + } } // Update context @@ -1179,17 +1181,17 @@ class EditorHistoryHandler { public getResults(searchValue?: string): QuickOpenEntry[] { // Massage search for scoring - searchValue = massageSearchForScoring(searchValue); + const query = prepareQuery(searchValue); // Just return all if we are not searching const history = this.historyService.getHistory(); - if (!searchValue) { + if (!query.value) { return history.map(input => this.instantiationService.createInstance(EditorHistoryEntry, input)); } // Otherwise filter by search value and sort by score. Include matches on description // in case the user is explicitly including path separators. - const accessor = searchValue.indexOf(paths.nativeSep) >= 0 ? MatchOnDescription : DoNotMatchOnDescription; + const accessor = query.containsPathSeparator ? MatchOnDescription : DoNotMatchOnDescription; return history // For now, only support to match on inputs that provide resource information @@ -1209,7 +1211,7 @@ class EditorHistoryHandler { // Make sure the search value is matching .filter(e => { - const itemScore = scoreItem(e, searchValue, false, accessor, this.scorerCache); + const itemScore = scoreItem(e, query, false, accessor, this.scorerCache); if (!itemScore.score) { return false; } @@ -1221,7 +1223,7 @@ class EditorHistoryHandler { // Sort by score and provide a fallback sorter that keeps the // recency of items in case the score for items is the same - .sort((e1, e2) => compareItemsByScore(e1, e2, searchValue, false, accessor, this.scorerCache, (e1, e2, searchValue, accessor) => -1)); + .sort((e1, e2) => compareItemsByScore(e1, e2, query, false, accessor, this.scorerCache, (e1, e2, query, accessor) => -1)); } } diff --git a/src/vs/workbench/browser/parts/quickopen/quickopen.ts b/src/vs/workbench/browser/parts/quickopen/quickopen.ts index 4b0331d645c..9138bb4c03c 100644 --- a/src/vs/workbench/browser/parts/quickopen/quickopen.ts +++ b/src/vs/workbench/browser/parts/quickopen/quickopen.ts @@ -29,6 +29,15 @@ CommandsRegistry.registerCommand(QUICKOPEN_ACTION_ID, function (accessor: Servic }); }); +export const QUICKOPEN_FOCUS_SECONDARY_ACTION_ID = 'workbench.action.quickOpenPreviousEditor'; +CommandsRegistry.registerCommand(QUICKOPEN_FOCUS_SECONDARY_ACTION_ID, function (accessor: ServicesAccessor, prefix: string = null) { + const quickOpenService = accessor.get(IQuickOpenService); + + return quickOpenService.show(null, { autoFocus: { autoFocusSecondEntry: true } }).then(() => { + return void 0; + }); +}); + export class BaseQuickOpenNavigateAction extends Action { constructor( diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index 806c5d7d2c8..9a3ec23a52f 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -18,7 +18,7 @@ import * as errors from 'vs/base/common/errors'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { IAction, Action } from 'vs/base/common/actions'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; import { IIntegrityService } from 'vs/platform/integrity/common/integrity'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; @@ -33,6 +33,7 @@ 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'; +import URI from 'vs/base/common/uri'; export class TitlebarPart extends Part implements ITitleService { @@ -51,7 +52,6 @@ export class TitlebarPart extends Part implements ITitleService { private isInactive: boolean; - private titleTemplate: string; private isPure: boolean; private activeEditorListeners: IDisposable[]; @@ -81,9 +81,6 @@ export class TitlebarPart extends Part implements ITitleService { private init(): void { - // Read initial config - this.onConfigurationChanged(); - // Initial window title when loading is done this.partService.joinCreation().done(() => this.setTitle(this.getWindowTitle())); @@ -99,7 +96,7 @@ export class TitlebarPart extends Part implements ITitleService { private registerListeners(): void { this.toUnbind.push(DOM.addDisposableListener(window, DOM.EventType.BLUR, () => this.onBlur())); this.toUnbind.push(DOM.addDisposableListener(window, DOM.EventType.FOCUS, () => this.onFocus())); - this.toUnbind.push(this.configurationService.onDidUpdateConfiguration(() => this.onConfigurationChanged(true))); + this.toUnbind.push(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationChanged(e))); this.toUnbind.push(this.editorGroupService.onEditorsChanged(() => this.onEditorsChanged())); this.toUnbind.push(this.contextService.onDidChangeWorkspaceFolders(() => this.setTitle(this.getWindowTitle()))); this.toUnbind.push(this.contextService.onDidChangeWorkbenchState(() => this.setTitle(this.getWindowTitle()))); @@ -116,11 +113,8 @@ export class TitlebarPart extends Part implements ITitleService { this.updateStyles(); } - private onConfigurationChanged(update?: boolean): void { - const currentTitleTemplate = this.titleTemplate; - this.titleTemplate = this.configurationService.lookup('window.title').value; - - if (update && currentTitleTemplate !== this.titleTemplate) { + private onConfigurationChanged(event: IConfigurationChangeEvent): void { + if (event.affectsConfiguration('window.title')) { this.setTitle(this.getWindowTitle()); } } @@ -185,7 +179,7 @@ export class TitlebarPart extends Part implements ITitleService { const input = this.editorService.getActiveEditorInput(); const workspace = this.contextService.getWorkspace(); - let root; + let root: URI; if (workspace.configuration) { root = workspace.configuration; } else if (workspace.folders.length) { @@ -208,8 +202,9 @@ export class TitlebarPart extends Part implements ITitleService { const dirty = input && input.isDirty() ? TitlebarPart.TITLE_DIRTY : ''; const appName = this.environmentService.appNameLong; const separator = TitlebarPart.TITLE_SEPARATOR; + const titleTemplate = this.configurationService.getValue('window.title'); - return labels.template(this.titleTemplate, { + return labels.template(titleTemplate, { activeEditorShort, activeEditorLong, activeEditorMedium, diff --git a/src/vs/workbench/browser/parts/views/viewsRegistry.ts b/src/vs/workbench/browser/parts/views/viewsRegistry.ts index fbdc2e1c425..e39b999ec43 100644 --- a/src/vs/workbench/browser/parts/views/viewsRegistry.ts +++ b/src/vs/workbench/browser/parts/views/viewsRegistry.ts @@ -48,6 +48,8 @@ export interface IViewDescriptor { readonly size?: number; + readonly collapsed?: boolean; + readonly canToggleVisibility?: boolean; } diff --git a/src/vs/workbench/browser/parts/views/viewsViewlet.ts b/src/vs/workbench/browser/parts/views/viewsViewlet.ts index 29e5639eacd..96ef5e0c9de 100644 --- a/src/vs/workbench/browser/parts/views/viewsViewlet.ts +++ b/src/vs/workbench/browser/parts/views/viewsViewlet.ts @@ -400,7 +400,7 @@ export class ViewsViewlet extends PanelViewlet { id: viewDescriptor.id, name: viewDescriptor.name, actionRunner: this.getActionRunner(), - expanded: !(viewState ? viewState.collapsed : void 0), + expanded: !(viewState ? viewState.collapsed : viewDescriptor.collapsed), viewletSettings: this.viewletSettings }); toCreate.push(view); diff --git a/src/vs/workbench/browser/quickopen.ts b/src/vs/workbench/browser/quickopen.ts index 07d0289da9e..8c7cf7bea1e 100644 --- a/src/vs/workbench/browser/quickopen.ts +++ b/src/vs/workbench/browser/quickopen.ts @@ -22,11 +22,10 @@ import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/edi import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; import { IConstructorSignature0, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +export const CLOSE_ON_FOCUS_LOST_CONFIG = 'workbench.quickOpen.closeOnFocusLost'; + export interface IWorkbenchQuickOpenConfiguration { workbench: { - quickOpen: { - closeOnFocusLost: boolean; - }, commandPalette: { history: number; preserveInput: boolean; diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index 440ef0a6265..6ec676c9306 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -11,7 +11,7 @@ import types = require('vs/base/common/types'); import URI from 'vs/base/common/uri'; import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; import { IEditor, IEditorViewState, IModel, ScrollType } from 'vs/editor/common/editorCommon'; -import { IEditorInput, IEditorModel, IEditorOptions, ITextEditorOptions, IBaseResourceInput, Position, Verbosity } from 'vs/platform/editor/common/editor'; +import { IEditorInput, IEditorModel, IEditorOptions, ITextEditorOptions, IBaseResourceInput, Position, Verbosity, IEditor as IBaseEditor } from 'vs/platform/editor/common/editor'; import { IInstantiationService, IConstructorSignature0 } from 'vs/platform/instantiation/common/instantiation'; import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -260,6 +260,48 @@ export abstract class EditorInput implements IEditorInput { } } +export interface IEditorOpeningEvent { + input: IEditorInput; + options?: IEditorOptions; + position: Position; + + /** + * Allows to prevent the opening of an editor by providing a callback + * that will be executed instead. By returning another editor promise + * it is possible to override the opening with another editor. It is ok + * to return a promise that resolves to NULL to prevent the opening + * altogether. + */ + prevent(callback: () => TPromise): void; +} + +export class EditorOpeningEvent { + private override: () => TPromise; + + constructor(private _input: IEditorInput, private _options: IEditorOptions, private _position: Position) { + } + + public get input(): IEditorInput { + return this._input; + } + + public get options(): IEditorOptions { + return this._options; + } + + public get position(): Position { + return this._position; + } + + public prevent(callback: () => TPromise): void { + this.override = callback; + } + + public isPrevented(): () => TPromise { + return this.override; + } +} + export enum EncodingMode { /** @@ -762,6 +804,8 @@ export const EditorOpenPositioning = { LAST: 'last' }; +export const OPEN_POSITIONING_CONFIG = 'workbench.editor.openPositioning'; + export interface IWorkbenchEditorConfiguration { /* __GDPR__FRAGMENT__ "IWorkbenchEditorConfiguration" : { diff --git a/src/vs/workbench/common/editor/editorStacksModel.ts b/src/vs/workbench/common/editor/editorStacksModel.ts index 26d0dca609d..20f09e32702 100644 --- a/src/vs/workbench/common/editor/editorStacksModel.ts +++ b/src/vs/workbench/common/editor/editorStacksModel.ts @@ -6,11 +6,11 @@ 'use strict'; import Event, { Emitter, once } from 'vs/base/common/event'; -import { Extensions, IEditorInputFactoryRegistry, EditorInput, toResource, IEditorStacksModel, IEditorGroup, IEditorIdentifier, IEditorCloseEvent, GroupIdentifier, IStacksModelChangeEvent, IWorkbenchEditorConfiguration, EditorOpenPositioning, SideBySideEditorInput } from 'vs/workbench/common/editor'; +import { Extensions, IEditorInputFactoryRegistry, EditorInput, toResource, IEditorStacksModel, IEditorGroup, IEditorIdentifier, IEditorCloseEvent, GroupIdentifier, IStacksModelChangeEvent, EditorOpenPositioning, SideBySideEditorInput, OPEN_POSITIONING_CONFIG } from 'vs/workbench/common/editor'; import URI from 'vs/base/common/uri'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -84,7 +84,7 @@ export class EditorGroup implements IEditorGroup { this.mru = []; this.toDispose = []; this.mapResourceToEditorCount = new ResourceMap(); - this.onConfigurationUpdated(configurationService.getConfiguration()); + this.onConfigurationUpdated(); this._onEditorActivated = new Emitter(); this._onEditorOpened = new Emitter(); @@ -110,13 +110,11 @@ export class EditorGroup implements IEditorGroup { } private registerListeners(): void { - this.toDispose.push(this.configurationService.onDidUpdateConfiguration(e => this.onConfigurationUpdated(this.configurationService.getConfiguration()))); + this.toDispose.push(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated(e))); } - private onConfigurationUpdated(config: IWorkbenchEditorConfiguration): void { - if (config && config.workbench && config.workbench.editor) { - this.editorOpenPositioning = config.workbench.editor.openPositioning; - } + private onConfigurationUpdated(event?: IConfigurationChangeEvent): void { + this.editorOpenPositioning = this.configurationService.getValue(OPEN_POSITIONING_CONFIG); } public get id(): GroupIdentifier { diff --git a/src/vs/workbench/common/editor/untitledEditorModel.ts b/src/vs/workbench/common/editor/untitledEditorModel.ts index 5442ba80ab6..278e1bdcad8 100644 --- a/src/vs/workbench/common/editor/untitledEditorModel.ts +++ b/src/vs/workbench/common/editor/untitledEditorModel.ts @@ -94,7 +94,7 @@ export class UntitledEditorModel extends BaseTextEditorModel implements IEncodin private registerListeners(): void { // Config Changes - this.toDispose.push(this.configurationService.onDidUpdateConfiguration(e => this.onConfigurationChange())); + this.toDispose.push(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationChange())); } private onConfigurationChange(): void { diff --git a/src/vs/workbench/common/resources.ts b/src/vs/workbench/common/resources.ts index ec82d07b99b..3a4d1381e61 100644 --- a/src/vs/workbench/common/resources.ts +++ b/src/vs/workbench/common/resources.ts @@ -11,8 +11,8 @@ import paths = require('vs/base/common/paths'); import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import Event, { Emitter } from 'vs/base/common/event'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ParsedExpression, IExpression } from 'vs/base/common/glob'; +import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; +import { ParsedExpression, IExpression, parse } from 'vs/base/common/glob'; import { basename } from 'vs/base/common/paths'; import { RawContextKey, IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IModeService } from 'vs/editor/common/services/modeService'; @@ -73,7 +73,7 @@ export class ResourceGlobMatcher { constructor( private globFn: (root?: URI) => IExpression, - private parseFn: (expression: IExpression) => ParsedExpression, + private shouldUpdate: (event: IConfigurationChangeEvent) => boolean, @IWorkspaceContextService private contextService: IWorkspaceContextService, @IConfigurationService private configurationService: IConfigurationService ) { @@ -95,7 +95,11 @@ export class ResourceGlobMatcher { } private registerListeners(): void { - this.toUnbind.push(this.configurationService.onDidUpdateConfiguration(() => this.updateExcludes(true))); + this.toUnbind.push(this.configurationService.onDidChangeConfiguration(e => { + if (this.shouldUpdate(e)) { + this.updateExcludes(true); + } + })); this.toUnbind.push(this.contextService.onDidChangeWorkspaceFolders(() => this.updateExcludes(true))); } @@ -108,7 +112,7 @@ export class ResourceGlobMatcher { if (!this.mapRootToExpressionConfig.has(folder.uri.toString()) || !objects.equals(this.mapRootToExpressionConfig.get(folder.uri.toString()), rootExcludes)) { changed = true; - this.mapRootToParsedExpression.set(folder.uri.toString(), this.parseFn(rootExcludes)); + this.mapRootToParsedExpression.set(folder.uri.toString(), parse(rootExcludes)); this.mapRootToExpressionConfig.set(folder.uri.toString(), objects.clone(rootExcludes)); } }); @@ -132,7 +136,7 @@ export class ResourceGlobMatcher { if (!this.mapRootToExpressionConfig.has(ResourceGlobMatcher.NO_ROOT) || !objects.equals(this.mapRootToExpressionConfig.get(ResourceGlobMatcher.NO_ROOT), globalExcludes)) { changed = true; - this.mapRootToParsedExpression.set(ResourceGlobMatcher.NO_ROOT, this.parseFn(globalExcludes)); + this.mapRootToParsedExpression.set(ResourceGlobMatcher.NO_ROOT, parse(globalExcludes)); this.mapRootToExpressionConfig.set(ResourceGlobMatcher.NO_ROOT, objects.clone(globalExcludes)); } diff --git a/src/vs/workbench/common/theme.ts b/src/vs/workbench/common/theme.ts index 5097ff16f1c..7aaa6c71af5 100644 --- a/src/vs/workbench/common/theme.ts +++ b/src/vs/workbench/common/theme.ts @@ -138,6 +138,11 @@ export const PANEL_ACTIVE_TITLE_BORDER = registerColor('panelTitle.activeBorder' hc: contrastBorder }, nls.localize('panelActiveTitleBorder', "Border color for the active panel title. Panels are shown below the editor area and contain views like output and integrated terminal.")); +export const PANEL_DRAG_AND_DROP_BACKGROUND = registerColor('panel.dropBackground', { + dark: Color.white.transparent(0.12), + light: Color.fromHex('#3399FF').transparent(0.18), + hc: Color.white.transparent(0.12) +}, nls.localize('panelDragAndDropBackground', "Drag and drop feedback color for the panel title items. The color should have transparency so that the panel entries can still shine through. Panels are shown below the editor area and contain views like output and integrated terminal.")); // < --- Status --- > @@ -224,6 +229,7 @@ export const ACTIVITY_BAR_BORDER = registerColor('activityBar.border', { hc: contrastBorder }, nls.localize('activityBarBorder', "Activity bar border color separating to the side bar. The activity bar is showing on the far left or right and allows to switch between views of the side bar.")); + export const ACTIVITY_BAR_DRAG_AND_DROP_BACKGROUND = registerColor('activityBar.dropBackground', { dark: Color.white.transparent(0.12), light: Color.white.transparent(0.12), @@ -243,7 +249,6 @@ export const ACTIVITY_BAR_BADGE_FOREGROUND = registerColor('activityBarBadge.for }, nls.localize('activityBarBadgeForeground', "Activity notification badge foreground color. The activity bar is showing on the far left or right and allows to switch between views of the side bar.")); - // < --- Side Bar --- > export const SIDE_BAR_BACKGROUND = registerColor('sideBar.background', { diff --git a/src/vs/workbench/electron-browser/actions.ts b/src/vs/workbench/electron-browser/actions.ts index 67796b9bc2f..95281cf2338 100644 --- a/src/vs/workbench/electron-browser/actions.ts +++ b/src/vs/workbench/electron-browser/actions.ts @@ -20,8 +20,7 @@ import errors = require('vs/base/common/errors'); import { IMessageService, Severity } from 'vs/platform/message/common/message'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { IExtensionManagementService, LocalExtensionType, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import paths = require('vs/base/common/paths'); @@ -157,14 +156,13 @@ export class ToggleMenuBarAction extends Action { id: string, label: string, @IMessageService private messageService: IMessageService, - @IConfigurationService private configurationService: IConfigurationService, - @IConfigurationEditingService private configurationEditingService: IConfigurationEditingService + @IConfigurationService private configurationService: IConfigurationService ) { super(id, label); } public run(): TPromise { - let currentVisibilityValue = this.configurationService.lookup(ToggleMenuBarAction.menuBarVisibilityKey).value; + let currentVisibilityValue = this.configurationService.getValue(ToggleMenuBarAction.menuBarVisibilityKey); if (typeof currentVisibilityValue !== 'string') { currentVisibilityValue = 'default'; } @@ -176,7 +174,7 @@ export class ToggleMenuBarAction extends Action { newVisibilityValue = 'default'; } - this.configurationEditingService.writeConfiguration(ConfigurationTarget.USER, { key: ToggleMenuBarAction.menuBarVisibilityKey, value: newVisibilityValue }); + this.configurationService.updateValue(ToggleMenuBarAction.menuBarVisibilityKey, newVisibilityValue, ConfigurationTarget.USER); return TPromise.as(null); } @@ -202,18 +200,12 @@ export abstract class BaseZoomAction extends Action { constructor( id: string, label: string, - @IWorkspaceConfigurationService private configurationService: IWorkspaceConfigurationService, - @IConfigurationEditingService private configurationEditingService: IConfigurationEditingService + @IWorkspaceConfigurationService private configurationService: IWorkspaceConfigurationService ) { super(id, label); } protected setConfiguredZoomLevel(level: number): void { - let target = ConfigurationTarget.USER; - if (typeof this.configurationService.lookup(BaseZoomAction.SETTING_KEY).workspace === 'number') { - target = ConfigurationTarget.WORKSPACE; - } - level = Math.round(level); // when reaching smallest zoom, prevent fractional zoom levels const applyZoom = () => { @@ -225,7 +217,7 @@ export abstract class BaseZoomAction extends Action { browser.setZoomLevel(webFrame.getZoomLevel(), /*isTrusted*/false); }; - this.configurationEditingService.writeConfiguration(target, { key: BaseZoomAction.SETTING_KEY, value: level }, { donotNotifyError: true }).done(() => applyZoom(), error => applyZoom()); + this.configurationService.updateValue(BaseZoomAction.SETTING_KEY, level).done(() => applyZoom()); } } @@ -237,10 +229,9 @@ export class ZoomInAction extends BaseZoomAction { constructor( id: string, label: string, - @IWorkspaceConfigurationService configurationService: IWorkspaceConfigurationService, - @IConfigurationEditingService configurationEditingService: IConfigurationEditingService + @IWorkspaceConfigurationService configurationService: IWorkspaceConfigurationService ) { - super(id, label, configurationService, configurationEditingService); + super(id, label, configurationService); } public run(): TPromise { @@ -258,10 +249,9 @@ export class ZoomOutAction extends BaseZoomAction { constructor( id: string, label: string, - @IWorkspaceConfigurationService configurationService: IWorkspaceConfigurationService, - @IConfigurationEditingService configurationEditingService: IConfigurationEditingService + @IWorkspaceConfigurationService configurationService: IWorkspaceConfigurationService ) { - super(id, label, configurationService, configurationEditingService); + super(id, label, configurationService); } public run(): TPromise { @@ -279,10 +269,9 @@ export class ZoomResetAction extends BaseZoomAction { constructor( id: string, label: string, - @IWorkspaceConfigurationService configurationService: IWorkspaceConfigurationService, - @IConfigurationEditingService configurationEditingService: IConfigurationEditingService + @IWorkspaceConfigurationService configurationService: IWorkspaceConfigurationService ) { - super(id, label, configurationService, configurationEditingService); + super(id, label, configurationService); } public run(): TPromise { diff --git a/src/vs/workbench/electron-browser/main.contribution.ts b/src/vs/workbench/electron-browser/main.contribution.ts index f56ae229cbe..6387586a777 100644 --- a/src/vs/workbench/electron-browser/main.contribution.ts +++ b/src/vs/workbench/electron-browser/main.contribution.ts @@ -132,9 +132,9 @@ let workbenchProperties: { [path: string]: IJSONSchema; } = { 'type': 'string', 'enum': ['default', 'short', 'medium', 'long'], 'enumDescriptions': [ - nls.localize('workbench.editor.labelFormat.default', "Show the name of the file. When tabs are enabled and two files have the same name in one group the distinguinshing sections of each file's path are added. When tabs are disabled, the path relative to workspace root is shown if the editor is active."), + nls.localize('workbench.editor.labelFormat.default', "Show the name of the file. When tabs are enabled and two files have the same name in one group the distinguinshing sections of each file's path are added. When tabs are disabled, the path relative to the workspace folder is shown if the editor is active."), nls.localize('workbench.editor.labelFormat.short', "Show the name of the file followed by it's directory name."), - nls.localize('workbench.editor.labelFormat.medium', "Show the name of the file followed by it's path relative to the workspace root."), + nls.localize('workbench.editor.labelFormat.medium', "Show the name of the file followed by it's path relative to the workspace folder."), nls.localize('workbench.editor.labelFormat.long', "Show the name of the file followed by it's absolute path.") ], 'default': 'default', @@ -170,7 +170,7 @@ let workbenchProperties: { [path: string]: IJSONSchema; } = { 'type': 'string', 'enum': ['left', 'right', 'first', 'last'], 'default': 'right', - 'description': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'editorOpenPositioning' }, "Controls where editors open. Select 'left' or 'right' to open editors to the left or right of the current active one. Select 'first' or 'last' to open editors independently from the currently active one.") + 'description': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'editorOpenPositioning' }, "Controls where editors open. Select 'left' or 'right' to open editors to the left or right of the currently active one. Select 'first' or 'last' to open editors independently from the currently active one.") }, 'workbench.editor.revealIfOpen': { 'type': 'boolean', diff --git a/src/vs/workbench/electron-browser/main.ts b/src/vs/workbench/electron-browser/main.ts index f461e27e464..7784777acea 100644 --- a/src/vs/workbench/electron-browser/main.ts +++ b/src/vs/workbench/electron-browser/main.ts @@ -17,7 +17,7 @@ import paths = require('vs/base/common/paths'); import uri from 'vs/base/common/uri'; import strings = require('vs/base/common/strings'); import { IWorkspaceContextService, Workspace, WorkbenchState } from 'vs/platform/workspace/common/workspace'; -import { WorkspaceService } from 'vs/workbench/services/configuration/node/configuration'; +import { WorkspaceService } from 'vs/workbench/services/configuration/node/configurationService'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { realpath } from 'vs/base/node/pfs'; diff --git a/src/vs/workbench/electron-browser/shell.ts b/src/vs/workbench/electron-browser/shell.ts index c5eb4b88f20..44543f77bd1 100644 --- a/src/vs/workbench/electron-browser/shell.ts +++ b/src/vs/workbench/electron-browser/shell.ts @@ -31,7 +31,7 @@ import ErrorTelemetry from 'vs/platform/telemetry/browser/errorTelemetry'; import { ElectronWindow } from 'vs/workbench/electron-browser/window'; import { resolveWorkbenchCommonProperties, getOrCreateMachineId } from 'vs/platform/telemetry/node/workbenchCommonProperties'; import { machineIdIpcChannel } from 'vs/platform/telemetry/node/commonProperties'; -import { WorkspaceStats } from 'vs/workbench/services/telemetry/common/workspaceStats'; +import { WorkspaceStats } from 'vs/workbench/services/telemetry/node/workspaceStats'; import { IWindowsService, IWindowService, IWindowConfiguration } from 'vs/platform/windows/common/windows'; import { WindowService } from 'vs/platform/windows/electron-browser/windowService'; import { MessageService } from 'vs/workbench/services/message/electron-browser/messageService'; @@ -225,9 +225,9 @@ export class WorkbenchShell { userAgent: navigator.userAgent, windowSize: { innerHeight: window.innerHeight, innerWidth: window.innerWidth, outerHeight: window.outerHeight, outerWidth: window.outerWidth }, emptyWorkbench: this.contextService.getWorkbenchState() === WorkbenchState.EMPTY, - 'workbench.filesToOpen': filesToOpen && filesToOpen.length || void 0, - 'workbench.filesToCreate': filesToCreate && filesToCreate.length || void 0, - 'workbench.filesToDiff': filesToDiff && filesToDiff.length || void 0, + 'workbench.filesToOpen': filesToOpen && filesToOpen.length || 0, + 'workbench.filesToCreate': filesToCreate && filesToCreate.length || 0, + 'workbench.filesToDiff': filesToDiff && filesToDiff.length || 0, customKeybindingsCount: info.customKeybindingsCount, theme: this.themeService.getColorTheme().id, language: platform.language, diff --git a/src/vs/workbench/electron-browser/window.ts b/src/vs/workbench/electron-browser/window.ts index bdf4e7eeef2..e40bc555cb2 100644 --- a/src/vs/workbench/electron-browser/window.ts +++ b/src/vs/workbench/electron-browser/window.ts @@ -29,7 +29,6 @@ import { IWindowsService, IWindowService, IWindowSettings, IPath, IOpenFileReque import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; import { ITitleService } from 'vs/workbench/services/title/common/titleService'; import { IWorkbenchThemeService, VS_HC_THEME, VS_DARK_THEME } from 'vs/workbench/services/themes/common/workbenchThemeService'; import * as browser from 'vs/base/browser/browser'; @@ -47,6 +46,7 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { fillInActions } from 'vs/platform/actions/browser/menuItemActionItem'; import { RunOnceScheduler } from 'vs/base/common/async'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { ConfigurationTarget, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; const TextInputActions: IAction[] = [ new Action('undo', nls.localize('undo', "Undo"), null, true, () => document.execCommand('undo') && TPromise.as(true)), @@ -81,7 +81,6 @@ export class ElectronWindow extends Themable { @ITitleService private titleService: ITitleService, @IWorkbenchThemeService protected themeService: IWorkbenchThemeService, @IMessageService private messageService: IMessageService, - @IConfigurationEditingService private configurationEditingService: IConfigurationEditingService, @ICommandService private commandService: ICommandService, @IExtensionService private extensionService: IExtensionService, @IViewletService private viewletService: IViewletService, @@ -241,7 +240,7 @@ export class ElectronWindow extends Themable { }); // Configuration changes - this.toUnbind.push(this.configurationService.onDidUpdateConfiguration(e => this.onDidUpdateConfiguration(e))); + this.toUnbind.push(this.configurationService.onDidChangeConfiguration(e => this.onDidUpdateConfiguration(e))); // Context menu support in input/textarea window.document.addEventListener('contextmenu', e => this.onContextMenu(e)); @@ -262,7 +261,11 @@ export class ElectronWindow extends Themable { } } - private onDidUpdateConfiguration(e): void { + private onDidUpdateConfiguration(event: IConfigurationChangeEvent): void { + if (!event.affectsConfiguration('window.zoomLevel')) { + return; + } + const windowConfig: IWindowsConfiguration = this.configurationService.getConfiguration(); let newZoomLevel = 0; @@ -401,7 +404,7 @@ export class ElectronWindow extends Themable { // Workspace: just add to workspace config if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) { - this.workspaceEditingService.addFolders(foldersToAdd).done(null, errors.onUnexpectedError); + this.contextService.addFolders(foldersToAdd).done(null, errors.onUnexpectedError); } // Single folder or no workspace: create workspace and open @@ -498,7 +501,7 @@ export class ElectronWindow extends Themable { } private toggleAutoSave(): void { - const setting = this.configurationService.lookup(ElectronWindow.AUTO_SAVE_SETTING); + const setting = this.configurationService.inspect(ElectronWindow.AUTO_SAVE_SETTING); let userAutoSaveConfig = setting.user; if (types.isUndefinedOrNull(userAutoSaveConfig)) { userAutoSaveConfig = setting.default; // use default if setting not defined @@ -511,7 +514,7 @@ export class ElectronWindow extends Themable { newAutoSaveValue = AutoSaveConfiguration.AFTER_DELAY; } - this.configurationEditingService.writeConfiguration(ConfigurationTarget.USER, { key: ElectronWindow.AUTO_SAVE_SETTING, value: newAutoSaveValue }); + this.configurationService.updateValue(ElectronWindow.AUTO_SAVE_SETTING, newAutoSaveValue, ConfigurationTarget.USER); } public dispose(): void { diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts index f6bdc8d057f..f2d871d7eb6 100644 --- a/src/vs/workbench/electron-browser/workbench.ts +++ b/src/vs/workbench/electron-browser/workbench.ts @@ -15,7 +15,6 @@ import DOM = require('vs/base/browser/dom'); import { Builder, $ } from 'vs/base/browser/builder'; import { Delayer, RunOnceScheduler } from 'vs/base/common/async'; import * as browser from 'vs/base/browser/browser'; -import assert = require('vs/base/common/assert'); import { StopWatch } from 'vs/base/common/stopwatch'; import { startTimer } from 'vs/base/node/startupTimers'; import errors = require('vs/base/common/errors'); @@ -39,23 +38,20 @@ import { IActionBarRegistry, Extensions as ActionBarExtensions } from 'vs/workbe import { PanelRegistry, Extensions as PanelExtensions } from 'vs/workbench/browser/panel'; import { QuickOpenController } from 'vs/workbench/browser/parts/quickopen/quickOpenController'; import { getServices } from 'vs/platform/instantiation/common/extensions'; -import { WorkbenchEditorService } from 'vs/workbench/services/editor/browser/editorService'; import { Position, Parts, IPartService, ILayoutOptions } from 'vs/workbench/services/part/common/partService'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { ContextMenuService } from 'vs/workbench/services/contextview/electron-browser/contextmenuService'; import { WorkbenchKeybindingService } from 'vs/workbench/services/keybinding/electron-browser/keybindingService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { WorkspaceService, DefaultConfigurationExportHelper } from 'vs/workbench/services/configuration/node/configuration'; -import { IConfigurationEditingService } from 'vs/workbench/services/configuration/common/configurationEditing'; -import { ConfigurationEditingService } from 'vs/workbench/services/configuration/node/configurationEditingService'; +import { WorkspaceService, DefaultConfigurationExportHelper } from 'vs/workbench/services/configuration/node/configurationService'; import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing'; import { JSONEditingService } from 'vs/workbench/services/configuration/node/jsonEditingService'; import { ContextKeyService } from 'vs/platform/contextkey/browser/contextKeyService'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IKeybindingEditingService, KeybindingsEditingService } from 'vs/workbench/services/keybinding/common/keybindingEditing'; import { ContextKeyExpr, RawContextKey, IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { IActivityBarService } from 'vs/workbench/services/activity/common/activityBarService'; +import { IActivityService } from 'vs/workbench/services/activity/common/activity'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { ViewletService } from 'vs/workbench/services/viewlet/browser/viewletService'; import { RemoteFileService } from 'vs/workbench/services/files/electron-browser/remoteFileService'; @@ -66,7 +62,7 @@ import { ConfigurationResolverService } from 'vs/workbench/services/configuratio import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { ITitleService } from 'vs/workbench/services/title/common/titleService'; import { WorkbenchMessageService } from 'vs/workbench/services/message/browser/messageService'; -import { IWorkbenchEditorService, IResourceInputType } from 'vs/workbench/services/editor/common/editorService'; +import { IWorkbenchEditorService, IResourceInputType, WorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { ClipboardService } from 'vs/platform/clipboard/electron-browser/clipboardService'; @@ -98,12 +94,14 @@ import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing'; import { WorkspaceEditingService } from 'vs/workbench/services/workspace/node/workspaceEditingService'; import { FileDecorationsService } from 'vs/workbench/services/decorations/browser/decorationsService'; -import { IResourceDecorationsService } from 'vs/workbench/services/decorations/browser/decorations'; +import { IDecorationsService } from 'vs/workbench/services/decorations/browser/decorations'; +import { ActivityService } from 'vs/workbench/services/activity/browser/activityService'; import URI from 'vs/base/common/uri'; export const MessagesVisibleContext = new RawContextKey('globalMessageVisible', false); export const EditorsVisibleContext = new RawContextKey('editorIsOpen', false); export const InZenModeContext = new RawContextKey('inZenMode', false); +export const SidebarVisibleContext = new RawContextKey('sidebarVisible', false); export const NoEditorsVisibleContext: ContextKeyExpr = EditorsVisibleContext.toNegated(); interface WorkbenchParams { @@ -178,7 +176,6 @@ export class Workbench implements IPartService { private contextKeyService: IContextKeyService; private keybindingService: IKeybindingService; private backupFileService: IBackupFileService; - private configurationEditingService: IConfigurationEditingService; private fileService: IFileService; private titlebarPart: TitlebarPart; private activitybarPart: ActivitybarPart; @@ -203,6 +200,7 @@ export class Workbench implements IPartService { private messagesVisibleContext: IContextKey; private editorsVisibleContext: IContextKey; private inZenMode: IContextKey; + private sideBarVisibleContext: IContextKey; private hasFilesToCreateOpenOrDiff: boolean; private fontAliasing: string; private zenMode: { @@ -268,9 +266,6 @@ export class Workbench implements IPartService { * once. Use the shutdown function to free up resources created by the workbench on startup. */ public startup(callbacks?: IWorkbenchCallbacks): void { - assert.ok(!this.workbenchStarted, 'Can not start a workbench that was already started'); - assert.ok(!this.workbenchShutdown, 'Can not start a workbench that was shutdown'); - try { this.workbenchStarted = true; this.callbacks = callbacks; @@ -291,6 +286,7 @@ export class Workbench implements IPartService { this.messagesVisibleContext = MessagesVisibleContext.bindTo(this.contextKeyService); this.editorsVisibleContext = EditorsVisibleContext.bindTo(this.contextKeyService); this.inZenMode = InZenModeContext.bindTo(this.contextKeyService); + this.sideBarVisibleContext = SidebarVisibleContext.bindTo(this.contextKeyService); // Register Listeners this.registerListeners(); @@ -311,6 +307,7 @@ export class Workbench implements IPartService { let viewletRestoreStopWatch: StopWatch; let viewletIdToRestore: string; if (!this.sideBarHidden) { + this.sideBarVisibleContext.set(true); if (this.shouldRestoreLastOpenedViewlet()) { viewletIdToRestore = this.storageService.get(SidebarPart.activeViewletSettingsKey, StorageScope.WORKSPACE); @@ -409,7 +406,7 @@ export class Workbench implements IPartService { workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenRecentAction, OpenRecentAction.ID, OpenRecentAction.LABEL, { primary: isDeveloping ? null : KeyMod.CtrlCmd | KeyCode.KEY_R, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_R } }), 'File: Open Recent...', localize('file', "File")); // Actions for macOS native tabs management (only when enabled) - const windowConfig = this.configurationService.getConfiguration(); + const windowConfig = this.configurationService.getConfiguration(); if (windowConfig && windowConfig.window && windowConfig.window.nativeTabs) { workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ShowPreviousWindowTab, ShowPreviousWindowTab.ID, ShowPreviousWindowTab.LABEL), 'Show Previous Window Tab'); workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ShowNextWindowTab, ShowNextWindowTab.ID, ShowNextWindowTab.LABEL), 'Show Next Window Tab'); @@ -486,13 +483,13 @@ export class Workbench implements IPartService { } private openUntitledFile() { - const startupEditor = this.configurationService.lookup('workbench.startupEditor'); + const startupEditor = this.configurationService.inspect('workbench.startupEditor'); // Fallback to previous workbench.welcome.enabled setting in case startupEditor is not defined if (!startupEditor.user && !startupEditor.workspace) { - const welcomeEnabled = this.configurationService.lookup('workbench.welcome.enabled'); - if (typeof welcomeEnabled.value === 'boolean') { - return !welcomeEnabled.value; + const welcomeEnabledValue = this.configurationService.getValue('workbench.welcome.enabled'); + if (typeof welcomeEnabledValue === 'boolean') { + return !welcomeEnabledValue; } } @@ -554,7 +551,8 @@ export class Workbench implements IPartService { this.activitybarPart = this.instantiationService.createInstance(ActivitybarPart, Identifiers.ACTIVITYBAR_PART); this.toDispose.push(this.activitybarPart); this.toShutdown.push(this.activitybarPart); - serviceCollection.set(IActivityBarService, this.activitybarPart); + const activityService = this.instantiationService.createInstance(ActivityService, this.activitybarPart, this.panelPart); + serviceCollection.set(IActivityService, activityService); // File Service this.fileService = this.instantiationService.createInstance(RemoteFileService); @@ -586,7 +584,7 @@ export class Workbench implements IPartService { serviceCollection.set(ITextFileService, new SyncDescriptor(TextFileService)); // File Decorations - serviceCollection.set(IResourceDecorationsService, new SyncDescriptor(FileDecorationsService)); + serviceCollection.set(IDecorationsService, new SyncDescriptor(FileDecorationsService)); // SCM Service serviceCollection.set(ISCMService, new SyncDescriptor(SCMService)); @@ -598,10 +596,6 @@ export class Workbench implements IPartService { const jsonEditingService = this.instantiationService.createInstance(JSONEditingService); serviceCollection.set(IJSONEditingService, jsonEditingService); - // Configuration Editing - this.configurationEditingService = this.instantiationService.createInstance(ConfigurationEditingService); - serviceCollection.set(IConfigurationEditingService, this.configurationEditingService); - // Workspace Editing serviceCollection.set(IWorkspaceEditingService, new SyncDescriptor(WorkspaceEditingService)); @@ -629,6 +623,8 @@ export class Workbench implements IPartService { Registry.as(EditorExtensions.EditorInputFactories).setInstantiationService(this.instantiationService); this.instantiationService.createInstance(DefaultConfigurationExportHelper); + + this.configurationService.setInstantiationService(this.getInstantiationService()); } private initSettings(): void { @@ -644,19 +640,19 @@ export class Workbench implements IPartService { } // Sidebar position - const sideBarPosition = this.configurationService.lookup(Workbench.sidebarPositionConfigurationKey).value; + const sideBarPosition = this.configurationService.getValue(Workbench.sidebarPositionConfigurationKey); this.sideBarPosition = (sideBarPosition === 'right') ? Position.RIGHT : Position.LEFT; // Statusbar visibility - const statusBarVisible = this.configurationService.lookup(Workbench.statusbarVisibleConfigurationKey).value; + const statusBarVisible = this.configurationService.getValue(Workbench.statusbarVisibleConfigurationKey); this.statusBarHidden = !statusBarVisible; // Activity bar visibility - const activityBarVisible = this.configurationService.lookup(Workbench.activityBarVisibleConfigurationKey).value; + const activityBarVisible = this.configurationService.getValue(Workbench.activityBarVisibleConfigurationKey); this.activityBarHidden = !activityBarVisible; // Font aliasing - this.fontAliasing = this.configurationService.lookup(Workbench.fontAliasingConfigurationKey).value; + this.fontAliasing = this.configurationService.getValue(Workbench.fontAliasingConfigurationKey); // Zen mode this.zenMode = { @@ -794,6 +790,7 @@ export class Workbench implements IPartService { public setSideBarHidden(hidden: boolean, skipLayout?: boolean): TPromise { this.sideBarHidden = hidden; + this.sideBarVisibleContext.set(!hidden); // Adjust CSS if (hidden) { @@ -992,7 +989,7 @@ export class Workbench implements IPartService { this.toDispose.push(this.quickOpen.onHide(() => (this.messageService).resume())); // resume messages once quick open is closed again // Configuration changes - this.toDispose.push(this.configurationService.onDidUpdateConfiguration(() => this.onDidUpdateConfiguration())); + this.toDispose.push(this.configurationService.onDidChangeConfiguration(() => this.onDidUpdateConfiguration())); // Fullscreen changes this.toDispose.push(browser.onDidChangeFullscreen(() => this.onFullscreenChanged())); @@ -1041,7 +1038,7 @@ export class Workbench implements IPartService { // Overruled by: window has a workspace opened or this window is for extension development // or setting is disabled. Also enabled when running with --wait from the command line. if (visibleEditors === 0 && this.contextService.getWorkbenchState() === WorkbenchState.EMPTY && !this.environmentService.isExtensionDevelopment) { - const closeWhenEmpty = this.configurationService.lookup(Workbench.closeWhenEmptyConfigurationKey).value; + const closeWhenEmpty = this.configurationService.getValue(Workbench.closeWhenEmptyConfigurationKey); if (closeWhenEmpty || this.environmentService.args.wait) { this.closeEmptyWindowScheduler.schedule(); } @@ -1073,24 +1070,24 @@ export class Workbench implements IPartService { } private onDidUpdateConfiguration(skipLayout?: boolean): void { - const newSidebarPositionValue = this.configurationService.lookup(Workbench.sidebarPositionConfigurationKey).value; + const newSidebarPositionValue = this.configurationService.getValue(Workbench.sidebarPositionConfigurationKey); const newSidebarPosition = (newSidebarPositionValue === 'right') ? Position.RIGHT : Position.LEFT; if (newSidebarPosition !== this.getSideBarPosition()) { this.setSideBarPosition(newSidebarPosition); } - const fontAliasing = this.configurationService.lookup(Workbench.fontAliasingConfigurationKey).value; + const fontAliasing = this.configurationService.getValue(Workbench.fontAliasingConfigurationKey); if (fontAliasing !== this.fontAliasing) { this.setFontAliasing(fontAliasing); } if (!this.zenMode.active) { - const newStatusbarHiddenValue = !this.configurationService.lookup(Workbench.statusbarVisibleConfigurationKey).value; + const newStatusbarHiddenValue = !this.configurationService.getValue(Workbench.statusbarVisibleConfigurationKey); if (newStatusbarHiddenValue !== this.statusBarHidden) { this.setStatusBarHidden(newStatusbarHiddenValue, skipLayout); } - const newActivityBarHiddenValue = !this.configurationService.lookup(Workbench.activityBarVisibleConfigurationKey).value; + const newActivityBarHiddenValue = !this.configurationService.getValue(Workbench.activityBarVisibleConfigurationKey); if (newActivityBarHiddenValue !== this.activityBarHidden) { this.setActivityBarHidden(newActivityBarHiddenValue, skipLayout); } @@ -1223,26 +1220,18 @@ export class Workbench implements IPartService { } public getEditorPart(): EditorPart { - assert.ok(this.workbenchStarted, 'Workbench is not started. Call startup() first.'); - return this.editorPart; } public getSidebarPart(): SidebarPart { - assert.ok(this.workbenchStarted, 'Workbench is not started. Call startup() first.'); - return this.sidebarPart; } public getPanelPart(): PanelPart { - assert.ok(this.workbenchStarted, 'Workbench is not started. Call startup() first.'); - return this.panelPart; } public getInstantiationService(): IInstantiationService { - assert.ok(this.workbenchStarted, 'Workbench is not started. Call startup() first.'); - return this.instantiationService; } @@ -1264,6 +1253,7 @@ export class Workbench implements IPartService { public toggleZenMode(skipLayout?: boolean): void { this.zenMode.active = !this.zenMode.active; + // Check if zen mode transitioned to full screen and if now we are out of zen mode -> we need to go out of full screen let toggleFullScreen = false; if (this.zenMode.active) { @@ -1278,9 +1268,11 @@ export class Workbench implements IPartService { if (config.hideActivityBar) { this.setActivityBarHidden(true, true); } + if (config.hideStatusBar) { this.setStatusBarHidden(true, true); } + if (config.hideTabs) { this.editorPart.hideTabs(true); } @@ -1288,9 +1280,11 @@ export class Workbench implements IPartService { if (this.zenMode.wasPanelVisible) { this.setPanelHidden(false, true).done(undefined, errors.onUnexpectedError); } + if (this.zenMode.wasSideBarVisible) { this.setSideBarHidden(false, true).done(undefined, errors.onUnexpectedError); } + // Status bar and activity bar visibility come from settings -> update their visibility. this.onDidUpdateConfiguration(true); this.editorPart.hideTabs(false); @@ -1298,13 +1292,16 @@ export class Workbench implements IPartService { if (activeEditor) { activeEditor.focus(); } + toggleFullScreen = this.zenMode.transitionedToFullScreen && browser.isFullscreen(); } + this.inZenMode.set(this.zenMode.active); if (!skipLayout) { this.layout(); } + if (toggleFullScreen) { this.windowService.toggleFullScreen().done(undefined, errors.onUnexpectedError); } diff --git a/src/vs/workbench/node/extensionHostMain.ts b/src/vs/workbench/node/extensionHostMain.ts index cda0a718027..a28fb2b6a9b 100644 --- a/src/vs/workbench/node/extensionHostMain.ts +++ b/src/vs/workbench/node/extensionHostMain.ts @@ -232,13 +232,13 @@ export class ExtensionHostMain { const query: ISearchQuery = { folderQueries, type: QueryType.File, - maxResults: 1, + exists: true, includePattern: includes, useRipgrep }; let result = await this._diskSearch.search(query); - if (result.results.length > 0) { + if (result.limitHit) { // a file was found matching one of the glob patterns return ( this._extensionService.activateById(extensionId, true) diff --git a/src/vs/workbench/parts/backup/common/backupModelTracker.ts b/src/vs/workbench/parts/backup/common/backupModelTracker.ts index 787897cd389..4b1408847d2 100644 --- a/src/vs/workbench/parts/backup/common/backupModelTracker.ts +++ b/src/vs/workbench/parts/backup/common/backupModelTracker.ts @@ -50,7 +50,7 @@ export class BackupModelTracker implements IWorkbenchContribution { this.toDispose.push(this.untitledEditorService.onDidDisposeModel((e) => this.discardBackup(e))); // Listen to config changes - this.toDispose.push(this.configurationService.onDidUpdateConfiguration(e => this.onConfigurationChange(this.configurationService.getConfiguration()))); + this.toDispose.push(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationChange(this.configurationService.getConfiguration()))); } private onConfigurationChange(configuration: IFilesConfiguration): void { diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.ts b/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.ts index ddbc5ae45e6..aaf8113fe70 100644 --- a/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.ts +++ b/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.ts @@ -25,10 +25,9 @@ import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions'; import { ToggleTabFocusModeAction } from 'vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { editorWidgetBackground, widgetShadow, contrastBorder } from 'vs/platform/theme/common/colorRegistry'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import * as editorOptions from 'vs/editor/common/config/editorOptions'; import * as platform from 'vs/base/common/platform'; -import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; import { alert } from 'vs/base/browser/ui/aria/aria'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import URI from 'vs/base/common/uri'; @@ -87,7 +86,6 @@ class AccessibilityHelpWidget extends Widget implements IOverlayWidget { @IContextKeyService private _contextKeyService: IContextKeyService, @IKeybindingService private _keybindingService: IKeybindingService, @IConfigurationService private _configurationService: IConfigurationService, - @IConfigurationEditingService private _configurationEditingService: IConfigurationEditingService, @IOpenerService private _openerService: IOpenerService ) { super(); @@ -124,10 +122,7 @@ class AccessibilityHelpWidget extends Widget implements IOverlayWidget { if (e.equals(KeyMod.CtrlCmd | KeyCode.KEY_E)) { alert(nls.localize('emergencyConfOn', "Now changing the setting `editor.accessibilitySupport` to 'on'.")); - this._configurationEditingService.writeConfiguration(ConfigurationTarget.USER, { - key: 'editor.accessibilitySupport', - value: 'on' - }); + this._configurationService.updateValue('editor.accessibilitySupport', 'on', ConfigurationTarget.USER); e.preventDefault(); e.stopPropagation(); 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 7da69d61142..4b7695b68b9 100644 --- a/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.ts +++ b/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.ts @@ -5,6 +5,7 @@ 'use strict'; import * as nls from 'vs/nls'; +import * as types from 'vs/base/common/types'; import { parse, ParseError } from 'vs/base/common/json'; import { readFile } from 'vs/base/node/pfs'; import { CharacterPair, LanguageConfiguration, IAutoClosingPair, IAutoClosingPairConditional, IndentationRule, CommentRule, FoldingRules } from 'vs/editor/common/modes/languageConfiguration'; @@ -38,6 +39,26 @@ interface ILanguageConfiguration { folding?: FoldingRules; } +function isStringArr(something: string[]): boolean { + if (!Array.isArray(something)) { + return false; + } + for (let i = 0, len = something.length; i < len; i++) { + if (typeof something[i] !== 'string') { + return false; + } + } + return true; + +} + +function isCharacterPair(something: CharacterPair): boolean { + return ( + isStringArr(something) + && something.length === 2 + ); +} + export class LanguageConfigurationFileHandler { private _modeService: IModeService; @@ -80,24 +101,177 @@ export class LanguageConfigurationFileHandler { }); } + private _extractValidCommentRule(languageIdentifier: LanguageIdentifier, configuration: ILanguageConfiguration): CommentRule { + const source = configuration.comments; + if (typeof source === 'undefined') { + return null; + } + if (!types.isObject(source)) { + console.warn(`[${languageIdentifier.language}]: language configuration: expected \`comments\` to be an object.`); + return null; + } + + let result: CommentRule = null; + if (typeof source.lineComment !== 'undefined') { + if (typeof source.lineComment !== 'string') { + console.warn(`[${languageIdentifier.language}]: language configuration: expected \`comments.lineComment\` to be a string.`); + } else { + result = result || {}; + result.lineComment = source.lineComment; + } + } + if (typeof source.blockComment !== 'undefined') { + if (!isCharacterPair(source.blockComment)) { + console.warn(`[${languageIdentifier.language}]: language configuration: expected \`comments.blockComment\` to be an array of two strings.`); + } else { + result = result || {}; + result.blockComment = source.blockComment; + } + } + return result; + } + + private _extractValidBrackets(languageIdentifier: LanguageIdentifier, configuration: ILanguageConfiguration): CharacterPair[] { + const source = configuration.brackets; + if (typeof source === 'undefined') { + return null; + } + if (!Array.isArray(source)) { + console.warn(`[${languageIdentifier.language}]: language configuration: expected \`brackets\` to be an array.`); + return null; + } + + let result: CharacterPair[] = null; + for (let i = 0, len = source.length; i < len; i++) { + const pair = source[i]; + if (!isCharacterPair(pair)) { + console.warn(`[${languageIdentifier.language}]: language configuration: expected \`brackets[${i}]\` to be an array of two strings.`); + continue; + } + + result = result || []; + result.push(pair); + } + return result; + } + + private _extractValidAutoClosingPairs(languageIdentifier: LanguageIdentifier, configuration: ILanguageConfiguration): IAutoClosingPairConditional[] { + const source = configuration.autoClosingPairs; + if (typeof source === 'undefined') { + return null; + } + if (!Array.isArray(source)) { + console.warn(`[${languageIdentifier.language}]: language configuration: expected \`autoClosingPairs\` to be an array.`); + return null; + } + + let result: IAutoClosingPairConditional[] = null; + for (let i = 0, len = source.length; i < len; i++) { + const pair = source[i]; + if (Array.isArray(pair)) { + if (!isCharacterPair(pair)) { + console.warn(`[${languageIdentifier.language}]: language configuration: expected \`autoClosingPairs[${i}]\` to be an array of two strings or an object.`); + continue; + } + result = result || []; + result.push({ open: pair[0], close: pair[1] }); + } else { + if (!types.isObject(pair)) { + console.warn(`[${languageIdentifier.language}]: language configuration: expected \`autoClosingPairs[${i}]\` to be an array of two strings or an object.`); + continue; + } + if (typeof pair.open !== 'string') { + console.warn(`[${languageIdentifier.language}]: language configuration: expected \`autoClosingPairs[${i}].open\` to be a string.`); + continue; + } + if (typeof pair.close !== 'string') { + console.warn(`[${languageIdentifier.language}]: language configuration: expected \`autoClosingPairs[${i}].close\` to be a string.`); + continue; + } + if (typeof pair.notIn !== 'undefined') { + if (!isStringArr(pair.notIn)) { + console.warn(`[${languageIdentifier.language}]: language configuration: expected \`autoClosingPairs[${i}].notIn\` to be a string array.`); + continue; + } + } + result = result || []; + result.push({ open: pair.open, close: pair.close, notIn: pair.notIn }); + } + } + return result; + } + + private _extractValidSurroundingPairs(languageIdentifier: LanguageIdentifier, configuration: ILanguageConfiguration): IAutoClosingPair[] { + const source = configuration.surroundingPairs; + if (typeof source === 'undefined') { + return null; + } + if (!Array.isArray(source)) { + console.warn(`[${languageIdentifier.language}]: language configuration: expected \`surroundingPairs\` to be an array.`); + return null; + } + + let result: IAutoClosingPair[] = null; + for (let i = 0, len = source.length; i < len; i++) { + const pair = source[i]; + if (Array.isArray(pair)) { + if (!isCharacterPair(pair)) { + console.warn(`[${languageIdentifier.language}]: language configuration: expected \`surroundingPairs[${i}]\` to be an array of two strings or an object.`); + continue; + } + result = result || []; + result.push({ open: pair[0], close: pair[1] }); + } else { + if (!types.isObject(pair)) { + console.warn(`[${languageIdentifier.language}]: language configuration: expected \`surroundingPairs[${i}]\` to be an array of two strings or an object.`); + continue; + } + if (typeof pair.open !== 'string') { + console.warn(`[${languageIdentifier.language}]: language configuration: expected \`surroundingPairs[${i}].open\` to be a string.`); + continue; + } + if (typeof pair.close !== 'string') { + console.warn(`[${languageIdentifier.language}]: language configuration: expected \`surroundingPairs[${i}].close\` to be a string.`); + continue; + } + result = result || []; + result.push({ open: pair.open, close: pair.close }); + } + } + return result; + } + + // private _mapCharacterPairs(pairs: (CharacterPair | IAutoClosingPairConditional)[]): IAutoClosingPairConditional[] { + // return pairs.map(pair => { + // if (Array.isArray(pair)) { + // return { open: pair[0], close: pair[1] }; + // } + // return pair; + // }); + // } + private _handleConfig(languageIdentifier: LanguageIdentifier, configuration: ILanguageConfiguration): void { let richEditConfig: LanguageConfiguration = {}; - if (configuration.comments) { - richEditConfig.comments = configuration.comments; + const comments = this._extractValidCommentRule(languageIdentifier, configuration); + if (comments) { + richEditConfig.comments = comments; } - if (configuration.brackets) { - richEditConfig.brackets = configuration.brackets; + const brackets = this._extractValidBrackets(languageIdentifier, configuration); + if (brackets) { + richEditConfig.brackets = brackets; } - if (configuration.autoClosingPairs) { - richEditConfig.autoClosingPairs = this._mapCharacterPairs(configuration.autoClosingPairs); + const autoClosingPairs = this._extractValidAutoClosingPairs(languageIdentifier, configuration); + if (autoClosingPairs) { + richEditConfig.autoClosingPairs = autoClosingPairs; } - if (configuration.surroundingPairs) { - richEditConfig.surroundingPairs = this._mapCharacterPairs(configuration.surroundingPairs); + const surroundingPairs = this._extractValidSurroundingPairs(languageIdentifier, configuration); + if (surroundingPairs) { + richEditConfig.surroundingPairs = surroundingPairs; } if (configuration.wordPattern) { @@ -166,15 +340,6 @@ export class LanguageConfigurationFileHandler { return null; } - - private _mapCharacterPairs(pairs: (CharacterPair | IAutoClosingPairConditional)[]): IAutoClosingPairConditional[] { - return pairs.map(pair => { - if (Array.isArray(pair)) { - return { open: pair[0], close: pair[1] }; - } - return pair; - }); - } } const schemaId = 'vscode://schemas/language-configuration'; diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.ts b/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.ts index 86cdf51939a..247b6b2e73b 100644 --- a/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.ts +++ b/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.ts @@ -7,7 +7,7 @@ import * as nls from 'vs/nls'; import { ICommonCodeEditor } from 'vs/editor/common/editorCommon'; import { editorAction, ServicesAccessor, EditorAction } from 'vs/editor/common/editorCommonExtensions'; -import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; +import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; @editorAction export class ToggleMinimapAction extends EditorAction { @@ -22,10 +22,10 @@ export class ToggleMinimapAction extends EditorAction { } public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void { - const configurationEditingService = accessor.get(IConfigurationEditingService); + const configurationService = accessor.get(IConfigurationService); const newValue = !editor.getConfiguration().viewInfo.minimap.enabled; - configurationEditingService.writeConfiguration(ConfigurationTarget.USER, { key: 'editor.minimap.enabled', value: newValue }); + configurationService.updateValue('editor.minimap.enabled', newValue, ConfigurationTarget.USER); } } diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.ts b/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.ts index 214f40e3a53..a2022660076 100644 --- a/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.ts +++ b/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.ts @@ -10,8 +10,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { Action } from 'vs/base/common/actions'; import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; -import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; export class ToggleMultiCursorModifierAction extends Action { @@ -23,21 +22,16 @@ export class ToggleMultiCursorModifierAction extends Action { constructor( id: string, label: string, - @IConfigurationService private configurationService: IConfigurationService, - @IConfigurationEditingService private configurationEditingService: IConfigurationEditingService + @IConfigurationService private configurationService: IConfigurationService ) { super(id, label); - - this.enabled = !!this.configurationService && !!this.configurationEditingService; } public run(): TPromise { const editorConf = this.configurationService.getConfiguration<{ multiCursorModifier: 'ctrlCmd' | 'alt' }>('editor'); const newValue: 'ctrlCmd' | 'alt' = (editorConf.multiCursorModifier === 'ctrlCmd' ? 'alt' : 'ctrlCmd'); - this.configurationEditingService.writeConfiguration(ConfigurationTarget.USER, { key: ToggleMultiCursorModifierAction.multiCursorModifierConfigurationKey, value: newValue }); - - return TPromise.as(null); + return this.configurationService.updateValue(ToggleMultiCursorModifierAction.multiCursorModifierConfigurationKey, newValue, ConfigurationTarget.USER); } } diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.ts b/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.ts index dc51edcbf3d..5eff950044a 100644 --- a/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.ts +++ b/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.ts @@ -7,7 +7,7 @@ import * as nls from 'vs/nls'; import { ICommonCodeEditor } from 'vs/editor/common/editorCommon'; import { editorAction, ServicesAccessor, EditorAction } from 'vs/editor/common/editorCommonExtensions'; -import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; +import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; @editorAction export class ToggleRenderControlCharacterAction extends EditorAction { @@ -22,10 +22,10 @@ export class ToggleRenderControlCharacterAction extends EditorAction { } public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void { - const configurationEditingService = accessor.get(IConfigurationEditingService); + const configurationService = accessor.get(IConfigurationService); let newRenderControlCharacters = !editor.getConfiguration().viewInfo.renderControlCharacters; - configurationEditingService.writeConfiguration(ConfigurationTarget.USER, { key: 'editor.renderControlCharacters', value: newRenderControlCharacters }); + configurationService.updateValue('editor.renderControlCharacters', newRenderControlCharacters, ConfigurationTarget.USER); } } diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.ts b/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.ts index 1ce47241da5..c95eca4cc60 100644 --- a/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.ts +++ b/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.ts @@ -7,7 +7,7 @@ import * as nls from 'vs/nls'; import { ICommonCodeEditor } from 'vs/editor/common/editorCommon'; import { editorAction, ServicesAccessor, EditorAction } from 'vs/editor/common/editorCommonExtensions'; -import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; +import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; @editorAction export class ToggleRenderWhitespaceAction extends EditorAction { @@ -22,7 +22,7 @@ export class ToggleRenderWhitespaceAction extends EditorAction { } public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void { - const configurationEditingService = accessor.get(IConfigurationEditingService); + const configurationService = accessor.get(IConfigurationService); let renderWhitespace = editor.getConfiguration().viewInfo.renderWhitespace; let newRenderWhitespace: string; @@ -32,6 +32,6 @@ export class ToggleRenderWhitespaceAction extends EditorAction { newRenderWhitespace = 'none'; } - configurationEditingService.writeConfiguration(ConfigurationTarget.USER, { key: 'editor.renderWhitespace', value: newRenderWhitespace }); + configurationService.updateValue('editor.renderWhitespace', newRenderWhitespace, ConfigurationTarget.USER); } } diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.ts b/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.ts index 83b7176dcbc..e2e99c99949 100644 --- a/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.ts +++ b/src/vs/workbench/parts/codeEditor/electron-browser/wordWrapMigration.ts @@ -83,7 +83,7 @@ class WordWrapMigrationController extends Disposable implements IEditorContribut } WordWrapMigrationController._checked = true; - let result = this.configurationService.lookup('editor.wrappingColumn'); + let result = this.configurationService.inspect('editor.wrappingColumn'); if (typeof result.value === 'undefined') { // Setting is not used return; diff --git a/src/vs/workbench/parts/debug/browser/debugActionItems.ts b/src/vs/workbench/parts/debug/browser/debugActionItems.ts index 346461ba287..81af57279a2 100644 --- a/src/vs/workbench/parts/debug/browser/debugActionItems.ts +++ b/src/vs/workbench/parts/debug/browser/debugActionItems.ts @@ -54,8 +54,8 @@ export class StartDebugActionItem extends EventEmitter implements IActionItem { } private registerListeners(): void { - this.toDispose.push(this.configurationService.onDidUpdateConfiguration(e => { - if (e.sourceConfig.launch) { + this.toDispose.push(this.configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('launch')) { this.updateOptions(); } })); diff --git a/src/vs/workbench/parts/debug/browser/debugActionsWidget.ts b/src/vs/workbench/parts/debug/browser/debugActionsWidget.ts index 5818d30bd3b..3513a9f08ac 100644 --- a/src/vs/workbench/parts/debug/browser/debugActionsWidget.ts +++ b/src/vs/workbench/parts/debug/browser/debugActionsWidget.ts @@ -21,7 +21,7 @@ import { IDebugConfiguration, IDebugService, State } from 'vs/workbench/parts/de import { AbstractDebugAction, PauseAction, ContinueAction, StepBackAction, ReverseContinueAction, StopAction, DisconnectAction, StepOverAction, StepIntoAction, StepOutAction, RestartAction, FocusProcessAction } from 'vs/workbench/parts/debug/browser/debugActions'; import { FocusProcessActionItem } from 'vs/workbench/parts/debug/browser/debugActionItems'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IMessageService } from 'vs/platform/message/common/message'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -93,7 +93,7 @@ export class DebugActionsWidget extends Themable implements IWorkbenchContributi private registerListeners(): void { this.toUnbind.push(this.debugService.onDidChangeState(state => this.update(state))); - this.toUnbind.push(this.configurationService.onDidUpdateConfiguration(() => this.update(this.debugService.state))); + this.toUnbind.push(this.configurationService.onDidChangeConfiguration(e => this.onDidConfigurationChange(e))); this.toUnbind.push(this.actionBar.actionRunner.addListener(EventType.RUN, (e: any) => { // check for error if (e.error && !errors.isPromiseCanceledError(e.error)) { @@ -195,6 +195,12 @@ export class DebugActionsWidget extends Themable implements IWorkbenchContributi return DebugActionsWidget.ID; } + private onDidConfigurationChange(event: IConfigurationChangeEvent): void { + if (event.affectsConfiguration('debug.hideActionBar')) { + this.update(this.debugService.state); + } + } + private update(state: State): void { if (state === State.Inactive || state === State.Initializing || this.configurationService.getConfiguration('debug').hideActionBar) { return this.hide(); diff --git a/src/vs/workbench/parts/debug/browser/debugEditorModelManager.ts b/src/vs/workbench/parts/debug/browser/debugEditorModelManager.ts index 6fd5d7c1c20..55f5e51c46f 100644 --- a/src/vs/workbench/parts/debug/browser/debugEditorModelManager.ts +++ b/src/vs/workbench/parts/debug/browser/debugEditorModelManager.ts @@ -13,7 +13,6 @@ import { IModel, TrackedRangeStickiness, IModelDeltaDecoration, IModelDecoration import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IDebugService, IBreakpoint, IRawBreakpoint, State } from 'vs/workbench/parts/debug/common/debug'; import { IModelService } from 'vs/editor/common/services/modelService'; -import { IModelDecorationsChangedEvent } from 'vs/editor/common/model/textModelEvents'; import { MarkdownString } from 'vs/base/common/htmlContent'; interface IDebugEditorModelData { @@ -21,7 +20,7 @@ interface IDebugEditorModelData { toDispose: lifecycle.IDisposable[]; breakpointDecorationIds: string[]; breakpointLines: number[]; - breakpointDecorationsAsMap: Map; + breakpointDecorationsAsMap: Map; currentStackDecorations: string[]; dirty: boolean; topStackFrameRange: Range; @@ -81,11 +80,12 @@ export class DebugEditorModelManager implements IWorkbenchContribution { const breakpoints = this.debugService.getModel().getBreakpoints().filter(bp => bp.uri.toString() === modelUrlStr); const currentStackDecorations = model.deltaDecorations([], this.createCallStackDecorations(modelUrlStr)); - const breakPointDecorations = model.deltaDecorations([], this.createBreakpointDecorations(breakpoints)); + const desiredDecorations = this.createBreakpointDecorations(model, breakpoints); + const breakPointDecorations = model.deltaDecorations([], desiredDecorations); - const toDispose: lifecycle.IDisposable[] = [model.onDidChangeDecorations((e) => this.onModelDecorationsChanged(modelUrlStr, e))]; - const breakpointDecorationsAsMap = new Map(); - breakPointDecorations.forEach(bpd => breakpointDecorationsAsMap.set(bpd, true)); + const toDispose: lifecycle.IDisposable[] = [model.onDidChangeDecorations((e) => this.onModelDecorationsChanged(modelUrlStr))]; + const breakpointDecorationsAsMap = new Map(); + breakPointDecorations.forEach((decorationId, index) => breakpointDecorationsAsMap.set(decorationId, desiredDecorations[index].range)); this.modelDataMap.set(modelUrlStr, { model: model, @@ -185,13 +185,23 @@ export class DebugEditorModelManager implements IWorkbenchContribution { } // breakpoints management. Represent data coming from the debug service and also send data back. - private onModelDecorationsChanged(modelUrlStr: string, e: IModelDecorationsChangedEvent): void { + private onModelDecorationsChanged(modelUrlStr: string): void { const modelData = this.modelDataMap.get(modelUrlStr); if (modelData.breakpointDecorationsAsMap.size === 0) { // I have no decorations return; } - if (!e.changedDecorations.some(decorationId => modelData.breakpointDecorationsAsMap.has(decorationId))) { + let somethingChanged = false; + modelData.breakpointDecorationsAsMap.forEach((breakpointRange, decorationId) => { + if (somethingChanged) { + return; + } + const newBreakpointRange = modelData.model.getDecorationRange(decorationId); + if (newBreakpointRange && !breakpointRange.equalsRange(newBreakpointRange)) { + somethingChanged = true; + } + }); + if (!somethingChanged) { // nothing to do, my decorations did not change. return; } @@ -254,16 +264,19 @@ export class DebugEditorModelManager implements IWorkbenchContribution { } private updateBreakpoints(modelData: IDebugEditorModelData, newBreakpoints: IBreakpoint[]): void { - modelData.breakpointDecorationIds = modelData.model.deltaDecorations(modelData.breakpointDecorationIds, this.createBreakpointDecorations(newBreakpoints)); + const desiredDecorations = this.createBreakpointDecorations(modelData.model, newBreakpoints); + modelData.breakpointDecorationIds = modelData.model.deltaDecorations(modelData.breakpointDecorationIds, desiredDecorations); modelData.breakpointDecorationsAsMap.clear(); - modelData.breakpointDecorationIds.forEach(id => modelData.breakpointDecorationsAsMap.set(id, true)); + modelData.breakpointDecorationIds.forEach((decorationId, index) => modelData.breakpointDecorationsAsMap.set(decorationId, desiredDecorations[index].range)); modelData.breakpointLines = newBreakpoints.map(bp => bp.lineNumber); } - private createBreakpointDecorations(breakpoints: IBreakpoint[]): IModelDeltaDecoration[] { + private createBreakpointDecorations(model: IModel, breakpoints: IBreakpoint[]): { range: Range; options: IModelDecorationOptions; }[] { return breakpoints.map((breakpoint) => { - const range = breakpoint.column ? new Range(breakpoint.lineNumber, breakpoint.column, breakpoint.lineNumber, breakpoint.column + 1) - : new Range(breakpoint.lineNumber, 1, breakpoint.lineNumber, Constants.MAX_SAFE_SMALL_INTEGER); // Decoration has to have a width #20688 + const range = model.validateRange( + breakpoint.column ? new Range(breakpoint.lineNumber, breakpoint.column, breakpoint.lineNumber, breakpoint.column + 1) + : new Range(breakpoint.lineNumber, 1, breakpoint.lineNumber, Constants.MAX_SAFE_SMALL_INTEGER) // Decoration has to have a width #20688 + ); return { options: this.getBreakpointDecorationOptions(breakpoint), range diff --git a/src/vs/workbench/parts/debug/browser/debugQuickOpen.ts b/src/vs/workbench/parts/debug/browser/debugQuickOpen.ts index e9c35de887b..27e900d3443 100644 --- a/src/vs/workbench/parts/debug/browser/debugQuickOpen.ts +++ b/src/vs/workbench/parts/debug/browser/debugQuickOpen.ts @@ -13,8 +13,38 @@ 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'; +import { QuickOpenEntry, QuickOpenEntryGroup } from 'vs/base/parts/quickopen/browser/quickOpenModel'; +import { ICommandService } from 'vs/platform/commands/common/commands'; -class DebugEntry extends Model.QuickOpenEntry { +class AddConfigEntry extends Model.QuickOpenEntry { + + constructor(private label: string, private launch: ILaunch, private commandService: ICommandService, private contextService: IWorkspaceContextService, highlights: Model.IHighlight[] = []) { + super(highlights); + } + + public getLabel(): string { + return this.label; + } + + public getDescription(): string { + return this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE ? this.launch.workspace.name : ''; + } + + public getAriaLabel(): string { + return nls.localize('entryAriaLabel', "{0}, debug", this.getLabel()); + } + + public run(mode: QuickOpen.Mode, context: Model.IContext): boolean { + if (mode === QuickOpen.Mode.PREVIEW) { + return false; + } + this.commandService.executeCommand('debug.addConfiguration', this.launch.workspace.uri.toString()).done(undefined, errors.onUnexpectedError); + + return true; + } +} + +class StartDebugEntry extends Model.QuickOpenEntry { constructor(private debugService: IDebugService, private contextService: IWorkspaceContextService, private launch: ILaunch, private configurationName: string, highlights: Model.IHighlight[] = []) { super(highlights); @@ -51,7 +81,8 @@ export class DebugQuickOpenHandler extends Quickopen.QuickOpenHandler { constructor( @IQuickOpenService private quickOpenService: IQuickOpenService, @IDebugService private debugService: IDebugService, - @IWorkspaceContextService private contextService: IWorkspaceContextService + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @ICommandService private commandService: ICommandService ) { super(); } @@ -61,13 +92,24 @@ export class DebugQuickOpenHandler extends Quickopen.QuickOpenHandler { } public getResults(input: string): TPromise { - const configurations: DebugEntry[] = []; + const configurations: QuickOpenEntry[] = []; - for (let launch of this.debugService.getConfigurationManager().getLaunches()) { + const launches = this.debugService.getConfigurationManager().getLaunches(); + for (let launch of launches) { launch.getConfigurationNames().map(config => ({ config: config, highlights: Filters.matchesContiguousSubString(input, config) })) .filter(({ highlights }) => !!highlights) - .forEach(({ config, highlights }) => configurations.push(new DebugEntry(this.debugService, this.contextService, launch, config, highlights))); + .forEach(({ config, highlights }) => configurations.push(new StartDebugEntry(this.debugService, this.contextService, launch, config, highlights))); } + launches.forEach((l, index) => { + const label = launches.length > 1 ? nls.localize("addConfigTo", "Add Config ({0})...", l.workspace.name) : nls.localize('addConfiguration', "Add Configuration..."); + const entry = new AddConfigEntry(label, l, this.commandService, this.contextService, Filters.matchesContiguousSubString(input, label)); + if (index === 0) { + configurations.push(new QuickOpenEntryGroup(entry, undefined, true)); + } else { + configurations.push(entry); + } + + }); return TPromise.as(new Model.QuickOpenModel(configurations)); } diff --git a/src/vs/workbench/parts/debug/browser/media/debug.contribution.css b/src/vs/workbench/parts/debug/browser/media/debug.contribution.css index 2796a460a0d..ea62255fc43 100644 --- a/src/vs/workbench/parts/debug/browser/media/debug.contribution.css +++ b/src/vs/workbench/parts/debug/browser/media/debug.contribution.css @@ -109,7 +109,7 @@ } .monaco-workbench .part.statusbar .debug-statusbar-item .icon { - -webkit-mask: url('debug-dark.svg') no-repeat 50% 50%; + -webkit-mask: url('continue.svg') no-repeat 50% 50%; -webkit-mask-size: 18px; display: inline-block; padding-right: 2px; diff --git a/src/vs/workbench/parts/debug/common/debug.ts b/src/vs/workbench/parts/debug/common/debug.ts index f226ca69b2b..24ee1c34060 100644 --- a/src/vs/workbench/parts/debug/common/debug.ts +++ b/src/vs/workbench/parts/debug/common/debug.ts @@ -591,11 +591,6 @@ export interface IDebugService { */ 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: 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 2859af8fc30..c8ef0226454 100644 --- a/src/vs/workbench/parts/debug/common/debugModel.ts +++ b/src/vs/workbench/parts/debug/common/debugModel.ts @@ -916,7 +916,6 @@ export class Model implements IModel { bp.verified = false; } }); - this.exceptionBreakpoints.forEach(ebp => ebp.enabled = enable); this.functionBreakpoints.forEach(fbp => fbp.enabled = enable); this._onDidChangeBreakpoints.fire(); 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 a88503d3faf..0b2665666de 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts @@ -6,7 +6,7 @@ import 'vs/css!../browser/media/debug.contribution'; import 'vs/css!../browser/media/debugHover'; import * as nls from 'vs/nls'; -import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; +import { KeyMod, KeyCode, KeyChord } from 'vs/base/common/keyCodes'; import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { Registry } from 'vs/platform/registry/common/platform'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; @@ -132,7 +132,7 @@ registry.registerWorkbenchAction(new SyncActionDescriptor(PauseAction, PauseActi registry.registerWorkbenchAction(new SyncActionDescriptor(ConfigureAction, ConfigureAction.ID, ConfigureAction.LABEL), 'Debug: Open launch.json', debugCategory); registry.registerWorkbenchAction(new SyncActionDescriptor(AddFunctionBreakpointAction, AddFunctionBreakpointAction.ID, AddFunctionBreakpointAction.LABEL), 'Debug: Add Function Breakpoint', debugCategory); registry.registerWorkbenchAction(new SyncActionDescriptor(ReapplyBreakpointsAction, ReapplyBreakpointsAction.ID, ReapplyBreakpointsAction.LABEL), 'Debug: Reapply All Breakpoints', debugCategory); -registry.registerWorkbenchAction(new SyncActionDescriptor(RunAction, RunAction.ID, RunAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.F5 }, CONTEXT_NOT_IN_DEBUG_MODE), 'Debug: Start Without Debugging', debugCategory); +registry.registerWorkbenchAction(new SyncActionDescriptor(RunAction, RunAction.ID, RunAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.F5, mac: { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_X) } }, CONTEXT_NOT_IN_DEBUG_MODE), 'Debug: Start Without Debugging', debugCategory); registry.registerWorkbenchAction(new SyncActionDescriptor(RemoveAllBreakpointsAction, RemoveAllBreakpointsAction.ID, RemoveAllBreakpointsAction.LABEL), 'Debug: Remove All Breakpoints', debugCategory); registry.registerWorkbenchAction(new SyncActionDescriptor(EnableAllBreakpointsAction, EnableAllBreakpointsAction.ID, EnableAllBreakpointsAction.LABEL), 'Debug: Enable All Breakpoints', debugCategory); registry.registerWorkbenchAction(new SyncActionDescriptor(DisableAllBreakpointsAction, DisableAllBreakpointsAction.ID, DisableAllBreakpointsAction.LABEL), 'Debug: Disable All Breakpoints', debugCategory); diff --git a/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts b/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts index d303e2417ff..3a1bc568bd4 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts @@ -323,8 +323,10 @@ export class ConfigurationManager implements IConfigurationManager { this.initLaunches(); this.selectConfiguration(); })); - this.toDispose.push(this.configurationService.onDidUpdateConfiguration(() => { - this.selectConfiguration(); + this.toDispose.push(this.configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('launch')) { + this.selectConfiguration(); + } })); this.toDispose.push(lifecycleService.onShutdown(this.store, this)); diff --git a/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.ts b/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.ts index 0660d442cfa..b4c56b11b18 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.ts @@ -39,6 +39,7 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { Position } from 'vs/editor/common/core/position'; import { CoreEditingCommands } from 'vs/editor/common/controller/coreCommands'; import { first } from 'vs/base/common/arrays'; +import { IMarginData } from 'vs/editor/browser/controller/mouseTarget'; const HOVER_DELAY = 300; const LAUNCH_JSON_REGEX = /launch\.json$/; @@ -146,7 +147,8 @@ export class DebugEditorContribution implements IDebugEditorContribution { private registerListeners(): void { this.toDispose.push(this.editor.onMouseDown((e: IEditorMouseEvent) => { - if (e.target.type !== MouseTargetType.GUTTER_GLYPH_MARGIN || /* after last line */ e.target.detail || !this.marginFreeFromNonDebugDecorations(e.target.position.lineNumber)) { + const data = e.target.detail as IMarginData; + if (e.target.type !== MouseTargetType.GUTTER_GLYPH_MARGIN || data.isAfterLines || !this.marginFreeFromNonDebugDecorations(e.target.position.lineNumber)) { return; } const canSetBreakpoints = this.debugService.getConfigurationManager().canSetBreakpointsIn(this.editor.getModel()); @@ -182,8 +184,8 @@ export class DebugEditorContribution implements IDebugEditorContribution { let showBreakpointHintAtLineNumber = -1; if (e.target.type === MouseTargetType.GUTTER_GLYPH_MARGIN && this.debugService.getConfigurationManager().canSetBreakpointsIn(this.editor.getModel()) && this.marginFreeFromNonDebugDecorations(e.target.position.lineNumber)) { - if (!e.target.detail) { - // is not after last line + const data = e.target.detail as IMarginData; + if (!data.isAfterLines) { showBreakpointHintAtLineNumber = e.target.position.lineNumber; } } diff --git a/src/vs/workbench/parts/debug/electron-browser/debugService.ts b/src/vs/workbench/parts/debug/electron-browser/debugService.ts index b5f6a854f91..2b5446c6602 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugService.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugService.ts @@ -651,7 +651,7 @@ export class DebugService implements debug.IDebugService { 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(() => + return this.extensionService.activateByEvent('onDebug').then(() => this.textFileService.saveAll().then(() => this.configurationService.reloadConfiguration(root).then(() => this.extensionService.onReady().then(() => { if (this.model.getProcesses().length === 0) { this.removeReplExpressions(); @@ -703,6 +703,12 @@ export class DebugService implements debug.IDebugService { const sessionId = generateUuid(); this.updateStateAndEmit(sessionId, debug.State.Initializing); + const wrapUpState = () => { + if (this.sessionStates.get(sessionId) === debug.State.Initializing) { + this.updateStateAndEmit(sessionId, debug.State.Inactive); + } + }; + return (type ? TPromise.as(null) : this.configurationManager.guessAdapter().then(a => type = a && a.type)).then(() => this.configurationManager.resolveConfigurationByProviders(launch ? launch.workspace.uri : undefined, type, config).then(config => { // a falsy config indicates an aborted launch @@ -710,10 +716,12 @@ export class DebugService implements debug.IDebugService { return this.createProcess(root, config, sessionId); } - this.updateStateAndEmit(sessionId, debug.State.Inactive); return launch.openConfigFile(false, type); // cast to ignore weird compile error }) - ); + ).then(() => wrapUpState(), (err) => { + wrapUpState(); + return err; + }); }) ))); } @@ -727,7 +735,7 @@ export class DebugService implements debug.IDebugService { return null; } - public createProcess(root: IWorkspaceFolder, config: debug.IConfig, sessionId?: string): TPromise { + private createProcess(root: IWorkspaceFolder, config: debug.IConfig, sessionId: string): TPromise { return this.textFileService.saveAll().then(() => (this.configurationManager.selectedLaunch ? this.configurationManager.selectedLaunch.resolveConfiguration(config) : TPromise.as(config)).then(resolvedConfig => { if (!resolvedConfig) { @@ -749,11 +757,6 @@ export class DebugService implements debug.IDebugService { return TPromise.wrapError(errors.create(message, { actions: [this.instantiationService.createInstance(debugactions.ConfigureAction, debugactions.ConfigureAction.ID, debugactions.ConfigureAction.LABEL), CloseAction] })); } - if (!sessionId) { - sessionId = generateUuid(); - this.updateStateAndEmit(sessionId, debug.State.Initializing); - } - return this.runPreLaunchTask(root, resolvedConfig.preLaunchTask).then((taskSummary: ITaskSummary) => { const errorCount = resolvedConfig.preLaunchTask ? this.markerService.getStatistics().errors : 0; const successExitCode = taskSummary && taskSummary.exitCode === 0; @@ -777,7 +780,6 @@ export class DebugService implements debug.IDebugService { }); return undefined; }, (err: TaskError) => { - this.updateStateAndEmit(sessionId, debug.State.Inactive); this.messageService.show(err.severity, { message: err.message, actions: [ @@ -788,7 +790,6 @@ export class DebugService implements debug.IDebugService { }); }); }, err => { - this.updateStateAndEmit(sessionId, debug.State.Inactive); if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) { this.messageService.show(severity.Error, nls.localize('noFolderWorkspaceDebugError', "The active file can not be debugged. Make sure it is saved on disk and that you have a debug extension installed for that file type.")); return undefined; @@ -1003,7 +1004,7 @@ export class DebugService implements debug.IDebugService { config.noDebug = process.configuration.noDebug; } config.__restart = restartData; - this.createProcess(process.session.root, config).then(() => c(null), err => e(err)); + this.createProcess(process.session.root, config, process.getId()).then(() => c(null), err => e(err)); }, 300); }); }).then(() => { diff --git a/src/vs/workbench/parts/debug/electron-browser/debugViewer.ts b/src/vs/workbench/parts/debug/electron-browser/debugViewer.ts index d8104fbc3a2..bc97bf770ba 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugViewer.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugViewer.ts @@ -282,6 +282,9 @@ export class CallStackController extends BaseDebugController { return element.source.uri.toString(); } + if (element instanceof Thread) { + return element.threadId; + } } // user clicked / pressed on 'Load More Stack Frames', get those stack frames and refresh the tree. @@ -1062,11 +1065,12 @@ export class BreakpointsActionProvider implements IActionProvider { } public getSecondaryActions(tree: ITree, element: any): TPromise { - const actions: IAction[] = []; - - if (element instanceof Breakpoint || element instanceof FunctionBreakpoint) { - actions.push(this.instantiationService.createInstance(RemoveBreakpointAction, RemoveBreakpointAction.ID, RemoveBreakpointAction.LABEL)); + if (element instanceof ExceptionBreakpoint) { + return TPromise.as([]); } + + const actions: IAction[] = []; + actions.push(this.instantiationService.createInstance(RemoveBreakpointAction, RemoveBreakpointAction.ID, RemoveBreakpointAction.LABEL)); if (this.debugService.getModel().getBreakpoints().length + this.debugService.getModel().getFunctionBreakpoints().length > 1) { actions.push(this.instantiationService.createInstance(RemoveAllBreakpointsAction, RemoveAllBreakpointsAction.ID, RemoveAllBreakpointsAction.LABEL)); actions.push(new Separator()); diff --git a/src/vs/workbench/parts/debug/test/common/mockDebug.ts b/src/vs/workbench/parts/debug/test/common/mockDebug.ts index 975950318de..c36e51331c9 100644 --- a/src/vs/workbench/parts/debug/test/common/mockDebug.ts +++ b/src/vs/workbench/parts/debug/test/common/mockDebug.ts @@ -92,10 +92,6 @@ export class MockDebugService implements debug.IDebugService { return TPromise.as(null); } - public createProcess(root: IWorkspaceFolder, config: debug.IConfig): TPromise { - return TPromise.as(null); - } - public findProcessByUUID(uuid: string): debug.IProcess | null { return null; } diff --git a/src/vs/workbench/parts/extensions/browser/extensionsActions.ts b/src/vs/workbench/parts/extensions/browser/extensionsActions.ts index 1217632d2ab..9fe32c36539 100644 --- a/src/vs/workbench/parts/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/parts/extensions/browser/extensionsActions.ts @@ -15,7 +15,7 @@ import * as json from 'vs/base/common/json'; import { ActionItem, IActionItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { IExtension, ExtensionState, IExtensionsWorkbenchService, VIEWLET_ID, IExtensionsViewlet } from 'vs/workbench/parts/extensions/common/extensions'; +import { IExtension, ExtensionState, IExtensionsWorkbenchService, VIEWLET_ID, IExtensionsViewlet, AutoUpdateConfigurationKey } from 'vs/workbench/parts/extensions/common/extensions'; import { ExtensionsConfigurationInitialContent } from 'vs/workbench/parts/extensions/common/extensionsFileTemplate'; import { LocalExtensionType, IExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; @@ -704,20 +704,19 @@ export class ToggleAutoUpdateAction extends Action { id: string, label: string, private autoUpdateValue: boolean, - @IConfigurationService configurationService: IConfigurationService, - @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService + @IConfigurationService private configurationService: IConfigurationService ) { super(id, label, '', true); this.updateEnablement(); - configurationService.onDidUpdateConfiguration(() => this.updateEnablement()); + configurationService.onDidChangeConfiguration(() => this.updateEnablement()); } private updateEnablement(): void { - this.enabled = this.extensionsWorkbenchService.isAutoUpdateEnabled !== this.autoUpdateValue; + this.enabled = this.configurationService.getValue(AutoUpdateConfigurationKey) !== this.autoUpdateValue; } run(): TPromise { - return this.extensionsWorkbenchService.setAutoUpdate(this.autoUpdateValue); + return this.configurationService.updateValue(AutoUpdateConfigurationKey, this.autoUpdateValue); } } @@ -729,10 +728,9 @@ export class EnableAutoUpdateAction extends ToggleAutoUpdateAction { constructor( id = EnableAutoUpdateAction.ID, label = EnableAutoUpdateAction.LABEL, - @IConfigurationService configurationService: IConfigurationService, - @IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService + @IConfigurationService configurationService: IConfigurationService ) { - super(id, label, true, configurationService, extensionsWorkbenchService); + super(id, label, true, configurationService); } } @@ -744,10 +742,9 @@ export class DisableAutoUpdateAction extends ToggleAutoUpdateAction { constructor( id = EnableAutoUpdateAction.ID, label = EnableAutoUpdateAction.LABEL, - @IConfigurationService configurationService: IConfigurationService, - @IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService + @IConfigurationService configurationService: IConfigurationService ) { - super(id, label, false, configurationService, extensionsWorkbenchService); + super(id, label, false, configurationService); } } diff --git a/src/vs/workbench/parts/extensions/common/extensions.ts b/src/vs/workbench/parts/extensions/common/extensions.ts index 788e1135a2a..c19f4aba5be 100644 --- a/src/vs/workbench/parts/extensions/common/extensions.ts +++ b/src/vs/workbench/parts/extensions/common/extensions.ts @@ -67,7 +67,6 @@ export interface IExtensionsWorkbenchService { _serviceBrand: any; onChange: Event; local: IExtension[]; - isAutoUpdateEnabled: boolean; queryLocal(): TPromise; queryGallery(options?: IQueryOptions): TPromise>; canInstall(extension: IExtension): boolean; @@ -78,11 +77,11 @@ export interface IExtensionsWorkbenchService { loadDependencies(extension: IExtension): TPromise; open(extension: IExtension, sideByside?: boolean): TPromise; checkForUpdates(): TPromise; - setAutoUpdate(autoUpdate: boolean): TPromise; allowedBadgeProviders: string[]; } export const ConfigurationKey = 'extensions'; +export const AutoUpdateConfigurationKey = 'extensions.autoUpdate'; export interface IExtensionsConfiguration { autoUpdate: boolean; diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts index a365c3fe77d..ee2fdca900c 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts @@ -7,7 +7,7 @@ import { localize } from 'vs/nls'; import * as paths from 'vs/base/common/paths'; import { TPromise } from 'vs/base/common/winjs.base'; import { forEach } from 'vs/base/common/collections'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; import { match } from 'vs/base/common/glob'; import * as json from 'vs/base/common/json'; import { IExtensionManagementService, IExtensionGalleryService, IExtensionTipsService, LocalExtensionType, EXTENSION_IDENTIFIER_PATTERN } from 'vs/platform/extensionManagement/common/extensionManagement'; @@ -19,12 +19,11 @@ 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, IWorkspaceFolder, IWorkspace } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, IWorkspaceFolder, IWorkspace, IWorkspaceFoldersChangeEvent } 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 { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import * as pfs from 'vs/base/node/pfs'; import * as os from 'os'; @@ -37,7 +36,7 @@ interface IExtensionsContent { const empty: { [key: string]: any; } = Object.create(null); const milliSecondsInADay = 1000 * 60 * 60 * 24; -export class ExtensionTipsService implements IExtensionTipsService { +export class ExtensionTipsService extends Disposable implements IExtensionTipsService { _serviceBrand: any; @@ -46,9 +45,11 @@ export class ExtensionTipsService implements IExtensionTipsService { private _availableRecommendations: { [pattern: string]: string[] } = Object.create(null); private importantRecommendations: { [id: string]: { name: string; pattern: string; } } = Object.create(null); private importantRecommendationsIgnoreList: string[]; - private _allRecommendations: string[]; + private _allRecommendations: string[] = []; private _disposables: IDisposable[] = []; + private _allWorkspaceRecommendedExtensions: string[] = []; + constructor( @IExtensionGalleryService private _galleryService: IExtensionGalleryService, @IModelService private _modelService: IModelService, @@ -59,26 +60,32 @@ export class ExtensionTipsService implements IExtensionTipsService { @IFileService private fileService: IFileService, @IWorkspaceContextService private contextService: IWorkspaceContextService, @IConfigurationService private configurationService: IConfigurationService, - @IConfigurationEditingService private configurationEditingService: IConfigurationEditingService, @IMessageService private messageService: IMessageService, @ITelemetryService private telemetryService: ITelemetryService ) { + super(); + if (!this._galleryService.isEnabled()) { return; } + 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); + this._register(this.contextService.onDidChangeWorkspaceFolders(e => this.onWorkspaceFoldersChanged(e))); } getWorkspaceRecommendations(): TPromise { const workspace = this.contextService.getWorkspace(); return TPromise.join([this.resolveWorkspaceRecommendations(workspace), ...workspace.folders.map(workspaceFolder => this.resolveWorkspaceFolderRecommendations(workspaceFolder))]) - .then(recommendations => distinct(flatten(recommendations))); + .then(recommendations => { + this._allWorkspaceRecommendedExtensions = distinct(flatten(recommendations)); + return this._allWorkspaceRecommendedExtensions; + }); } private resolveWorkspaceRecommendations(workspace: IWorkspace): TPromise { @@ -104,12 +111,23 @@ export class ExtensionTipsService implements IExtensionTipsService { return []; } + private onWorkspaceFoldersChanged(event: IWorkspaceFoldersChangeEvent): void { + if (event.added.length) { + TPromise.join(event.added.map(workspaceFolder => this.resolveWorkspaceFolderRecommendations(workspaceFolder))) + .then(result => { + const newRecommendations = flatten(result); + // Suggest only if atleast one of the newly added recommendtations was not suggested before + if (newRecommendations.some(e => this._allWorkspaceRecommendedExtensions.indexOf(e) === -1)) { + this._suggestWorkspaceRecommendations(); + } + }); + } + } + getRecommendations(installedExtensions: string[], searchText: string): string[] { - const allRecomendations = this._getAllRecommendationsInProduct(); const fileBased = Object.keys(this._fileBasedRecommendations) .filter(recommendation => { - return allRecomendations.indexOf(recommendation) > -1 - && installedExtensions.indexOf(recommendation) === -1 + return installedExtensions.indexOf(recommendation) === -1 && recommendation.toLowerCase().indexOf(searchText) > -1; }).sort((a, b) => { return this._fileBasedRecommendations[a] > this._fileBasedRecommendations[b] ? -1 : 1; @@ -146,16 +164,6 @@ export class ExtensionTipsService implements IExtensionTipsService { return product.keymapExtensionTips || []; } - private _getAllRecommendationsInProduct(): string[] { - if (!this._allRecommendations) { - this._allRecommendations = [...Object.keys(this.importantRecommendations)]; - forEach(this._availableRecommendations, ({ value: ids }) => { - this._allRecommendations.push(...ids); - }); - } - return this._allRecommendations; - } - private _suggestTips() { const extensionTips = product.extensionTips; if (!extensionTips) { @@ -164,26 +172,6 @@ export class ExtensionTipsService implements IExtensionTipsService { this.importantRecommendations = product.extensionImportantTips || Object.create(null); this.importantRecommendationsIgnoreList = JSON.parse(this.storageService.get('extensionsAssistant/importantRecommendationsIgnore', StorageScope.GLOBAL, '[]')); - // retrieve ids of previous recommendations - const storedRecommendationsJson = JSON.parse(this.storageService.get('extensionsAssistant/recommendations', StorageScope.GLOBAL, '[]')); - if (Array.isArray(storedRecommendationsJson)) { - for (let id of storedRecommendationsJson) { - this._fileBasedRecommendations[id] = Date.now(); - } - } else { - const now = Date.now(); - forEach(storedRecommendationsJson, entry => { - if (typeof entry.value === 'number') { - const diff = (now - entry.value) / milliSecondsInADay; - if (diff > 7) { - delete this._fileBasedRecommendations[entry.value]; - } else { - this._fileBasedRecommendations[entry.key] = entry.value; - } - } - }); - } - // group ids by pattern, like {**/*.md} -> [ext.foo1, ext.bar2] this._availableRecommendations = Object.create(null); forEach(extensionTips, entry => { @@ -207,6 +195,31 @@ export class ExtensionTipsService implements IExtensionTipsService { } }); + forEach(this._availableRecommendations, ({ value: ids }) => { + this._allRecommendations.push(...ids); + }); + + // retrieve ids of previous recommendations + const storedRecommendationsJson = JSON.parse(this.storageService.get('extensionsAssistant/recommendations', StorageScope.GLOBAL, '[]')); + + if (Array.isArray(storedRecommendationsJson)) { + for (let id of storedRecommendationsJson) { + if (this._allRecommendations.indexOf(id) > -1) { + this._fileBasedRecommendations[id] = Date.now(); + } + } + } else { + const now = Date.now(); + forEach(storedRecommendationsJson, entry => { + if (typeof entry.value === 'number') { + const diff = (now - entry.value) / milliSecondsInADay; + if (diff <= 7 && this._allRecommendations.indexOf(entry.key) > -1) { + this._fileBasedRecommendations[entry.key] = entry.value; + } + } + }); + } + this._modelService.onModelAdded(this._suggest, this, this._disposables); this._modelService.getModels().forEach(model => this._suggest(model)); } @@ -449,9 +462,7 @@ export class ExtensionTipsService implements IExtensionTipsService { } private setIgnoreRecommendationsConfig(configVal: boolean) { - let target = ConfigurationTarget.USER; - const configKey = 'extensions.ignoreRecommendations'; - this.configurationEditingService.writeConfiguration(target, { key: configKey, value: configVal }); + this.configurationService.updateValue('extensions.ignoreRecommendations', configVal, ConfigurationTarget.USER); if (configVal) { const ignoreWorkspaceRecommendationsStorageKey = 'extensionsAssistant/workspaceRecommendationsIgnore'; this.storageService.store(ignoreWorkspaceRecommendationsStorageKey, true, StorageScope.WORKSPACE); diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts index ccbee9dd3a1..26f160e6164 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts @@ -25,7 +25,7 @@ import { append, $, addStandardDisposableListener, EventType, addClass, removeCl import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IExtensionService } from 'vs/platform/extensions/common/extensions'; -import { IExtensionsWorkbenchService, IExtensionsViewlet, VIEWLET_ID, ExtensionState } from '../common/extensions'; +import { IExtensionsWorkbenchService, IExtensionsViewlet, VIEWLET_ID, ExtensionState, AutoUpdateConfigurationKey } from '../common/extensions'; import { ShowEnabledExtensionsAction, ShowInstalledExtensionsAction, ShowRecommendedExtensionsAction, ShowWorkspaceRecommendedExtensionsAction, ShowPopularExtensionsAction, ShowDisabledExtensionsAction, ShowOutdatedExtensionsAction, ClearExtensionsInputAction, ChangeSortAction, UpdateAllAction, CheckForUpdatesAction, DisableAllAction, EnableAllAction, @@ -41,7 +41,7 @@ import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/edi import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; import { IMessageService, CloseAction } from 'vs/platform/message/common/message'; import Severity from 'vs/base/common/severity'; -import { IActivityBarService, ProgressBadge, NumberBadge } from 'vs/workbench/services/activity/common/activityBarService'; +import { IActivityService, ProgressBadge, NumberBadge } from 'vs/workbench/services/activity/common/activity'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { inputForeground, inputBackground, inputBorder } from 'vs/platform/theme/common/colorRegistry'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -79,8 +79,6 @@ export class ExtensionsViewlet extends PersistentViewsViewlet implements IExtens private secondaryActions: IAction[]; private disposables: IDisposable[] = []; - private isAutoUpdateEnabled: boolean; - constructor( @ITelemetryService telemetryService: ITelemetryService, @IProgressService private progressService: IProgressService, @@ -109,12 +107,9 @@ export class ExtensionsViewlet extends PersistentViewsViewlet implements IExtens this.searchRecommendedExtensionsContextKey = SearchRecommendedExtensionsContext.bindTo(contextKeyService); this.disposables.push(viewletService.onDidViewletOpen(this.onViewletOpen, this, this.disposables)); - this.isAutoUpdateEnabled = this.extensionsWorkbenchService.isAutoUpdateEnabled; - this.configurationService.onDidUpdateConfiguration(() => { - const isAutoUpdateEnabled = this.extensionsWorkbenchService.isAutoUpdateEnabled; - if (this.isAutoUpdateEnabled !== isAutoUpdateEnabled) { - this.isAutoUpdateEnabled = isAutoUpdateEnabled; + this.configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration(AutoUpdateConfigurationKey)) { this.secondaryActions = null; this.updateTitleArea(); } @@ -277,7 +272,7 @@ export class ExtensionsViewlet extends PersistentViewsViewlet implements IExtens this.instantiationService.createInstance(ChangeSortAction, 'extensions.sort.name', localize('sort by name', "Sort By: Name"), this.onSearchChange, 'name'), new Separator(), this.instantiationService.createInstance(CheckForUpdatesAction, CheckForUpdatesAction.ID, CheckForUpdatesAction.LABEL), - ...(this.isAutoUpdateEnabled ? [this.instantiationService.createInstance(DisableAutoUpdateAction, DisableAutoUpdateAction.ID, DisableAutoUpdateAction.LABEL)] : [this.instantiationService.createInstance(UpdateAllAction, UpdateAllAction.ID, UpdateAllAction.LABEL), this.instantiationService.createInstance(EnableAutoUpdateAction, EnableAutoUpdateAction.ID, EnableAutoUpdateAction.LABEL)]), + ...(this.configurationService.getValue(AutoUpdateConfigurationKey) ? [this.instantiationService.createInstance(DisableAutoUpdateAction, DisableAutoUpdateAction.ID, DisableAutoUpdateAction.LABEL)] : [this.instantiationService.createInstance(UpdateAllAction, UpdateAllAction.ID, UpdateAllAction.LABEL), this.instantiationService.createInstance(EnableAutoUpdateAction, EnableAutoUpdateAction.ID, EnableAutoUpdateAction.LABEL)]), this.instantiationService.createInstance(InstallVSIXAction, InstallVSIXAction.ID, InstallVSIXAction.LABEL), new Separator(), this.instantiationService.createInstance(DisableAllAction, DisableAllAction.ID, DisableAllAction.LABEL), @@ -404,7 +399,7 @@ export class StatusUpdater implements IWorkbenchContribution { private badgeHandle: IDisposable; constructor( - @IActivityBarService private activityBarService: IActivityBarService, + @IActivityService private activityService: IActivityService, @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService ) { extensionsWorkbenchService.onChange(this.onServiceChange, this, this.disposables); @@ -419,14 +414,14 @@ export class StatusUpdater implements IWorkbenchContribution { dispose(this.badgeHandle); if (this.extensionsWorkbenchService.local.some(e => e.state === ExtensionState.Installing)) { - this.badgeHandle = this.activityBarService.showActivity(VIEWLET_ID, new ProgressBadge(() => localize('extensions', "Extensions")), 'extensions-badge progress-badge'); + this.badgeHandle = this.activityService.showActivity(VIEWLET_ID, new ProgressBadge(() => localize('extensions', "Extensions")), 'extensions-badge progress-badge'); return; } const outdated = this.extensionsWorkbenchService.local.reduce((r, e) => r + (e.outdated ? 1 : 0), 0); if (outdated > 0) { const badge = new NumberBadge(outdated, n => localize('outdatedExtensions', '{0} Outdated Extensions', n)); - this.badgeHandle = this.activityBarService.showActivity(VIEWLET_ID, badge, 'extensions-badge count-badge'); + this.badgeHandle = this.activityService.showActivity(VIEWLET_ID, badge, 'extensions-badge count-badge'); } } diff --git a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts index 91edacaad0a..b21a040cde0 100644 --- a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts +++ b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts @@ -26,11 +26,10 @@ import { getGalleryExtensionIdFromLocal, getGalleryExtensionTelemetryData, getLo import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IWindowService } from 'vs/platform/windows/common/windows'; -import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; import { IChoiceService, IMessageService } from 'vs/platform/message/common/message'; import Severity from 'vs/base/common/severity'; import URI from 'vs/base/common/uri'; -import { IExtension, IExtensionDependencies, ExtensionState, IExtensionsWorkbenchService, IExtensionsConfiguration, ConfigurationKey } from 'vs/workbench/parts/extensions/common/extensions'; +import { IExtension, IExtensionDependencies, ExtensionState, IExtensionsWorkbenchService, AutoUpdateConfigurationKey } from 'vs/workbench/parts/extensions/common/extensions'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IURLService } from 'vs/platform/url/common/url'; import { ExtensionsInput } from 'vs/workbench/parts/extensions/common/extensionsInput'; @@ -313,8 +312,6 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService { private _onChange: Emitter = new Emitter(); get onChange(): Event { return this._onChange.event; } - private _isAutoUpdateEnabled: boolean; - private _extensionAllowedBadgeProviders: string[]; constructor( @@ -323,7 +320,6 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService { @IExtensionManagementService private extensionService: IExtensionManagementService, @IExtensionGalleryService private galleryService: IExtensionGalleryService, @IConfigurationService private configurationService: IConfigurationService, - @IConfigurationEditingService private configurationEditingService: IConfigurationEditingService, @ITelemetryService private telemetryService: ITelemetryService, @IMessageService private messageService: IMessageService, @IChoiceService private choiceService: IChoiceService, @@ -348,12 +344,9 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService { .filter(uri => /^extension/.test(uri.path)) .on(this.onOpenExtensionUrl, this, this.disposables); - this._isAutoUpdateEnabled = this.configurationService.getConfiguration(ConfigurationKey).autoUpdate; - this.configurationService.onDidUpdateConfiguration(() => { - const isAutoUpdateEnabled = this.configurationService.getConfiguration(ConfigurationKey).autoUpdate; - if (this._isAutoUpdateEnabled !== isAutoUpdateEnabled) { - this._isAutoUpdateEnabled = isAutoUpdateEnabled; - if (this._isAutoUpdateEnabled) { + this.configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration(AutoUpdateConfigurationKey)) { + if (this.isAutoUpdateEnabled()) { this.checkForUpdates(); } } @@ -456,15 +449,8 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService { return this.syncDelayer.trigger(() => this.syncWithGallery(), 0); } - get isAutoUpdateEnabled(): boolean { - return this._isAutoUpdateEnabled; - } - - setAutoUpdate(autoUpdate: boolean): TPromise { - if (this.isAutoUpdateEnabled === autoUpdate) { - return TPromise.as(null); - } - return this.configurationEditingService.writeConfiguration(ConfigurationTarget.USER, { key: 'extensions.autoUpdate', value: autoUpdate }); + private isAutoUpdateEnabled(): boolean { + return this.configurationService.getValue(AutoUpdateConfigurationKey); } private eventuallySyncWithGallery(immediate = false): void { @@ -493,7 +479,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService { } private autoUpdateExtensions(): TPromise { - if (!this.isAutoUpdateEnabled) { + if (!this.isAutoUpdateEnabled()) { return TPromise.as(null); } diff --git a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsActions.test.ts b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsActions.test.ts index 405be3a135f..17f180eedc1 100644 --- a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsActions.test.ts +++ b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsActions.test.ts @@ -53,7 +53,7 @@ suite('ExtensionsActions Test', () => { instantiationService.stub(ITelemetryService, NullTelemetryService); instantiationService.stub(IWorkspaceContextService, new TestContextService()); - instantiationService.stub(IConfigurationService, { onDidUpdateConfiguration: () => { }, getConfiguration: () => ({}) }); + instantiationService.stub(IConfigurationService, { onDidUpdateConfiguration: () => { }, onDidChangeConfiguration: () => { }, getConfiguration: () => ({}) }); instantiationService.stub(IExtensionGalleryService, ExtensionGalleryService); diff --git a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsWorkbenchService.test.ts b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsWorkbenchService.test.ts index 3cfe083cd16..2fad5c275f0 100644 --- a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsWorkbenchService.test.ts +++ b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsWorkbenchService.test.ts @@ -56,7 +56,7 @@ suite('ExtensionsWorkbenchService Test', () => { instantiationService.stub(IExtensionGalleryService, ExtensionGalleryService); instantiationService.stub(IWorkspaceContextService, new TestContextService()); - instantiationService.stub(IConfigurationService, { onDidUpdateConfiguration: () => { }, getConfiguration: () => ({}) }); + instantiationService.stub(IConfigurationService, { onDidUpdateConfiguration: () => { }, onDidChangeConfiguration: () => { }, getConfiguration: () => ({}) }); instantiationService.stub(IExtensionManagementService, ExtensionManagementService); instantiationService.stub(IExtensionManagementService, 'onInstallExtension', installEvent.event); diff --git a/src/vs/workbench/parts/files/browser/explorerViewlet.ts b/src/vs/workbench/parts/files/browser/explorerViewlet.ts index 44cac3254e1..ccf0c46e326 100644 --- a/src/vs/workbench/parts/files/browser/explorerViewlet.ts +++ b/src/vs/workbench/parts/files/browser/explorerViewlet.ts @@ -13,8 +13,7 @@ import * as DOM from 'vs/base/browser/dom'; import { Builder } from 'vs/base/browser/builder'; import { VIEWLET_ID, ExplorerViewletVisibleContext, IFilesConfiguration, OpenEditorsVisibleContext, OpenEditorsVisibleCondition } from 'vs/workbench/parts/files/common/files'; import { PersistentViewsViewlet, ViewsViewletPanel, IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IConfigurationEditingService } from 'vs/workbench/services/configuration/common/configurationEditing'; +import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; import { ActionRunner, FileViewletState } from 'vs/workbench/parts/files/browser/views/explorerViewer'; import { ExplorerView, IExplorerViewOptions } from 'vs/workbench/parts/files/browser/views/explorerView'; import { EmptyView } from 'vs/workbench/parts/files/browser/views/emptyView'; @@ -24,11 +23,10 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IExtensionService } from 'vs/platform/extensions/common/extensions'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { DelegatingWorkbenchEditorService } from 'vs/workbench/services/editor/browser/editorService'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { EditorInput, EditorOptions } from 'vs/workbench/common/editor'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; -import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IWorkbenchEditorService, DelegatingWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IThemeService } from 'vs/platform/theme/common/themeService'; @@ -52,7 +50,6 @@ export class ExplorerViewlet extends PersistentViewsViewlet { @IConfigurationService private configurationService: IConfigurationService, @IInstantiationService protected instantiationService: IInstantiationService, @IContextKeyService contextKeyService: IContextKeyService, - @IConfigurationEditingService private configurationEditingService: IConfigurationEditingService, @IThemeService themeService: IThemeService, @IContextMenuService contextMenuService: IContextMenuService, @IExtensionService extensionService: IExtensionService @@ -64,8 +61,9 @@ export class ExplorerViewlet extends PersistentViewsViewlet { this.openEditorsVisibleContextKey = OpenEditorsVisibleContext.bindTo(contextKeyService); this.registerViews(); - this.onConfigurationUpdated(); - this._register(this.configurationService.onDidUpdateConfiguration(e => this.onConfigurationUpdated())); + this.updateOpenEditorsVisibility(); + + this._register(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated(e))); this._register(this.contextService.onDidChangeWorkspaceName(e => this.updateTitleArea())); this._register(this.contextService.onDidChangeWorkbenchState(() => this.registerViews())); this._register(this.contextService.onDidChangeWorkspaceFolders(() => this.registerViews())); @@ -152,8 +150,14 @@ export class ExplorerViewlet extends PersistentViewsViewlet { }; } - private onConfigurationUpdated(): void { - this.openEditorsVisibleContextKey.set(this.contextService.getWorkbenchState() === WorkbenchState.EMPTY || (this.configurationService.getConfiguration()).explorer.openEditors.visible !== 0); + private onConfigurationUpdated(e: IConfigurationChangeEvent): void { + if (e.affectsConfiguration('explorer.openEditors.visible')) { + this.updateOpenEditorsVisibility(); + } + } + + private updateOpenEditorsVisibility(): void { + this.openEditorsVisibleContextKey.set(this.contextService.getWorkbenchState() === WorkbenchState.EMPTY || this.configurationService.getValue('explorer.openEditors.visible') !== 0); } protected createView(viewDescriptor: IViewDescriptor, options: IViewletViewOptions): ViewsViewletPanel { diff --git a/src/vs/workbench/parts/files/browser/fileActions.ts b/src/vs/workbench/parts/files/browser/fileActions.ts index 8f9afccbafe..5bfcb8ac062 100644 --- a/src/vs/workbench/parts/files/browser/fileActions.ts +++ b/src/vs/workbench/parts/files/browser/fileActions.ts @@ -27,7 +27,7 @@ import { VIEWLET_ID, FileOnDiskContentProvider } from 'vs/workbench/parts/files/ import labels = require('vs/base/common/labels'); import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IFileService, IFileStat } from 'vs/platform/files/common/files'; -import { toResource, IEditorIdentifier, EditorInput } from 'vs/workbench/common/editor'; +import { toResource, IEditorIdentifier } from 'vs/workbench/common/editor'; import { FileStat, Model, NewStatPlaceholder } from 'vs/workbench/parts/files/common/explorerModel'; import { ExplorerView } from 'vs/workbench/parts/files/browser/views/explorerView'; import { ExplorerViewlet } from 'vs/workbench/parts/files/browser/explorerViewlet'; @@ -35,10 +35,9 @@ import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/un import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { CollapseAction } from 'vs/workbench/browser/viewlet'; import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; -import { IQuickOpenService, IFilePickOpenEntry } from 'vs/platform/quickOpen/common/quickOpen'; -import { IHistoryService } from 'vs/workbench/services/history/common/history'; +import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; -import { Position, IResourceInput, IEditorInput, IUntitledResourceInput } from 'vs/platform/editor/common/editor'; +import { Position, IResourceInput, IUntitledResourceInput } from 'vs/platform/editor/common/editor'; import { IInstantiationService, IConstructorSignature2, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IMessageService, IMessageWithAction, IConfirmation, Severity, CancelAction, IConfirmationResult } from 'vs/platform/message/common/message'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; @@ -50,8 +49,8 @@ import { withFocusedFilesExplorer, revealInOSCommand, revealInExplorerCommand, c import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; +import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; +import { once } from 'vs/base/common/event'; export interface IEditableData { action: IAction; @@ -641,8 +640,7 @@ export class BaseDeleteFileAction extends BaseFileAction { @IFileService fileService: IFileService, @IMessageService messageService: IMessageService, @ITextFileService textFileService: ITextFileService, - @IConfigurationService private configurationService: IConfigurationService, - @IConfigurationEditingService private configurationEditingService: IConfigurationEditingService + @IConfigurationService private configurationService: IConfigurationService ) { super(id, label, fileService, messageService, textFileService); @@ -714,7 +712,7 @@ export class BaseDeleteFileAction extends BaseFileAction { let confirmPromise: TPromise; // Check if we need to ask for confirmation at all - if (this.skipConfirm || (this.useTrash && this.configurationService.lookup(BaseDeleteFileAction.CONFIRM_DELETE_SETTING_KEY).value === false)) { + if (this.skipConfirm || (this.useTrash && this.configurationService.getValue(BaseDeleteFileAction.CONFIRM_DELETE_SETTING_KEY) === false)) { confirmPromise = TPromise.as({ confirmed: true } as IConfirmationResult); } @@ -746,7 +744,7 @@ export class BaseDeleteFileAction extends BaseFileAction { // Check for confirmation checkbox let updateConfirmSettingsPromise: TPromise = TPromise.as(void 0); if (confirmation.checkboxChecked === true) { - updateConfirmSettingsPromise = this.configurationEditingService.writeConfiguration(ConfigurationTarget.USER, { key: BaseDeleteFileAction.CONFIRM_DELETE_SETTING_KEY, value: false }); + updateConfirmSettingsPromise = this.configurationService.updateValue(BaseDeleteFileAction.CONFIRM_DELETE_SETTING_KEY, false, ConfigurationTarget.USER); } return updateConfirmSettingsPromise.then(() => { @@ -792,10 +790,9 @@ export class MoveFileToTrashAction extends BaseDeleteFileAction { @IFileService fileService: IFileService, @IMessageService messageService: IMessageService, @ITextFileService textFileService: ITextFileService, - @IConfigurationService configurationService: IConfigurationService, - @IConfigurationEditingService configurationEditingService: IConfigurationEditingService + @IConfigurationService configurationService: IConfigurationService ) { - super(MoveFileToTrashAction.ID, nls.localize('delete', "Delete"), tree, element, true, fileService, messageService, textFileService, configurationService, configurationEditingService); + super(MoveFileToTrashAction.ID, nls.localize('delete', "Delete"), tree, element, true, fileService, messageService, textFileService, configurationService); } } @@ -1197,12 +1194,9 @@ export class GlobalCompareResourcesAction extends Action { id: string, label: string, @IQuickOpenService private quickOpenService: IQuickOpenService, - @IInstantiationService private instantiationService: IInstantiationService, @IWorkbenchEditorService private editorService: IWorkbenchEditorService, - @IHistoryService private historyService: IHistoryService, - @IWorkspaceContextService private contextService: IWorkspaceContextService, @IMessageService private messageService: IMessageService, - @IEnvironmentService private environmentService: IEnvironmentService + @IEditorGroupService private editorGroupService: IEditorGroupService ) { super(id, label); } @@ -1212,50 +1206,22 @@ export class GlobalCompareResourcesAction extends Action { const activeResource = activeInput ? activeInput.getResource() : void 0; if (activeResource) { - // Keep as resource to compare - globalResourceToCompare = activeResource; - - // Pick another entry from history - interface IHistoryPickEntry extends IFilePickOpenEntry { - input: IEditorInput | IResourceInput; - } - - const history = this.historyService.getHistory(); - const picks: IHistoryPickEntry[] = history.map(input => { - let resource: URI; - let label: string; - let description: string; - - if (input instanceof EditorInput) { - resource = input.getResource(); - } else { - resource = (input as IResourceInput).resource; + // Compare with next editor that opens + const unbind = once(this.editorGroupService.onEditorOpening)(e => { + const resource = e.input.getResource(); + if (resource) { + e.prevent(() => { + return this.editorService.openEditor({ + leftResource: activeResource, + rightResource: resource + }); + }); } + }); - // Cannot compare file with self - exclude active file - if (!!resource && resource.toString() === globalResourceToCompare.toString()) { - return void 0; - } - - if (!resource) { - return void 0; // only support to compare with files and untitled - } - - label = paths.basename(resource.fsPath); - description = labels.getPathLabel(resources.dirname(resource), this.contextService, this.environmentService); - - return { input, resource, label, description }; - }).filter(p => !!p); - - return this.quickOpenService.pick(picks, { placeHolder: nls.localize('pickHistory', "Select a previously opened file to compare with"), autoFocus: { autoFocusFirstEntry: true }, matchOnDescription: true }).then(pick => { - if (pick) { - const compareAction = this.instantiationService.createInstance(CompareResourcesAction, pick.resource, null); - if (compareAction._isEnabled()) { - compareAction.run().done(() => compareAction.dispose()); - } else { - this.messageService.show(Severity.Info, nls.localize('unableToFileToCompare', "The selected file can not be compared with '{0}'.", paths.basename(globalResourceToCompare.fsPath))); - } - } + // Bring up quick open + this.quickOpenService.show('', { autoFocus: { autoFocusSecondEntry: true } }).then(() => { + unbind.dispose(); // make sure to unbind if quick open is closing }); } else { this.messageService.show(Severity.Info, nls.localize('openFileToCompare', "Open a file first to compare it with another file.")); @@ -1305,7 +1271,7 @@ export class CompareResourcesAction extends Action { return nls.localize('compareFiles', "Compare Files"); } - _isEnabled(): boolean { + public _isEnabled(): boolean { // Need at least a resource to compare if (!globalResourceToCompare) { @@ -2016,20 +1982,28 @@ export function validateFileName(parent: IFileStat, name: string, allowOverwriti // Invalid File name if (!paths.isValidBasename(name)) { - return nls.localize('invalidFileNameError', "The name **{0}** is not valid as a file or folder name. Please choose a different name.", name); + return nls.localize('invalidFileNameError', "The name **{0}** is not valid as a file or folder name. Please choose a different name.", trimLongName(name)); } // Max length restriction (on Windows) if (isWindows) { const fullPathLength = name.length + parent.resource.fsPath.length + 1 /* path segment */; if (fullPathLength > 255) { - return nls.localize('filePathTooLongError', "The name **{0}** results in a path that is too long. Please choose a shorter name.", name); + return nls.localize('filePathTooLongError', "The name **{0}** results in a path that is too long. Please choose a shorter name.", trimLongName(name)); } } return null; } +function trimLongName(name: string): string { + if (name && name.length > 255) { + return `${name.substr(0, 255)}...`; + } + + return name; +} + export function getWellFormedFileName(filename: string): string { if (!filename) { return filename; diff --git a/src/vs/workbench/parts/files/browser/files.contribution.ts b/src/vs/workbench/parts/files/browser/files.contribution.ts index 878dea1810b..12d38ec1aa7 100644 --- a/src/vs/workbench/parts/files/browser/files.contribution.ts +++ b/src/vs/workbench/parts/files/browser/files.contribution.ts @@ -276,7 +276,7 @@ configurationRegistry.registerConfiguration({ }, 'files.useExperimentalFileWatcher': { 'type': 'boolean', - 'default': false, + 'default': true, 'description': nls.localize('useExperimentalFileWatcher', "Use the new experimental file watcher.") }, 'files.defaultLanguage': { diff --git a/src/vs/workbench/parts/files/browser/views/explorerView.ts b/src/vs/workbench/parts/files/browser/views/explorerView.ts index c9680dc41f9..4bfa0322764 100644 --- a/src/vs/workbench/parts/files/browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/browser/views/explorerView.ts @@ -19,7 +19,7 @@ import { memoize } from 'vs/base/common/decorators'; import { ITree } from 'vs/base/parts/tree/browser/tree'; import { Tree } from 'vs/base/parts/tree/browser/treeImpl'; import { IFilesConfiguration, ExplorerFolderContext, FilesExplorerFocusedContext, ExplorerFocusedContext, SortOrderConfiguration, SortOrder } from 'vs/workbench/parts/files/common/files'; -import { FileOperation, FileOperationEvent, IResolveFileOptions, FileChangeType, FileChangesEvent, IFileService } from 'vs/platform/files/common/files'; +import { FileOperation, FileOperationEvent, IResolveFileOptions, FileChangeType, FileChangesEvent, IFileService, FILES_EXCLUDE_CONFIG } from 'vs/platform/files/common/files'; import { RefreshViewExplorerAction, NewFolderAction, NewFileAction } from 'vs/workbench/parts/files/browser/fileActions'; import { FileDragAndDrop, FileFilter, FileSorter, FileController, FileRenderer, FileDataSource, FileViewletState, FileAccessibilityProvider } from 'vs/workbench/parts/files/browser/views/explorerViewer'; import { toResource } from 'vs/workbench/common/editor'; @@ -33,7 +33,7 @@ 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, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IProgressService } from 'vs/platform/progress/common/progress'; @@ -115,12 +115,16 @@ export class ExplorerView extends ViewsViewletPanel { this.filesExplorerFocusedContext = FilesExplorerFocusedContext.bindTo(contextKeyService); this.explorerFocusedContext = ExplorerFocusedContext.bindTo(contextKeyService); - this.fileEventsFilter = instantiationService.createInstance(ResourceGlobMatcher, (root: URI) => this.getFileEventsExcludes(root), (expression: glob.IExpression) => glob.parse(expression)); + this.fileEventsFilter = instantiationService.createInstance( + ResourceGlobMatcher, + (root: URI) => this.getFileEventsExcludes(root), + (event: IConfigurationChangeEvent) => event.affectsConfiguration(FILES_EXCLUDE_CONFIG) + ); } private getFileEventsExcludes(root?: URI): glob.IExpression { const scope = root ? { resource: root } : void 0; - const configuration = this.configurationService.getConfiguration(undefined, scope); + const configuration = this.configurationService.getConfiguration(scope); return (configuration && configuration.files && configuration.files.exclude) || Object.create(null); } @@ -144,6 +148,14 @@ export class ExplorerView extends ViewsViewletPanel { return this.contextService.getWorkspace().name; } + public get title(): string { + return this.name; + } + + public set title(value: string) { + // noop + } + public set name(value) { // noop } @@ -204,7 +216,7 @@ export class ExplorerView extends ViewsViewletPanel { this.disposables.push(this.editorGroupService.onEditorsChanged(() => this.onEditorsChanged())); // Also handle configuration updates - this.disposables.push(this.configurationService.onDidUpdateConfiguration(e => this.onConfigurationUpdated(this.configurationService.getConfiguration(), true))); + this.disposables.push(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated(this.configurationService.getConfiguration(), e))); }); } @@ -251,7 +263,7 @@ export class ExplorerView extends ViewsViewletPanel { } } - private onConfigurationUpdated(configuration: IFilesConfiguration, refresh?: boolean): void { + private onConfigurationUpdated(configuration: IFilesConfiguration, event?: IConfigurationChangeEvent): void { if (this.isDisposed) { return; // guard against possible race condition when config change causes recreate of views } @@ -270,8 +282,13 @@ export class ExplorerView extends ViewsViewletPanel { needsRefresh = true; } - // Refresh viewer as needed - if (refresh && needsRefresh) { + if (event && !needsRefresh) { + needsRefresh = event.affectsConfiguration('explorer.decorations.colors') + || event.affectsConfiguration('explorer.decorations.badges'); + } + + // Refresh viewer as needed if this originates from a config event + if (event && needsRefresh) { this.doRefresh().done(null, errors.onUnexpectedError); } } diff --git a/src/vs/workbench/parts/files/browser/views/explorerViewer.ts b/src/vs/workbench/parts/files/browser/views/explorerViewer.ts index b82590980a9..21617036d53 100644 --- a/src/vs/workbench/parts/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/parts/files/browser/views/explorerViewer.ts @@ -37,7 +37,7 @@ import { DragMouseEvent, IMouseEvent } from 'vs/base/browser/mouseEvent'; 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 { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -57,7 +57,6 @@ import { distinct } from 'vs/base/common/arrays'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { getPathLabel } from 'vs/base/common/labels'; import { extractResources } from 'vs/base/browser/dnd'; -import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; export class FileDataSource implements IDataSource { constructor( @@ -563,17 +562,17 @@ export class FileSorter implements ISorter { ) { this.toDispose = []; - this.onConfigurationUpdated(configurationService.getConfiguration()); + this.updateSortOrder(); this.registerListeners(); } private registerListeners(): void { - this.toDispose.push(this.configurationService.onDidUpdateConfiguration(e => this.onConfigurationUpdated(this.configurationService.getConfiguration()))); + this.toDispose.push(this.configurationService.onDidChangeConfiguration(e => this.updateSortOrder())); } - private onConfigurationUpdated(configuration: IFilesConfiguration): void { - this.sortOrder = configuration && configuration.explorer && configuration.explorer.sortOrder || 'default'; + private updateSortOrder(): void { + this.sortOrder = this.configurationService.getValue('explorer.sortOrder') || 'default'; } public compare(tree: ITree, statA: FileStat, statB: FileStat): number { @@ -689,7 +688,7 @@ export class FileFilter implements IFilter { public updateConfiguration(): boolean { let needsRefresh = false; this.contextService.getWorkspace().folders.forEach(folder => { - const configuration = this.configurationService.getConfiguration(undefined, { resource: folder.uri }); + const configuration = this.configurationService.getConfiguration({ resource: folder.uri }); const excludesConfig = (configuration && configuration.files && configuration.files.exclude) || Object.create(null); needsRefresh = needsRefresh || !objects.equals(this.hiddenExpressionPerRoot.get(folder.uri.toString()), excludesConfig); this.hiddenExpressionPerRoot.set(folder.uri.toString(), objects.clone(excludesConfig)); // do not keep the config, as it gets mutated under our hoods @@ -746,14 +745,13 @@ export class FileDragAndDrop extends SimpleFileResourceDragAndDrop { @IBackupFileService private backupFileService: IBackupFileService, @IWindowService private windowService: IWindowService, @IWorkspaceEditingService private workspaceEditingService: IWorkspaceEditingService, - @IEnvironmentService private environmentService: IEnvironmentService, - @IConfigurationEditingService private configurationEditingService: IConfigurationEditingService + @IEnvironmentService private environmentService: IEnvironmentService ) { super(stat => this.statToResource(stat)); this.toDispose = []; - this.onConfigurationUpdated(configurationService.getConfiguration()); + this.updateDropEnablement(); this.registerListeners(); } @@ -771,11 +769,11 @@ export class FileDragAndDrop extends SimpleFileResourceDragAndDrop { } private registerListeners(): void { - this.toDispose.push(this.configurationService.onDidUpdateConfiguration(e => this.onConfigurationUpdated(this.configurationService.getConfiguration()))); + this.toDispose.push(this.configurationService.onDidChangeConfiguration(e => this.updateDropEnablement())); } - private onConfigurationUpdated(config: IFilesConfiguration): void { - this.dropEnabled = config && config.explorer && config.explorer.enableDragAndDrop; + private updateDropEnablement(): void { + this.dropEnabled = this.configurationService.getValue('explorer.enableDragAndDrop'); } public onDragStart(tree: ITree, data: IDragAndDropData, originalEvent: DragMouseEvent): void { @@ -949,7 +947,7 @@ export class FileDragAndDrop extends SimpleFileResourceDragAndDrop { let confirmPromise: TPromise; // Handle confirm setting - const confirmDragAndDrop = !isCopy && this.configurationService.lookup(FileDragAndDrop.CONFIRM_DND_SETTING_KEY).value; + const confirmDragAndDrop = !isCopy && this.configurationService.getValue(FileDragAndDrop.CONFIRM_DND_SETTING_KEY); if (confirmDragAndDrop) { confirmPromise = this.messageService.confirm({ message: nls.localize('confirmMove', "Are you sure you want to move '{0}'?", source.name), @@ -967,7 +965,7 @@ export class FileDragAndDrop extends SimpleFileResourceDragAndDrop { // Check for confirmation checkbox let updateConfirmSettingsPromise: TPromise = TPromise.as(void 0); if (confirmation.checkboxChecked === true) { - updateConfirmSettingsPromise = this.configurationEditingService.writeConfiguration(ConfigurationTarget.USER, { key: FileDragAndDrop.CONFIRM_DND_SETTING_KEY, value: false }); + updateConfirmSettingsPromise = this.configurationService.updateValue(FileDragAndDrop.CONFIRM_DND_SETTING_KEY, false, ConfigurationTarget.USER); } return updateConfirmSettingsPromise.then(() => { diff --git a/src/vs/workbench/parts/files/browser/views/openEditorsView.ts b/src/vs/workbench/parts/files/browser/views/openEditorsView.ts index 51b5ab51ba9..e248d36c195 100644 --- a/src/vs/workbench/parts/files/browser/views/openEditorsView.ts +++ b/src/vs/workbench/parts/files/browser/views/openEditorsView.ts @@ -14,12 +14,12 @@ import { IItemCollapseEvent } from 'vs/base/parts/tree/browser/treeModel'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IEditorStacksModel, IStacksModelChangeEvent, IEditorGroup } from 'vs/workbench/common/editor'; import { SaveAllAction } from 'vs/workbench/parts/files/browser/fileActions'; import { ViewsViewletPanel, IViewletViewOptions, IViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; -import { IFilesConfiguration, VIEWLET_ID, OpenEditorsFocusedContext, ExplorerFocusedContext } from 'vs/workbench/parts/files/common/files'; +import { VIEWLET_ID, OpenEditorsFocusedContext, ExplorerFocusedContext } from 'vs/workbench/parts/files/common/files'; import { ITextFileService, AutoSaveMode } from 'vs/workbench/services/textfile/common/textfiles'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { OpenEditor } from 'vs/workbench/parts/files/common/explorerModel'; @@ -43,9 +43,6 @@ export class OpenEditorsView extends ViewsViewletPanel { static ID = 'workbench.explorer.openEditorsView'; static NAME = nls.localize({ key: 'openEditors', comment: ['Open is an adjective'] }, "Open Editors"); - private visibleOpenEditors: number; - private dynamicHeight: boolean; - private model: IEditorStacksModel; private dirtyCountElement: HTMLElement; private structuralTreeRefreshScheduler: RunOnceScheduler; @@ -168,8 +165,7 @@ export class OpenEditorsView extends ViewsViewletPanel { public create(): TPromise { // Load Config - const configuration = this.configurationService.getConfiguration(); - this.onConfigurationUpdated(configuration); + this.updateSize(); // listeners this.registerListeners(); @@ -183,7 +179,7 @@ export class OpenEditorsView extends ViewsViewletPanel { this.disposables.push(this.model.onModelChanged(e => this.onEditorStacksModelChanged(e))); // Also handle configuration updates - this.disposables.push(this.configurationService.onDidUpdateConfiguration(e => this.onConfigurationUpdated(this.configurationService.getConfiguration()))); + this.disposables.push(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationChange(e))); // Handle dirty counter this.disposables.push(this.untitledEditorService.onDidChangeDirty(e => this.updateDirtyIndicator())); @@ -259,25 +255,22 @@ export class OpenEditorsView extends ViewsViewletPanel { } } - private onConfigurationUpdated(configuration: IFilesConfiguration): void { + private onConfigurationChange(event: IConfigurationChangeEvent): void { if (this.isDisposed) { return; // guard against possible race condition when config change causes recreate of views } - let visibleOpenEditors = configuration && configuration.explorer && configuration.explorer.openEditors && configuration.explorer.openEditors.visible; - if (typeof visibleOpenEditors === 'number') { - this.visibleOpenEditors = visibleOpenEditors; - } else { - this.visibleOpenEditors = OpenEditorsView.DEFAULT_VISIBLE_OPEN_EDITORS; + if (event.affectsConfiguration('explorer.openEditors')) { + this.updateSize(); } - let dynamicHeight = configuration && configuration.explorer && configuration.explorer.openEditors && configuration.explorer.openEditors.dynamicHeight; - if (typeof dynamicHeight === 'boolean') { - this.dynamicHeight = dynamicHeight; - } else { - this.dynamicHeight = OpenEditorsView.DEFAULT_DYNAMIC_HEIGHT; + // Trigger a 'repaint' when decoration settings change + if (event.affectsConfiguration('explorer.decorations')) { + this.tree.refresh(); } + } + private updateSize(): void { // Adjust expanded body size this.minimumBodySize = this.maximumBodySize = this.getExpandedBodySize(this.model); } @@ -294,7 +287,16 @@ export class OpenEditorsView extends ViewsViewletPanel { } private getExpandedBodySize(model: IEditorStacksModel): number { - return OpenEditorsView.computeExpandedBodySize(model, this.visibleOpenEditors, this.dynamicHeight); + let visibleOpenEditors = this.configurationService.getValue('explorer.openEditors.visible'); + if (typeof visibleOpenEditors !== 'number') { + visibleOpenEditors = OpenEditorsView.DEFAULT_VISIBLE_OPEN_EDITORS; + } + + let dynamicHeight = this.configurationService.getValue('explorer.openEditors.dynamicHeight'); + if (typeof dynamicHeight !== 'boolean') { + dynamicHeight = OpenEditorsView.DEFAULT_DYNAMIC_HEIGHT; + } + return OpenEditorsView.computeExpandedBodySize(model, visibleOpenEditors, dynamicHeight); } private static computeExpandedBodySize(model: IEditorStacksModel, visibleOpenEditors = OpenEditorsView.DEFAULT_VISIBLE_OPEN_EDITORS, dynamicHeight = OpenEditorsView.DEFAULT_DYNAMIC_HEIGHT): number { diff --git a/src/vs/workbench/parts/files/browser/views/openEditorsViewer.ts b/src/vs/workbench/parts/files/browser/views/openEditorsViewer.ts index a67a4d87ea6..c72ae86cbaa 100644 --- a/src/vs/workbench/parts/files/browser/views/openEditorsViewer.ts +++ b/src/vs/workbench/parts/files/browser/views/openEditorsViewer.ts @@ -24,13 +24,14 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IEditorGroup, IEditorStacksModel } from 'vs/workbench/common/editor'; import { OpenEditor } from 'vs/workbench/parts/files/common/explorerModel'; import { ContributableActionProvider } from 'vs/workbench/browser/actions'; -import { explorerItemToFileResource } from 'vs/workbench/parts/files/common/files'; +import { explorerItemToFileResource, IFilesConfiguration } from 'vs/workbench/parts/files/common/files'; import { ITextFileService, AutoSaveMode } from 'vs/workbench/services/textfile/common/textfiles'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { EditorStacksModel, EditorGroup } from 'vs/workbench/common/editor/editorStacksModel'; import { SaveFileAction, RevertFileAction, SaveFileAsAction, OpenToSideAction, SelectResourceForCompareAction, CompareResourcesAction, SaveAllInGroupAction, CompareWithSavedAction } from 'vs/workbench/parts/files/browser/fileActions'; import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; import { CloseOtherEditorsInGroupAction, CloseEditorAction, CloseEditorsInGroupAction, CloseUnmodifiedEditorsInGroupAction } from 'vs/workbench/browser/parts/editor/editorActions'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; const $ = dom.$; @@ -86,7 +87,8 @@ export class Renderer implements IRenderer { constructor( private actionProvider: ActionProvider, @IInstantiationService private instantiationService: IInstantiationService, - @IKeybindingService private keybindingService: IKeybindingService + @IKeybindingService private keybindingService: IKeybindingService, + @IConfigurationService private configurationService: IConfigurationService ) { // noop } @@ -149,7 +151,11 @@ export class Renderer implements IRenderer { private renderOpenEditor(tree: ITree, editor: OpenEditor, templateData: IOpenEditorTemplateData): void { editor.isDirty() ? dom.addClass(templateData.container, 'dirty') : dom.removeClass(templateData.container, 'dirty'); - templateData.root.setEditor(editor.editorInput, { italic: editor.isPreview(), extraClasses: ['open-editor'] }); + templateData.root.setEditor(editor.editorInput, { + italic: editor.isPreview(), + extraClasses: ['open-editor'], + fileDecorations: this.configurationService.getConfiguration().explorer.decorations + }); templateData.actionBar.context = { group: editor.editorGroup, editor: editor.editorInput }; } diff --git a/src/vs/workbench/parts/files/common/dirtyFilesTracker.ts b/src/vs/workbench/parts/files/common/dirtyFilesTracker.ts index f02e1e86e24..31c62f9d546 100644 --- a/src/vs/workbench/parts/files/common/dirtyFilesTracker.ts +++ b/src/vs/workbench/parts/files/common/dirtyFilesTracker.ts @@ -19,7 +19,7 @@ import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import URI from 'vs/base/common/uri'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { IActivityBarService, NumberBadge } from 'vs/workbench/services/activity/common/activityBarService'; +import { IActivityService, NumberBadge } from 'vs/workbench/services/activity/common/activity'; import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; import arrays = require('vs/base/common/arrays'); @@ -35,7 +35,7 @@ export class DirtyFilesTracker implements IWorkbenchContribution { @ILifecycleService private lifecycleService: ILifecycleService, @IEditorGroupService editorGroupService: IEditorGroupService, @IWorkbenchEditorService private editorService: IWorkbenchEditorService, - @IActivityBarService private activityBarService: IActivityBarService, + @IActivityService private activityService: IActivityService, @IWindowService private windowService: IWindowService, @IUntitledEditorService private untitledEditorService: IUntitledEditorService ) { @@ -141,7 +141,7 @@ export class DirtyFilesTracker implements IWorkbenchContribution { this.lastDirtyCount = dirtyCount; dispose(this.badgeHandle); if (dirtyCount > 0) { - this.badgeHandle = this.activityBarService.showActivity(VIEWLET_ID, new NumberBadge(dirtyCount, num => num === 1 ? nls.localize('dirtyFile', "1 unsaved file") : nls.localize('dirtyFiles', "{0} unsaved files", dirtyCount)), 'explorer-viewlet-label'); + this.badgeHandle = this.activityService.showActivity(VIEWLET_ID, new NumberBadge(dirtyCount, num => num === 1 ? nls.localize('dirtyFile', "1 unsaved file") : nls.localize('dirtyFiles', "{0} unsaved files", dirtyCount)), 'explorer-viewlet-label'); } } diff --git a/src/vs/workbench/parts/files/common/editors/fileEditorTracker.ts b/src/vs/workbench/parts/files/common/editors/fileEditorTracker.ts index c07ed76b795..3209fbc2a2e 100644 --- a/src/vs/workbench/parts/files/common/editors/fileEditorTracker.ts +++ b/src/vs/workbench/parts/files/common/editors/fileEditorTracker.ts @@ -75,7 +75,7 @@ export class FileEditorTracker implements IWorkbenchContribution { this.lifecycleService.onShutdown(this.dispose, this); // Configuration - this.toUnbind.push(this.configurationService.onDidUpdateConfiguration(e => this.onConfigurationUpdated(this.configurationService.getConfiguration()))); + this.toUnbind.push(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated(this.configurationService.getConfiguration()))); } private onConfigurationUpdated(configuration: IWorkbenchEditorConfiguration): void { diff --git a/src/vs/workbench/parts/files/common/explorerModel.ts b/src/vs/workbench/parts/files/common/explorerModel.ts index e20b9c1921e..33f96015590 100644 --- a/src/vs/workbench/parts/files/common/explorerModel.ts +++ b/src/vs/workbench/parts/files/common/explorerModel.ts @@ -77,7 +77,7 @@ export class FileStat implements IFileStat { public name: string; public mtime: number; public etag: string; - public isDirectory: boolean; + private _isDirectory: boolean; public hasChildren: boolean; public children: FileStat[]; public parent: FileStat; @@ -92,10 +92,6 @@ export class FileStat implements IFileStat { this.etag = etag; this.mtime = mtime; - // Prepare child stat array - if (this.isDirectory) { - this.children = []; - } if (!this.root) { this.root = this; } @@ -103,6 +99,22 @@ export class FileStat implements IFileStat { this.isDirectoryResolved = false; } + public get isDirectory(): boolean { + return this._isDirectory; + } + + public set isDirectory(value: boolean) { + if (value !== this._isDirectory) { + this._isDirectory = value; + if (this._isDirectory) { + this.children = []; + } else { + this.children = undefined; + } + } + + } + public get nonexistentRoot(): boolean { return this.isRoot && !this.isDirectoryResolved; } diff --git a/src/vs/workbench/parts/markers/browser/markersFileDecorations.ts b/src/vs/workbench/parts/markers/browser/markersFileDecorations.ts index d2e31659374..9dd0ce444fb 100644 --- a/src/vs/workbench/parts/markers/browser/markersFileDecorations.ts +++ b/src/vs/workbench/parts/markers/browser/markersFileDecorations.ts @@ -6,13 +6,12 @@ 'use strict'; import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; -import { IMarkerService } from 'vs/platform/markers/common/markers'; -import { IResourceDecorationsService, IDecorationsProvider, IResourceDecorationData } from 'vs/workbench/services/decorations/browser/decorations'; +import { IMarkerService, IMarker } from 'vs/platform/markers/common/markers'; +import { IDecorationsService, IDecorationsProvider, IDecorationData } from 'vs/workbench/services/decorations/browser/decorations'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import URI from 'vs/base/common/uri'; import Event from 'vs/base/common/event'; import { localize } from 'vs/nls'; -import { isFalsyOrEmpty } from 'vs/base/common/arrays'; import { Registry } from 'vs/platform/registry/common/platform'; import Severity from 'vs/base/common/severity'; import { editorErrorForeground, editorWarningForeground } from 'vs/editor/common/view/editorColorRegistry'; @@ -30,19 +29,23 @@ class MarkersDecorationsProvider implements IDecorationsProvider { this.onDidChange = _markerService.onMarkerChanged; } - provideDecorations(resource: URI): IResourceDecorationData { + provideDecorations(resource: URI): IDecorationData { + let markers = this._markerService.read({ resource }); + let first: IMarker; + for (const marker of markers) { + if (!first || marker.severity > first.severity) { + first = marker; + } + } - const markers = this._markerService.read({ resource }) - .sort((a, b) => Severity.compare(a.severity, b.severity)); - - if (isFalsyOrEmpty(markers)) { + if (!first) { return undefined; } - const [first] = markers; return { - severity: first.severity, - tooltip: localize('tooltip', "{0} problems in this file", markers.length), + weight: 100 * first.severity, + bubble: true, + title: markers.length === 1 ? localize('tooltip.1', "1 problem in this file") : localize('tooltip.N', "{0} problems in this file", markers.length), letter: markers.length.toString(), color: first.severity === Severity.Error ? editorErrorForeground : editorWarningForeground, }; @@ -53,17 +56,17 @@ class MarkersFileDecorations implements IWorkbenchContribution { private readonly _disposables: IDisposable[]; private _provider: IDisposable; + private _enabled: boolean; constructor( @IMarkerService private _markerService: IMarkerService, - @IResourceDecorationsService private _decorationsService: IResourceDecorationsService, + @IDecorationsService private _decorationsService: IDecorationsService, @IConfigurationService private _configurationService: IConfigurationService ) { // this._disposables = [ - this._configurationService.onDidUpdateConfiguration(this._updateEnablement, this), + this._configurationService.onDidChangeConfiguration(this._updateEnablement, this), ]; - this._updateEnablement(); } @@ -77,11 +80,16 @@ class MarkersFileDecorations implements IWorkbenchContribution { } private _updateEnablement(): void { - let value = this._configurationService.getConfiguration<{ fileDecorations: { enabled: boolean } }>('problems'); - if (value.fileDecorations.enabled) { + let value = this._configurationService.getConfiguration<{ decorations: { enabled: boolean } }>('problems'); + if (value.decorations.enabled === this._enabled) { + return; + } + this._enabled = value.decorations.enabled; + if (this._enabled) { const provider = new MarkersDecorationsProvider(this._markerService); - this._provider = this._decorationsService.registerDecortionsProvider(provider); + this._provider = this._decorationsService.registerDecorationsProvider(provider); } else if (this._provider) { + this._enabled = value.decorations.enabled; this._provider.dispose(); } } @@ -94,10 +102,10 @@ Registry.as(ConfigurationExtensions.Configuration).regis 'order': 101, 'type': 'object', 'properties': { - 'problems.fileDecorations.enabled': { + 'problems.decorations.enabled': { 'description': localize('markers.showOnFile', "Show Errors & Warnings on files and folder."), 'type': 'boolean', - 'default': true + 'default': false } } }); diff --git a/src/vs/workbench/parts/markers/browser/markersPanel.ts b/src/vs/workbench/parts/markers/browser/markersPanel.ts index a4060b88693..5c811c3b975 100644 --- a/src/vs/workbench/parts/markers/browser/markersPanel.ts +++ b/src/vs/workbench/parts/markers/browser/markersPanel.ts @@ -19,7 +19,7 @@ import { IEditorGroupService } from 'vs/workbench/services/group/common/groupSer import { Panel } from 'vs/workbench/browser/panel'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import Constants from 'vs/workbench/parts/markers/common/constants'; -import { IProblemsConfiguration, MarkersModel, Marker, Resource, FilterOptions } from 'vs/workbench/parts/markers/common/markersModel'; +import { MarkersModel, Marker, Resource, FilterOptions } from 'vs/workbench/parts/markers/common/markersModel'; import { Controller } from 'vs/workbench/parts/markers/browser/markersTreeController'; import Tree = require('vs/base/parts/tree/browser/tree'); import TreeImpl = require('vs/base/parts/tree/browser/treeImpl'); @@ -47,7 +47,6 @@ export class MarkersPanel extends Panel { private lastSelectedRelativeTop: number = 0; private currentActiveResource: URI = null; - private hasToAutoReveal: boolean; private tree: Tree.ITree; private autoExpanded: Set; @@ -90,9 +89,6 @@ export class MarkersPanel extends Panel { dom.addClass(parent.getHTMLElement(), 'markers-panel'); - const conf = this.configurationService.getConfiguration(); - this.onConfigurationsUpdated(conf); - let container = dom.append(parent.getHTMLElement(), dom.$('.markers-panel-container')); this.createMessageBox(container); @@ -253,7 +249,6 @@ export class MarkersPanel extends Panel { } private createListeners(): void { - this.toUnbind.push(this.configurationService.onDidUpdateConfiguration(e => this.onConfigurationsUpdated(this.configurationService.getConfiguration()))); this.toUnbind.push(this.markerService.onMarkerChanged(this.onMarkerChanged, this)); this.toUnbind.push(this.editorGroupService.onEditorsChanged(this.onEditorsChanged, this)); this.toUnbind.push(this.tree.addListener('selection', () => this.onSelected())); @@ -289,10 +284,6 @@ export class MarkersPanel extends Panel { this.autoReveal(); } - private onConfigurationsUpdated(conf: IProblemsConfiguration): void { - this.hasToAutoReveal = conf && conf.problems && conf.problems.autoReveal; - } - private onSelected(): void { let selection = this.tree.getSelection(); if (selection && selection.length > 0) { @@ -338,8 +329,8 @@ export class MarkersPanel extends Panel { } private autoReveal(focus: boolean = false): void { - let conf = this.configurationService.getConfiguration(); - if (conf && conf.problems && conf.problems.autoReveal) { + let autoReveal = this.configurationService.getValue('problems.autoReveal'); + if (typeof autoReveal === 'boolean' && autoReveal) { this.revealMarkersForCurrentActiveEditor(focus); } } diff --git a/src/vs/workbench/parts/markers/browser/media/markers.css b/src/vs/workbench/parts/markers/browser/media/markers.css index 78b9a6132bd..91252ec8466 100644 --- a/src/vs/workbench/parts/markers/browser/media/markers.css +++ b/src/vs/workbench/parts/markers/browser/media/markers.css @@ -5,7 +5,7 @@ .monaco-action-bar .action-item.markers-panel-action-filter { max-width: 400px; - min-width: 100px; + min-width: 150px; flex: 1; cursor: default; margin: 4px 10px 0 0; diff --git a/src/vs/workbench/parts/markers/common/markersModel.ts b/src/vs/workbench/parts/markers/common/markersModel.ts index 561705c5a60..735d45e845b 100644 --- a/src/vs/workbench/parts/markers/common/markersModel.ts +++ b/src/vs/workbench/parts/markers/common/markersModel.ts @@ -58,11 +58,14 @@ export class Marker { } public toString(): string { - return [`file: '${this.marker.resource}'`, - `severity: '${Severity.toString(this.marker.severity)}'`, - `message: '${this.marker.message}'`, - `at: '${this.marker.startLineNumber},${this.marker.startColumn}'`, - `source: '${this.marker.source ? this.marker.source : ''}'`].join('\n'); + return [ + `file: '${this.marker.resource}'`, + `severity: '${Severity.toString(this.marker.severity)}'`, + `message: '${this.marker.message}'`, + `at: '${this.marker.startLineNumber},${this.marker.startColumn}'`, + `source: '${this.marker.source ? this.marker.source : ''}'`, + `code: '${this.marker.code ? this.marker.code : ''}'` + ].join('\n'); } } diff --git a/src/vs/workbench/parts/markers/markers.contribution.ts b/src/vs/workbench/parts/markers/markers.contribution.ts index 4ab845211d8..b7b91dbc852 100644 --- a/src/vs/workbench/parts/markers/markers.contribution.ts +++ b/src/vs/workbench/parts/markers/markers.contribution.ts @@ -5,7 +5,7 @@ import { registerContributions } from 'vs/workbench/parts/markers/browser/markersWorkbenchContributions'; import { registerContributions as registerElectronContributions } from 'vs/workbench/parts/markers/electron-browser/markersElectronContributions'; -// import './browser/markersFileDecorations'; +import './browser/markersFileDecorations'; registerContributions(); registerElectronContributions(); diff --git a/src/vs/workbench/parts/markers/test/common/markersModel.test.ts b/src/vs/workbench/parts/markers/test/common/markersModel.test.ts index 450eb2d2ff9..eacdf1fcc13 100644 --- a/src/vs/workbench/parts/markers/test/common/markersModel.test.ts +++ b/src/vs/workbench/parts/markers/test/common/markersModel.test.ts @@ -121,10 +121,12 @@ suite('MarkersModel Test', () => { }); test('toString()', function () { - assert.equal(`file: 'file:///a/res1'\nseverity: 'Error'\nmessage: 'some message'\nat: '10,5'\nsource: 'tslint'`, new Marker('', aMarker('a/res1')).toString()); - assert.equal(`file: 'file:///a/res2'\nseverity: 'Warning'\nmessage: 'some message'\nat: '10,5'\nsource: 'tslint'`, new Marker('', aMarker('a/res2', Severity.Warning)).toString()); - assert.equal(`file: 'file:///a/res2'\nseverity: 'Info'\nmessage: 'Info'\nat: '1,2'\nsource: ''`, new Marker('', aMarker('a/res2', Severity.Info, 1, 2, 1, 8, 'Info', '')).toString()); - assert.equal(`file: 'file:///a/res2'\nseverity: ''\nmessage: 'Ignore message'\nat: '1,2'\nsource: 'Ignore'`, new Marker('', aMarker('a/res2', Severity.Ignore, 1, 2, 1, 8, 'Ignore message', 'Ignore')).toString()); + const res1Marker = aMarker('a/res1'); + res1Marker.code = '1234'; + assert.equal(`file: 'file:///a/res1'\nseverity: 'Error'\nmessage: 'some message'\nat: '10,5'\nsource: 'tslint'\ncode: '1234'`, new Marker('', res1Marker).toString()); + assert.equal(`file: 'file:///a/res2'\nseverity: 'Warning'\nmessage: 'some message'\nat: '10,5'\nsource: 'tslint'\ncode: ''`, new Marker('', aMarker('a/res2', Severity.Warning)).toString()); + assert.equal(`file: 'file:///a/res2'\nseverity: 'Info'\nmessage: 'Info'\nat: '1,2'\nsource: ''\ncode: ''`, new Marker('', aMarker('a/res2', Severity.Info, 1, 2, 1, 8, 'Info', '')).toString()); + assert.equal(`file: 'file:///a/res2'\nseverity: ''\nmessage: 'Ignore message'\nat: '1,2'\nsource: 'Ignore'\ncode: ''`, new Marker('', aMarker('a/res2', Severity.Ignore, 1, 2, 1, 8, 'Ignore message', 'Ignore')).toString()); }); function hasMarker(markers: Marker[], marker: IMarker): boolean { diff --git a/src/vs/workbench/parts/preferences/browser/preferences.contribution.ts b/src/vs/workbench/parts/preferences/browser/preferences.contribution.ts index a2007eb7622..d24ed6250c3 100644 --- a/src/vs/workbench/parts/preferences/browser/preferences.contribution.ts +++ b/src/vs/workbench/parts/preferences/browser/preferences.contribution.ts @@ -24,7 +24,7 @@ import { import { PreferencesService } from 'vs/workbench/parts/preferences/browser/preferencesService'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; -import { PreferencesContentProvider } from 'vs/workbench/parts/preferences/common/preferencesContentProvider'; +import { PreferencesContribution } from 'vs/workbench/parts/preferences/common/preferencesContribution'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; @@ -257,7 +257,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ } }); -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(PreferencesContentProvider); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(PreferencesContribution); CommandsRegistry.registerCommand(OPEN_FOLDER_SETTINGS_COMMAND, function (accessor: ServicesAccessor, args?: IWorkspaceFolder) { const preferencesService = accessor.get(IPreferencesService); diff --git a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts index 411b295653f..a20434dd807 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts @@ -39,7 +39,6 @@ import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; -import { ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { VSash } from 'vs/base/browser/ui/sash/sash'; import { Widget } from 'vs/base/browser/ui/widget'; @@ -50,7 +49,7 @@ import { getCodeEditor } from 'vs/editor/common/services/codeEditorService'; import { IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/browser/editor'; import { FoldingController } from 'vs/editor/contrib/folding/browser/folding'; import { FindController } from 'vs/editor/contrib/find/browser/find'; -import { SelectionHighlighter } from 'vs/editor/contrib/find/common/findController'; +import { SelectionHighlighter } from 'vs/editor/contrib/multicursor/common/multicursor'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { attachStylerCallback } from 'vs/platform/theme/common/styler'; @@ -58,6 +57,8 @@ import { scrollbarShadow } from 'vs/platform/theme/common/colorRegistry'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import Event, { Emitter } from 'vs/base/common/event'; import { Registry } from 'vs/platform/registry/common/platform'; +import { MessageController } from 'vs/editor/contrib/message/messageController'; +import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; export class PreferencesEditorInput extends SideBySideEditorInput { public static ID: string = 'workbench.editorinputs.preferencesEditorInput'; @@ -247,7 +248,7 @@ export class PreferencesEditor extends BaseEditor { } if (this.workspaceContextService.getWorkspaceFolder(resource)) { - return ConfigurationTarget.FOLDER; + return ConfigurationTarget.WORKSPACE_FOLDER; } return null; @@ -722,7 +723,19 @@ export class DefaultPreferencesEditor extends BaseTextEditor { } public createEditorControl(parent: Builder, configuration: IEditorOptions): editorCommon.IEditor { - return this.instantiationService.createInstance(DefaultPreferencesCodeEditor, parent.getHTMLElement(), configuration); + const editor = this.instantiationService.createInstance(DefaultPreferencesCodeEditor, parent.getHTMLElement(), configuration); + + // Inform user about editor being readonly if user starts type + this.toUnbind.push(editor.onDidType(() => this.onDidType(editor))); + + return editor; + } + + private onDidType(editor: editorCommon.ICommonCodeEditor): void { + const messageController = MessageController.get(editor); + if (!messageController.isVisible()) { + messageController.showMessage(nls.localize('defaultEditorReadonly', "Edit in the right hand side editor to override defaults."), editor.getSelection().getPosition()); + } } protected getConfigurationOverrides(): IEditorOptions { @@ -905,7 +918,7 @@ class SettingsEditorContribution extends AbstractSettingsEditorContribution impl return this.instantiationService.createInstance(UserSettingsRenderer, this.editor, settingsModel, defaultSettingsModel); case ConfigurationTarget.WORKSPACE: return this.instantiationService.createInstance(WorkspaceSettingsRenderer, this.editor, settingsModel, defaultSettingsModel); - case ConfigurationTarget.FOLDER: + case ConfigurationTarget.WORKSPACE_FOLDER: return this.instantiationService.createInstance(FolderSettingsRenderer, this.editor, settingsModel, defaultSettingsModel); } } diff --git a/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts b/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts index d74dd56da46..08edba32488 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts @@ -22,9 +22,7 @@ import { IContextMenuService, ContextSubMenu } from 'vs/platform/contextview/bro import { SettingsGroupTitleWidget, EditPreferenceWidget, SettingsHeaderWidget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { RangeHighlightDecorations } from 'vs/workbench/common/editor/rangeDecorations'; -import { IConfigurationEditingService, ConfigurationEditingError, ConfigurationEditingErrorCode, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; -import { overrideIdentifierFromKey } from 'vs/platform/configuration/common/model'; import { IMarkerService, IMarkerData } from 'vs/platform/markers/common/markers'; import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { IMessageService, Severity } from 'vs/platform/message/common/message'; @@ -33,6 +31,7 @@ import { ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorE import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { MarkdownString } from 'vs/base/common/htmlContent'; +import { overrideIdentifierFromKey, IConfigurationService, ConfigurationTarget, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; export interface IPreferencesRenderer extends IDisposable { preferencesModel: IPreferencesEditorModel; @@ -72,7 +71,7 @@ export class UserSettingsRenderer extends Disposable implements IPreferencesRend @IPreferencesService protected preferencesService: IPreferencesService, @ITelemetryService private telemetryService: ITelemetryService, @ITextFileService private textFileService: ITextFileService, - @IConfigurationEditingService private configurationEditingService: IConfigurationEditingService, + @IConfigurationService private configurationService: IConfigurationService, @IMessageService private messageService: IMessageService, @IInstantiationService protected instantiationService: IInstantiationService ) { @@ -116,19 +115,8 @@ export class UserSettingsRenderer extends Disposable implements IPreferencesRend this.telemetryService.publicLog('defaultSettingsActions.copySetting', { userConfigurationKeys: [key] }); const overrideIdentifier = source.overrideOf ? overrideIdentifierFromKey(source.overrideOf.key) : null; const resource = this.preferencesModel.uri; - this.configurationEditingService.writeConfiguration(this.preferencesModel.configurationTarget, { key, value }, { donotSave: this.textFileService.isDirty(resource), donotNotifyError: true, scopes: { overrideIdentifier, resource } }) - .then(() => this.onSettingUpdated(source), error => { - this.messageService.show(Severity.Error, this.toErrorMessage(error, this.preferencesModel.configurationTarget)); - }); - } - - private toErrorMessage(error: ConfigurationEditingError, target: ConfigurationTarget): string { - switch (error.code) { - case ConfigurationEditingErrorCode.ERROR_INVALID_CONFIGURATION: { - return nls.localize('errorInvalidConfiguration', "Unable to write into settings. Correct errors/warnings in the file and try again."); - }; - } - return error.message; + this.configurationService.updateValue(key, value, { overrideIdentifier, resource }, this.preferencesModel.configurationTarget) + .then(() => this.onSettingUpdated(source)); } private onModelChanged(): void { @@ -192,11 +180,11 @@ export class WorkspaceSettingsRenderer extends UserSettingsRenderer implements I @IPreferencesService preferencesService: IPreferencesService, @ITelemetryService telemetryService: ITelemetryService, @ITextFileService textFileService: ITextFileService, - @IConfigurationEditingService configurationEditingService: IConfigurationEditingService, + @IConfigurationService configurationService: IConfigurationService, @IMessageService messageService: IMessageService, @IInstantiationService instantiationService: IInstantiationService ) { - super(editor, preferencesModel, associatedPreferencesModel, preferencesService, telemetryService, textFileService, configurationEditingService, messageService, instantiationService); + super(editor, preferencesModel, associatedPreferencesModel, preferencesService, telemetryService, textFileService, configurationService, messageService, instantiationService); this.untrustedSettingRenderer = this._register(instantiationService.createInstance(UnsupportedWorkspaceSettingsRenderer, editor, preferencesModel)); this.workspaceConfigurationRenderer = this._register(instantiationService.createInstance(WorkspaceConfigurationRenderer, editor, preferencesModel)); } @@ -220,11 +208,11 @@ export class FolderSettingsRenderer extends UserSettingsRenderer implements IPre @IPreferencesService preferencesService: IPreferencesService, @ITelemetryService telemetryService: ITelemetryService, @ITextFileService textFileService: ITextFileService, - @IConfigurationEditingService configurationEditingService: IConfigurationEditingService, + @IConfigurationService configurationService: IConfigurationService, @IMessageService messageService: IMessageService, @IInstantiationService instantiationService: IInstantiationService ) { - super(editor, preferencesModel, associatedPreferencesModel, preferencesService, telemetryService, textFileService, configurationEditingService, messageService, instantiationService); + super(editor, preferencesModel, associatedPreferencesModel, preferencesService, telemetryService, textFileService, configurationService, messageService, instantiationService); this.unsupportedWorkbenchSettingsRenderer = this._register(instantiationService.createInstance(UnsupportedWorkbenchSettingsRenderer, editor, preferencesModel)); } @@ -413,7 +401,7 @@ class DefaultSettingsHeaderRenderer extends Disposable { public render(settingsGroups: ISettingsGroup[]) { if (settingsGroups.length) { - this.settingsHeaderWidget.setMessage(''); + this.settingsHeaderWidget.setMessage(nls.localize('defaultSettings', "Place your settings in the right hand side editor to override.")); } else { this.settingsHeaderWidget.setMessage(nls.localize('noSettingsFound', "No Settings Found.")); } @@ -563,6 +551,7 @@ export class FilteredMatchesRenderer extends Disposable implements HiddenAreasPr stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, className: 'findMatch' } + }; } @@ -808,7 +797,7 @@ class EditSettingRenderer extends Disposable { return true; } if (configurationNode.type === 'boolean' || configurationNode.enum) { - if ((this.masterSettingsModel).configurationTarget !== ConfigurationTarget.FOLDER) { + if ((this.masterSettingsModel).configurationTarget !== ConfigurationTarget.WORKSPACE_FOLDER) { return true; } if (configurationNode.scope === ConfigurationScope.RESOURCE) { @@ -961,7 +950,7 @@ class UnsupportedWorkspaceSettingsRenderer extends Disposable { @IMarkerService private markerService: IMarkerService ) { super(); - this._register(this.configurationService.onDidUpdateConfiguration(() => this.render())); + this._register(this.configurationService.onDidChangeConfiguration(e => this.onDidConfigurationChange(e))); } private getMarkerMessage(settingKey: string): string { @@ -998,6 +987,12 @@ class UnsupportedWorkspaceSettingsRenderer extends Disposable { } } + private onDidConfigurationChange(event: IConfigurationChangeEvent): void { + if (event.source === ConfigurationTarget.DEFAULT || event.source === ConfigurationTarget.WORKSPACE || event.source === ConfigurationTarget.WORKSPACE_FOLDER) { + this.render(); + } + } + public dispose(): void { this.markerService.remove('preferencesEditor', [this.workspaceSettingsEditorModel.uri]); super.dispose(); diff --git a/src/vs/workbench/parts/preferences/browser/preferencesService.ts b/src/vs/workbench/parts/preferences/browser/preferencesService.ts index 5e5597721c6..fdf08f805c2 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesService.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesService.ts @@ -8,7 +8,6 @@ import * as network from 'vs/base/common/network'; import { TPromise } from 'vs/base/common/winjs.base'; import * as nls from 'vs/nls'; import URI from 'vs/base/common/uri'; -import * as paths from 'vs/base/common/paths'; import { ResourceMap } from 'vs/base/common/map'; import * as labels from 'vs/base/common/labels'; import * as strings from 'vs/base/common/strings'; @@ -18,7 +17,7 @@ import { EditorInput } from 'vs/workbench/common/editor'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; -import { Position as EditorPosition, IEditor } from 'vs/platform/editor/common/editor'; +import { Position as EditorPosition, IEditor, IEditorOptions } from 'vs/platform/editor/common/editor'; import { ICommonCodeEditor } from 'vs/editor/common/editorCommon'; import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; import { IStorageService } from 'vs/platform/storage/common/storage'; @@ -27,8 +26,7 @@ import { IMessageService, Severity, IChoiceService } from 'vs/platform/message/c import { IExtensionService } from 'vs/platform/extensions/common/extensions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; -import { IPreferencesService, IPreferencesEditorModel, ISetting, getSettingsTargetName } from 'vs/workbench/parts/preferences/common/preferences'; +import { IPreferencesService, IPreferencesEditorModel, ISetting, getSettingsTargetName, FOLDER_SETTINGS_PATH, DEFAULT_SETTINGS_EDITOR_SETTING } from 'vs/workbench/parts/preferences/common/preferences'; import { SettingsEditorModel, DefaultSettingsEditorModel, DefaultKeybindingsEditorModel, defaultKeybindingsContents, WorkspaceConfigModel } from 'vs/workbench/parts/preferences/common/preferencesModels'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { DefaultPreferencesEditorInput, PreferencesEditorInput } from 'vs/workbench/parts/preferences/browser/preferencesEditor'; @@ -41,15 +39,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing'; import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; - - -interface IWorkbenchSettingsConfiguration { - workbench: { - settings: { - openDefaultSettings: boolean; - } - }; -} +import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; const emptyEditableSettingsContent = '{\n}'; @@ -76,7 +66,6 @@ export class PreferencesService extends Disposable implements IPreferencesServic @IEnvironmentService private environmentService: IEnvironmentService, @ITelemetryService private telemetryService: ITelemetryService, @ITextModelService private textModelResolverService: ITextModelService, - @IConfigurationEditingService private configurationEditingService: IConfigurationEditingService, @IExtensionService private extensionService: IExtensionService, @IKeybindingService keybindingService: IKeybindingService, @IModelService private modelService: IModelService, @@ -117,7 +106,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic } getFolderSettingsResource(resource: URI): URI { - return this.getEditableSettingsURI(ConfigurationTarget.FOLDER, resource); + return this.getEditableSettingsURI(ConfigurationTarget.WORKSPACE_FOLDER, resource); } resolveContent(uri: URI): TPromise { @@ -180,26 +169,26 @@ export class PreferencesService extends Disposable implements IPreferencesServic } if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) { - return this.createEditableSettingsEditorModel(ConfigurationTarget.FOLDER, uri); + return this.createEditableSettingsEditorModel(ConfigurationTarget.WORKSPACE_FOLDER, uri); } return TPromise.wrap>(null); } - openGlobalSettings(): TPromise { - return this.doOpenSettings(ConfigurationTarget.USER, this.userSettingsResource); + openGlobalSettings(options?: IEditorOptions, position?: EditorPosition): TPromise { + return this.doOpenSettings(ConfigurationTarget.USER, this.userSettingsResource, options, position); } - openWorkspaceSettings(): TPromise { + openWorkspaceSettings(options?: IEditorOptions, position?: EditorPosition): TPromise { if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) { this.messageService.show(Severity.Info, nls.localize('openFolderFirst', "Open a folder first to create workspace settings")); return TPromise.as(null); } - return this.doOpenSettings(ConfigurationTarget.WORKSPACE, this.workspaceSettingsResource); + return this.doOpenSettings(ConfigurationTarget.WORKSPACE, this.workspaceSettingsResource, options, position); } - openFolderSettings(folder: URI): TPromise { - return this.doOpenSettings(ConfigurationTarget.FOLDER, this.getEditableSettingsURI(ConfigurationTarget.FOLDER, folder)); + openFolderSettings(folder: URI, options?: IEditorOptions, position?: EditorPosition): TPromise { + return this.doOpenSettings(ConfigurationTarget.WORKSPACE_FOLDER, this.getEditableSettingsURI(ConfigurationTarget.WORKSPACE_FOLDER, folder), options, position); } switchSettings(target: ConfigurationTarget, resource: URI): TPromise { @@ -259,22 +248,28 @@ export class PreferencesService extends Disposable implements IPreferencesServic }); } - private doOpenSettings(configurationTarget: ConfigurationTarget, resource: URI): TPromise { - const openDefaultSettings = !!this.configurationService.getConfiguration().workbench.settings.openDefaultSettings; + private doOpenSettings(configurationTarget: ConfigurationTarget, resource: URI, options?: IEditorOptions, position?: EditorPosition): TPromise { + const openDefaultSettings = !!this.configurationService.getValue(DEFAULT_SETTINGS_EDITOR_SETTING); return this.getOrCreateEditableSettingsEditorInput(configurationTarget, resource) .then(editableSettingsEditorInput => { + if (!options) { + options = { pinned: true }; + } else { + options.pinned = true; + } + if (openDefaultSettings) { const defaultPreferencesEditorInput = this.instantiationService.createInstance(DefaultPreferencesEditorInput, this.getDefaultSettingsResource(configurationTarget)); const preferencesEditorInput = new PreferencesEditorInput(this.getPreferencesEditorInputName(configurationTarget, resource), editableSettingsEditorInput.getDescription(), defaultPreferencesEditorInput, editableSettingsEditorInput); this.lastOpenedSettingsInput = preferencesEditorInput; - return this.editorService.openEditor(preferencesEditorInput, { pinned: true }); + return this.editorService.openEditor(preferencesEditorInput, options, position); } - return this.editorService.openEditor(editableSettingsEditorInput, { pinned: true }); + return this.editorService.openEditor(editableSettingsEditorInput, options, position); }); } private getDefaultSettingsResource(configurationTarget: ConfigurationTarget): URI { - if (configurationTarget === ConfigurationTarget.FOLDER) { + if (configurationTarget === ConfigurationTarget.WORKSPACE_FOLDER) { return this.defaultResourceSettingsResource; } return this.defaultSettingsResource; @@ -282,7 +277,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic private getPreferencesEditorInputName(target: ConfigurationTarget, resource: URI): string { const name = getSettingsTargetName(target, resource, this.contextService); - return target === ConfigurationTarget.FOLDER ? nls.localize('folderSettingsName', "{0} (Folder Settings)", name) : name; + return target === ConfigurationTarget.WORKSPACE_FOLDER ? nls.localize('folderSettingsName', "{0} (Folder Settings)", name) : name; } private getOrCreateEditableSettingsEditorInput(target: ConfigurationTarget, resource: URI): TPromise { @@ -325,10 +320,10 @@ export class PreferencesService extends Disposable implements IPreferencesServic return null; } const workspace = this.contextService.getWorkspace(); - return workspace.configuration || workspace.folders[0].toResource(paths.join('.vscode', 'settings.json')); - case ConfigurationTarget.FOLDER: + return workspace.configuration || workspace.folders[0].toResource(FOLDER_SETTINGS_PATH); + case ConfigurationTarget.WORKSPACE_FOLDER: const folder = this.contextService.getWorkspaceFolder(resource); - return folder ? folder.toResource(paths.join('.vscode', 'settings.json')) : null; + return folder ? folder.toResource(FOLDER_SETTINGS_PATH) : null; } return null; } @@ -393,7 +388,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic } return { lineNumber: setting.valueRange.startLineNumber, column: setting.valueRange.startColumn + 1 }; } - return this.configurationEditingService.writeConfiguration(ConfigurationTarget.USER, { key: languageKey, value: {} }, { donotSave: true }) + return this.configurationService.updateValue(languageKey, {}, ConfigurationTarget.USER) .then(() => { setting = settingsModel.getPreference(languageKey); let content = eol + this.spaces(2, configuration) + eol + this.spaces(1, configuration); diff --git a/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts b/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts index f2df5ec870c..0eeeadfeb47 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts @@ -32,9 +32,10 @@ import { ISelectBoxStyles, defaultStyles } from 'vs/base/browser/ui/selectBox/se import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { Color } from 'vs/base/common/color'; import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; -import { ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; import { IMouseEvent } from 'vs/base/browser/mouseEvent'; import { MarkdownString } from 'vs/base/common/htmlContent'; +import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; +import { IMarginData } from 'vs/editor/browser/controller/mouseTarget'; export class SettingsHeaderWidget extends Widget implements IViewZone { @@ -313,7 +314,7 @@ export class SettingsTargetsWidget extends Widget { private updateLabel(): void { this.targetLabel.textContent = getSettingsTargetName(this._configuartionTarget, this._uri, this.workspaceContextService); - const details = ConfigurationTarget.FOLDER === this._configuartionTarget ? localize('folderSettingsDetails', "Folder Settings") : ''; + const details = ConfigurationTarget.WORKSPACE_FOLDER === this._configuartionTarget ? localize('folderSettingsDetails', "Folder Settings") : ''; this.targetDetails.textContent = details; DOM.toggleClass(this.targetDetails, 'empty', !details); } @@ -358,7 +359,7 @@ export class SettingsTargetsWidget extends Widget { actions.push(...workspaceFolders.map((folder, index) => { return { id: 'folderSettingsTarget' + index, - label: getSettingsTargetName(ConfigurationTarget.FOLDER, folder.uri, this.workspaceContextService), + label: getSettingsTargetName(ConfigurationTarget.WORKSPACE_FOLDER, folder.uri, this.workspaceContextService), checked: this._uri.toString() === folder.uri.toString(), enabled: true, run: () => this.onTargetClicked(folder.uri) @@ -585,7 +586,8 @@ export class EditPreferenceWidget extends Disposable { super(); this._editPreferenceDecoration = []; this._register(this.editor.onMouseDown((e: IEditorMouseEvent) => { - if (e.target.type !== MouseTargetType.GUTTER_GLYPH_MARGIN || /* after last line */ e.target.detail || !this.isVisible()) { + const data = e.target.detail as IMarginData; + if (e.target.type !== MouseTargetType.GUTTER_GLYPH_MARGIN || data.isAfterLines || !this.isVisible()) { return; } this._onClick.fire(e); diff --git a/src/vs/workbench/parts/preferences/common/preferences.ts b/src/vs/workbench/parts/preferences/common/preferences.ts index 1b7200927c9..4bd6f7acdef 100644 --- a/src/vs/workbench/parts/preferences/common/preferences.ts +++ b/src/vs/workbench/parts/preferences/common/preferences.ts @@ -8,11 +8,12 @@ import URI from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { IEditor } from 'vs/platform/editor/common/editor'; +import { IEditor, Position, IEditorOptions } from 'vs/platform/editor/common/editor'; import { IKeybindingItemEntry } from 'vs/workbench/parts/preferences/common/keybindingsEditorModel'; import { IRange } from 'vs/editor/common/core/range'; -import { ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { join } from 'vs/base/common/paths'; +import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; export interface ISettingsGroup { id: string; @@ -76,9 +77,9 @@ export interface IPreferencesService { resolveContent(uri: URI): TPromise; createPreferencesEditorModel(uri: URI): TPromise>; - openGlobalSettings(): TPromise; - openWorkspaceSettings(): TPromise; - openFolderSettings(folder: URI): TPromise; + openGlobalSettings(options?: IEditorOptions, position?: Position): TPromise; + openWorkspaceSettings(options?: IEditorOptions, position?: Position): TPromise; + openFolderSettings(folder: URI, options?: IEditorOptions, position?: Position): TPromise; switchSettings(target: ConfigurationTarget, resource: URI): TPromise; openGlobalKeybindingSettings(textual: boolean): TPromise; @@ -106,10 +107,11 @@ export function getSettingsTargetName(target: ConfigurationTarget, resource: URI return localize('userSettingsTarget', "User Settings"); case ConfigurationTarget.WORKSPACE: return localize('workspaceSettingsTarget', "Workspace Settings"); - case ConfigurationTarget.FOLDER: + case ConfigurationTarget.WORKSPACE_FOLDER: const folder = workspaceContextService.getWorkspaceFolder(resource); return folder ? folder.name : ''; } + return ''; } export const CONTEXT_SETTINGS_EDITOR = new RawContextKey('inSettingsEditor', false); @@ -130,4 +132,7 @@ export const KEYBINDINGS_EDITOR_COMMAND_REMOVE = 'keybindings.editor.removeKeybi export const KEYBINDINGS_EDITOR_COMMAND_RESET = 'keybindings.editor.resetKeybinding'; export const KEYBINDINGS_EDITOR_COMMAND_COPY = 'keybindings.editor.copyKeybindingEntry'; export const KEYBINDINGS_EDITOR_COMMAND_SHOW_CONFLICTS = 'keybindings.editor.showConflicts'; -export const KEYBINDINGS_EDITOR_COMMAND_FOCUS_KEYBINDINGS = 'keybindings.editor.focusKeybindings'; \ No newline at end of file +export const KEYBINDINGS_EDITOR_COMMAND_FOCUS_KEYBINDINGS = 'keybindings.editor.focusKeybindings'; + +export const FOLDER_SETTINGS_PATH = join('.vscode', 'settings.json'); +export const DEFAULT_SETTINGS_EDITOR_SETTING = 'workbench.settings.openDefaultSettings'; \ No newline at end of file diff --git a/src/vs/workbench/parts/preferences/common/preferencesContentProvider.ts b/src/vs/workbench/parts/preferences/common/preferencesContentProvider.ts deleted file mode 100644 index 33141c96052..00000000000 --- a/src/vs/workbench/parts/preferences/common/preferencesContentProvider.ts +++ /dev/null @@ -1,82 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * 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 { IModelService } from 'vs/editor/common/services/modelService'; -import { IModeService } from 'vs/editor/common/services/modeService'; -import URI from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; -import { IModel } from 'vs/editor/common/editorCommon'; -import JSONContributionRegistry = require('vs/platform/jsonschemas/common/jsonContributionRegistry'); -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); - -export class PreferencesContentProvider implements IWorkbenchContribution { - - constructor( - @IModelService private modelService: IModelService, - @ITextModelService private textModelResolverService: ITextModelService, - @IPreferencesService private preferencesService: IPreferencesService, - @IModeService private modeService: IModeService - ) { - this.start(); - } - - public getId(): string { - return 'vs.contentprovider'; - } - - private start(): void { - - this.textModelResolverService.registerTextModelContentProvider('vscode', { - provideTextContent: (uri: URI): TPromise => { - if (uri.scheme !== 'vscode') { - return null; - } - if (uri.authority === 'schemas') { - const schemaModel = this.getSchemaModel(uri); - if (schemaModel) { - return TPromise.as(schemaModel); - } - } - return this.preferencesService.resolveContent(uri) - .then(content => { - if (content !== null && content !== void 0) { - let mode = this.modeService.getOrCreateMode('json'); - const model = this.modelService.createModel(content, mode, uri); - return TPromise.as(model); - } - return null; - }); - } - }); - } - - 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/preferences/common/preferencesContribution.ts b/src/vs/workbench/parts/preferences/common/preferencesContribution.ts new file mode 100644 index 00000000000..665bb232104 --- /dev/null +++ b/src/vs/workbench/parts/preferences/common/preferencesContribution.ts @@ -0,0 +1,161 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { IModelService } from 'vs/editor/common/services/modelService'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import URI from 'vs/base/common/uri'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IModel } from 'vs/editor/common/editorCommon'; +import JSONContributionRegistry = require('vs/platform/jsonschemas/common/jsonContributionRegistry'); +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, FOLDER_SETTINGS_PATH, DEFAULT_SETTINGS_EDITOR_SETTING } from 'vs/workbench/parts/preferences/common/preferences'; +import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; +import { endsWith } from 'vs/base/common/strings'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IEditorOpeningEvent } from 'vs/workbench/common/editor'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; + +const schemaRegistry = Registry.as(JSONContributionRegistry.Extensions.JSONContribution); + +export class PreferencesContribution implements IWorkbenchContribution { + private editorOpeningListener: IDisposable; + private settingsListener: IDisposable; + + constructor( + @IModelService private modelService: IModelService, + @ITextModelService private textModelResolverService: ITextModelService, + @IPreferencesService private preferencesService: IPreferencesService, + @IModeService private modeService: IModeService, + @IEditorGroupService private editorGroupService: IEditorGroupService, + @IEnvironmentService private environmentService: IEnvironmentService, + @IWorkspaceContextService private workspaceService: IWorkspaceContextService, + @IConfigurationService private configurationService: IConfigurationService + ) { + this.settingsListener = this.configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration(DEFAULT_SETTINGS_EDITOR_SETTING)) { + this.handleSettingsEditorOverride(); + } + }); + this.handleSettingsEditorOverride(); + + this.start(); + } + + private handleSettingsEditorOverride(): void { + + // dispose any old listener we had + this.editorOpeningListener = dispose(this.editorOpeningListener); + + // install editor opening listener unless user has disabled this + if (!!this.configurationService.getValue(DEFAULT_SETTINGS_EDITOR_SETTING)) { + this.editorOpeningListener = this.editorGroupService.onEditorOpening(e => this.onEditorOpening(e)); + } + } + + private onEditorOpening(event: IEditorOpeningEvent): void { + const resource = event.input.getResource(); + if ( + !resource || resource.scheme !== 'file' || // require a file path opening + !endsWith(resource.fsPath, 'settings.json') || // file must end in settings.json + !this.configurationService.getValue(DEFAULT_SETTINGS_EDITOR_SETTING) // user has not disabled default settings editor + ) { + return; + } + + // If the file resource was already opened before in the group, do not prevent + // the opening of that resource. Otherwise we would have the same settings + // opened twice (https://github.com/Microsoft/vscode/issues/36447) + const stacks = this.editorGroupService.getStacksModel(); + const group = stacks.groupAt(event.position); + if (group && group.contains(event.input)) { + return; + } + + // Global User Settings File + if (resource.fsPath === this.environmentService.appSettingsPath) { + return event.prevent(() => this.preferencesService.openGlobalSettings(event.options, event.position)); + } + + // Single Folder Workspace Settings File + const state = this.workspaceService.getWorkbenchState(); + if (state === WorkbenchState.FOLDER) { + const folders = this.workspaceService.getWorkspace().folders; + if (resource.fsPath === folders[0].toResource(FOLDER_SETTINGS_PATH).fsPath) { + return event.prevent(() => this.preferencesService.openWorkspaceSettings(event.options, event.position)); + } + } + + // Multi Folder Workspace Settings File + else if (state === WorkbenchState.WORKSPACE) { + const folders = this.workspaceService.getWorkspace().folders; + for (let i = 0; i < folders.length; i++) { + if (resource.fsPath === folders[i].toResource(FOLDER_SETTINGS_PATH).fsPath) { + return event.prevent(() => this.preferencesService.openFolderSettings(folders[i].uri, event.options, event.position)); + } + } + } + } + + public getId(): string { + return 'vs.contentprovider'; + } + + private start(): void { + + this.textModelResolverService.registerTextModelContentProvider('vscode', { + provideTextContent: (uri: URI): TPromise => { + if (uri.scheme !== 'vscode') { + return null; + } + if (uri.authority === 'schemas') { + const schemaModel = this.getSchemaModel(uri); + if (schemaModel) { + return TPromise.as(schemaModel); + } + } + return this.preferencesService.resolveContent(uri) + .then(content => { + if (content !== null && content !== void 0) { + let mode = this.modeService.getOrCreateMode('json'); + const model = this.modelService.createModel(content, mode, uri); + return TPromise.as(model); + } + return null; + }); + } + }); + } + + 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; + } + + public dispose(): void { + this.editorOpeningListener = dispose(this.editorOpeningListener); + this.settingsListener = dispose(this.settingsListener); + } +} diff --git a/src/vs/workbench/parts/preferences/common/preferencesModels.ts b/src/vs/workbench/parts/preferences/common/preferencesModels.ts index f17231a589b..1233bd69199 100644 --- a/src/vs/workbench/parts/preferences/common/preferencesModels.ts +++ b/src/vs/workbench/parts/preferences/common/preferencesModels.ts @@ -18,7 +18,6 @@ import { EditorModel } from 'vs/workbench/common/editor'; import { IConfigurationNode, IConfigurationRegistry, Extensions, OVERRIDE_PROPERTY_PATTERN, IConfigurationPropertySchema, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; import { ISettingsEditorModel, IKeybindingsEditorModel, ISettingsGroup, ISetting, IFilterResult, ISettingsSection } from 'vs/workbench/parts/preferences/common/preferences'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; import { IMatch, or, matchesContiguousSubString, matchesPrefix, matchesCamelCase, matchesWords } from 'vs/base/common/filters'; import { ITextEditorModel, ITextModelService } from 'vs/editor/common/services/resolverService'; import { IRange } from 'vs/editor/common/core/range'; @@ -26,6 +25,7 @@ import { ITextFileService, StateChange } from 'vs/workbench/services/textfile/co import { TPromise } from 'vs/base/common/winjs.base'; import { Queue } from 'vs/base/common/async'; import { IFileService } from 'vs/platform/files/common/files'; +import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; class SettingMatches { diff --git a/src/vs/workbench/parts/quickopen/browser/commandsHandler.ts b/src/vs/workbench/parts/quickopen/browser/commandsHandler.ts index f5e99434096..8b5d93e1438 100644 --- a/src/vs/workbench/parts/quickopen/browser/commandsHandler.ts +++ b/src/vs/workbench/parts/quickopen/browser/commandsHandler.ts @@ -95,7 +95,7 @@ class CommandsHistory { } private registerListeners(): void { - this.configurationService.onDidUpdateConfiguration(e => this.updateConfiguration()); + this.configurationService.onDidChangeConfiguration(e => this.updateConfiguration()); once(this.lifecycleService.onShutdown)(reason => this.save()); } @@ -404,7 +404,7 @@ export class CommandsHandler extends QuickOpenHandler { this.commandsHistory = this.instantiationService.createInstance(CommandsHistory); - this.configurationService.onDidUpdateConfiguration(e => this.updateConfiguration()); + this.configurationService.onDidChangeConfiguration(e => this.updateConfiguration()); this.updateConfiguration(); } diff --git a/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts b/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts index 854fbca08ae..988464d7332 100644 --- a/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts +++ b/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts @@ -58,7 +58,7 @@ export class SettingsChangeRelauncher implements IWorkbenchContribution { } private registerListeners(): void { - this.toDispose.push(this.configurationService.onDidUpdateConfiguration(e => this.onConfigurationChange(this.configurationService.getConfiguration(), true))); + this.toDispose.push(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationChange(this.configurationService.getConfiguration(), true))); this.toDispose.push(this.contextService.onDidChangeWorkbenchState(() => setTimeout(() => this.handleWorkbenchState()))); } diff --git a/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.ts b/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.ts index cdb0a5ab4a4..ec011aa71c6 100644 --- a/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.ts +++ b/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.ts @@ -50,6 +50,7 @@ import { MenuId, IMenuService, IMenu, MenuItemAction } from 'vs/platform/actions import { fillInActions, MenuItemActionItem } from 'vs/platform/actions/browser/menuItemActionItem'; import { IChange, ICommonCodeEditor, IEditorModel, ScrollType, IEditorContribution, OverviewRulerLane, IModel } from 'vs/editor/common/editorCommon'; import { sortedDiff, Splice } from 'vs/base/common/arrays'; +import { IMarginData } from 'vs/editor/browser/controller/mouseTarget'; // TODO@Joao // Need to subclass MenuItemActionItem in order to respect @@ -315,6 +316,10 @@ class DirtyDiffWidget extends PeekViewWidget { secondaryHeadingColor: theme.getColor(peekViewTitleInfoForeground) }); } + + protected revealLine(lineNumber: number) { + this.editor.revealLineInCenterIfOutsideViewport(lineNumber, ScrollType.Smooth); + } } @editorAction @@ -527,14 +532,6 @@ export class DirtyDiffController implements IEditorContribution { private onEditorMouseDown(e: IEditorMouseEvent): void { this.mouseDownInfo = null; - // if (!this.model) { - // return; - // } - - // if (this.model.changes.length === 0) { - // return; - // } - const range = e.target.range; if (!range) { @@ -549,6 +546,14 @@ export class DirtyDiffController implements IEditorContribution { return; } + const data = e.target.detail as IMarginData; + const gutterOffsetX = data.offsetX - data.glyphMarginWidth - data.lineNumbersWidth; + + // TODO@joao TODO@alex TODO@martin this is such that we don't collide with folding + if (gutterOffsetX > 12) { + return; + } + this.mouseDownInfo = { lineNumber: range.startLineNumber }; } diff --git a/src/vs/workbench/parts/scm/electron-browser/scm.contribution.ts b/src/vs/workbench/parts/scm/electron-browser/scm.contribution.ts index 9405fdb123c..b5465c83f69 100644 --- a/src/vs/workbench/parts/scm/electron-browser/scm.contribution.ts +++ b/src/vs/workbench/parts/scm/electron-browser/scm.contribution.ts @@ -17,9 +17,7 @@ import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { StatusUpdater, StatusBarController } from './scmActivity'; -import { FileDecorations } from './scmFileDecorations'; import { SCMViewlet } from 'vs/workbench/parts/scm/electron-browser/scmViewlet'; -import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry'; class OpenSCMViewletAction extends ToggleViewletAction { @@ -51,9 +49,6 @@ Registry.as(WorkbenchExtensions.Workbench) Registry.as(WorkbenchExtensions.Workbench) .registerWorkbenchContribution(StatusBarController); -Registry.as(WorkbenchExtensions.Workbench) - .registerWorkbenchContribution(FileDecorations); - // Register Action to Open Viewlet Registry.as(WorkbenchActionExtensions.WorkbenchActions).registerWorkbenchAction( new SyncActionDescriptor(OpenSCMViewletAction, VIEWLET_ID, localize('toggleSCMViewlet', "Show SCM"), { @@ -65,17 +60,3 @@ Registry.as(WorkbenchActionExtensions.WorkbenchActions 'View: Show SCM', localize('view', "View") ); - - -Registry.as(Extensions.Configuration).registerConfiguration({ - 'id': 'scm', - 'order': 101, - 'type': 'object', - 'properties': { - 'scm.fileDecorations.enabled': { - 'description': localize('scm.fileDecorations.enabled', "Show source control status on files and folders"), - 'type': 'boolean', - 'default': true - } - } -}); diff --git a/src/vs/workbench/parts/scm/electron-browser/scmActivity.ts b/src/vs/workbench/parts/scm/electron-browser/scmActivity.ts index f5900b648ef..2558e2a1717 100644 --- a/src/vs/workbench/parts/scm/electron-browser/scmActivity.ts +++ b/src/vs/workbench/parts/scm/electron-browser/scmActivity.ts @@ -11,7 +11,7 @@ import { IDisposable, dispose, empty as EmptyDisposable, combinedDisposable } fr import { filterEvent, any as anyEvent } from 'vs/base/common/event'; import { VIEWLET_ID } from 'vs/workbench/parts/scm/common/scm'; import { ISCMService, ISCMRepository } from 'vs/workbench/services/scm/common/scm'; -import { IActivityBarService, NumberBadge } from 'vs/workbench/services/activity/common/activityBarService'; +import { IActivityService, NumberBadge } from 'vs/workbench/services/activity/common/activity'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IStatusbarService, StatusbarAlignment as MainThreadStatusBarAlignment } from 'vs/platform/statusbar/common/statusbar'; @@ -25,7 +25,7 @@ export class StatusUpdater implements IWorkbenchContribution { constructor( @ISCMService private scmService: ISCMService, - @IActivityBarService private activityBarService: IActivityBarService + @IActivityService private activityService: IActivityService ) { this.scmService.onDidAddRepository(this.onDidAddRepository, this, this.disposables); this.render(); @@ -64,7 +64,7 @@ export class StatusUpdater implements IWorkbenchContribution { if (count > 0) { const badge = new NumberBadge(count, num => localize('scmPendingChangesBadge', '{0} pending changes', num)); - this.badgeDisposable = this.activityBarService.showActivity(VIEWLET_ID, badge, 'scm-viewlet-label'); + this.badgeDisposable = this.activityService.showActivity(VIEWLET_ID, badge, 'scm-viewlet-label'); } else { this.badgeDisposable = EmptyDisposable; } diff --git a/src/vs/workbench/parts/scm/electron-browser/scmFileDecorations.ts b/src/vs/workbench/parts/scm/electron-browser/scmFileDecorations.ts deleted file mode 100644 index dec7a16bb77..00000000000 --- a/src/vs/workbench/parts/scm/electron-browser/scmFileDecorations.ts +++ /dev/null @@ -1,142 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * 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 { IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { IResourceDecorationsService, IDecorationsProvider, IResourceDecorationData } from 'vs/workbench/services/decorations/browser/decorations'; -import { IDisposable, dispose, combinedDisposable } from 'vs/base/common/lifecycle'; -import { ISCMService, ISCMRepository, ISCMProvider, ISCMResource } from 'vs/workbench/services/scm/common/scm'; -import URI from 'vs/base/common/uri'; -import Severity from 'vs/base/common/severity'; -import Event, { Emitter } from 'vs/base/common/event'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { localize } from 'vs/nls'; -import { equals } from 'vs/base/common/objects'; - -class SCMDecorationsProvider implements IDecorationsProvider { - - private readonly _disposable: IDisposable; - private readonly _onDidChange = new Emitter(); - private _data = new Map(); - - readonly label: string; - readonly onDidChange: Event = this._onDidChange.event; - - constructor( - private readonly _provider: ISCMProvider, - private readonly _config: ISCMConfiguration - ) { - this.label = this._provider.label; - this._disposable = this._provider.onDidChangeResources(this._updateGroups, this); - this._updateGroups(); - } - - dispose(): void { - this._disposable.dispose(); - } - - private _updateGroups(): void { - const uris: URI[] = []; - const newData = new Map(); - for (const group of this._provider.resources) { - for (const resource of group.resourceCollection.resources) { - newData.set(resource.sourceUri.toString(), resource); - - if (!this._data.has(resource.sourceUri.toString())) { - uris.push(resource.sourceUri); // added - } - } - } - - this._data.forEach((value, key) => { - if (!newData.has(key)) { - uris.push(value.sourceUri); // removed - } - }); - - this._data = newData; - this._onDidChange.fire(uris); - } - - provideDecorations(uri: URI): IResourceDecorationData { - const resource = this._data.get(uri.toString()); - if (!resource) { - return undefined; - } - return { - severity: Severity.Info, - tooltip: localize('tooltip', "{0}, {1}", resource.decorations.tooltip, this._provider.label), - color: resource.decorations.color, - letter: resource.decorations.tooltip.charAt(0) - }; - } -} - -interface ISCMConfiguration { - fileDecorations: { - enabled: boolean; - }; -} - -export class FileDecorations implements IWorkbenchContribution { - - private _providers = new Map(); - private _configListener: IDisposable; - private _repoListeners: IDisposable[]; - private _currentConfig: ISCMConfiguration; - - constructor( - @IResourceDecorationsService private _decorationsService: IResourceDecorationsService, - @IConfigurationService private _configurationService: IConfigurationService, - @ISCMService private _scmService: ISCMService, - ) { - this._configListener = this._configurationService.onDidUpdateConfiguration(this._update, this); - this._update(); - } - - getId(): string { - throw new Error('smc.SCMFileDecorations'); - } - - dispose(): void { - this._providers.forEach(value => dispose(value)); - dispose(this._repoListeners); - dispose(this._configListener, this._configListener); - } - - private _update(): void { - const config = this._configurationService.getConfiguration('scm'); - if (!equals(config, this._currentConfig)) { - this._currentConfig = config; - - if (this._currentConfig.fileDecorations.enabled) { - this._scmService.repositories.forEach(this._onDidAddRepository, this); - this._repoListeners = [ - this._scmService.onDidAddRepository(this._onDidAddRepository, this), - this._scmService.onDidRemoveRepository(this._onDidRemoveRepository, this) - ]; - } else { - this._repoListeners = dispose(this._repoListeners); - this._providers.forEach(value => dispose(value)); - this._providers.clear(); - } - } - } - - private _onDidAddRepository(repo: ISCMRepository): void { - const provider = new SCMDecorationsProvider(repo.provider, this._configurationService.getConfiguration('scm')); - const registration = this._decorationsService.registerDecortionsProvider(provider); - this._providers.set(repo, combinedDisposable([registration, provider])); - } - - private _onDidRemoveRepository(repo: ISCMRepository): void { - let listener = this._providers.get(repo); - if (listener) { - this._providers.delete(repo); - listener.dispose(); - } - } -} diff --git a/src/vs/workbench/parts/search/browser/openAnythingHandler.ts b/src/vs/workbench/parts/search/browser/openAnythingHandler.ts index 69c76344f67..7e9faa28fd4 100644 --- a/src/vs/workbench/parts/search/browser/openAnythingHandler.ts +++ b/src/vs/workbench/parts/search/browser/openAnythingHandler.ts @@ -23,7 +23,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IWorkbenchSearchConfiguration } from 'vs/workbench/parts/search/common/search'; import { IRange } from 'vs/editor/common/core/range'; -import { compareItemsByScore, scoreItem, ScorerCache, massageSearchForScoring } from 'vs/base/parts/quickopen/common/quickOpenScorer'; +import { compareItemsByScore, scoreItem, ScorerCache, prepareQuery } from 'vs/base/parts/quickopen/common/quickOpenScorer'; export import OpenSymbolHandler = openSymbolHandler.OpenSymbolHandler; // OpenSymbolHandler is used from an extension and must be in the main bundle file so it can load @@ -148,7 +148,7 @@ export class OpenAnythingHandler extends QuickOpenHandler { } private registerListeners(): void { - this.configurationService.onDidUpdateConfiguration(e => this.updateHandlers(this.configurationService.getConfiguration())); + this.configurationService.onDidChangeConfiguration(e => this.updateHandlers(this.configurationService.getConfiguration())); } private updateHandlers(configuration: IWorkbenchSearchConfiguration): void { @@ -173,15 +173,16 @@ export class OpenAnythingHandler extends QuickOpenHandler { this.cancelPendingSearch(); this.isClosed = false; // Treat this call as the handler being in use - // Massage search for scoring - searchValue = massageSearchForScoring(searchValue); + // Prepare search for scoring + const query = prepareQuery(searchValue); - const searchWithRange = this.extractRange(searchValue); // Find a suitable range from the pattern looking for ":" and "#" + const searchWithRange = this.extractRange(query.value); // Find a suitable range from the pattern looking for ":" and "#" if (searchWithRange) { - searchValue = searchWithRange.search; // ignore range portion in query + query.value = searchWithRange.search; // ignore range portion in query + query.lowercase = query.value.toLowerCase(); } - if (!searchValue) { + if (!query.value) { return TPromise.as(new QuickOpenModel()); // Respond directly to empty search } @@ -190,12 +191,12 @@ export class OpenAnythingHandler extends QuickOpenHandler { const resultPromises: TPromise[] = []; // File Results - const filePromise = this.openFileHandler.getResults(searchValue, OpenAnythingHandler.MAX_DISPLAYED_RESULTS); + const filePromise = this.openFileHandler.getResults(query.value, OpenAnythingHandler.MAX_DISPLAYED_RESULTS); resultPromises.push(filePromise); // Symbol Results (unless disabled or a range or absolute path is specified) if (this.includeSymbols && !searchWithRange) { - resultPromises.push(this.openSymbolHandler.getResults(searchValue)); + resultPromises.push(this.openSymbolHandler.getResults(query.value)); } // Join and sort unified @@ -212,7 +213,7 @@ export class OpenAnythingHandler extends QuickOpenHandler { // Sort const unsortedResultTime = Date.now(); - const compare = (elementA: QuickOpenEntry, elementB: QuickOpenEntry) => compareItemsByScore(elementA, elementB, searchValue, true, QuickOpenItemAccessor, this.scorerCache); + const compare = (elementA: QuickOpenEntry, elementB: QuickOpenEntry) => compareItemsByScore(elementA, elementB, query, true, QuickOpenItemAccessor, this.scorerCache); const viewResults = arrays.top(mergedResults, compare, OpenAnythingHandler.MAX_DISPLAYED_RESULTS); const sortedResultTime = Date.now(); @@ -221,7 +222,7 @@ export class OpenAnythingHandler extends QuickOpenHandler { if (entry instanceof FileEntry) { entry.setRange(searchWithRange ? searchWithRange.range : null); - const itemScore = scoreItem(entry, searchValue, true, QuickOpenItemAccessor, this.scorerCache); + const itemScore = scoreItem(entry, query, true, QuickOpenItemAccessor, this.scorerCache); entry.setHighlights(itemScore.labelMatch, itemScore.descriptionMatch); } }); @@ -229,7 +230,7 @@ export class OpenAnythingHandler extends QuickOpenHandler { const duration = new Date().getTime() - startTime; filePromise.then(fileModel => { const data = this.createTimerEventData(startTime, { - searchLength: searchValue.length, + searchLength: query.value.length, unsortedResultTime, sortedResultTime, resultCount: mergedResults.length, diff --git a/src/vs/workbench/parts/search/browser/search.contribution.ts b/src/vs/workbench/parts/search/browser/search.contribution.ts index 5c489ff6e63..07f79b0238b 100644 --- a/src/vs/workbench/parts/search/browser/search.contribution.ts +++ b/src/vs/workbench/parts/search/browser/search.contribution.ts @@ -371,6 +371,11 @@ configurationRegistry.registerConfiguration({ 'type': 'boolean', 'description': nls.localize('search.quickOpen.includeSymbols', "Configure to include results from a global symbol search in the file results for Quick Open."), 'default': false + }, + 'search.followSymlinks': { + 'type': 'boolean', + 'description': nls.localize('search.followSymlinks', "Controls whether to follow symlinks while searching."), + 'default': true } } }); diff --git a/src/vs/workbench/parts/search/browser/searchActions.ts b/src/vs/workbench/parts/search/browser/searchActions.ts index 8c65a7a44b1..e50b636779e 100644 --- a/src/vs/workbench/parts/search/browser/searchActions.ts +++ b/src/vs/workbench/parts/search/browser/searchActions.ts @@ -507,7 +507,7 @@ export abstract class AbstractSearchAndReplaceAction extends Action { export class RemoveAction extends AbstractSearchAndReplaceAction { constructor(private viewer: ITree, private element: RenderableMatch) { - super('remove', nls.localize('RemoveAction.label', "Remove"), 'action-remove'); + super('remove', nls.localize('RemoveAction.label', "Dismiss"), 'action-remove'); } public run(): TPromise { diff --git a/src/vs/workbench/parts/search/common/queryBuilder.ts b/src/vs/workbench/parts/search/common/queryBuilder.ts index 9b3120dcdc6..65d70195724 100644 --- a/src/vs/workbench/parts/search/common/queryBuilder.ts +++ b/src/vs/workbench/parts/search/common/queryBuilder.ts @@ -56,10 +56,12 @@ export class QueryBuilder { } const useRipgrep = !folderResources || folderResources.every(folder => { - const folderConfig = this.configurationService.getConfiguration(undefined, { resource: folder }); + const folderConfig = this.configurationService.getConfiguration({ resource: folder }); return folderConfig.search.useRipgrep; }); + const ignoreSymlinks = !this.configurationService.getConfiguration().search.followSymlinks; + const query = { type, folderQueries, @@ -74,7 +76,8 @@ export class QueryBuilder { contentPattern: contentPattern, useRipgrep, disregardIgnoreFiles: options.disregardIgnoreFiles, - disregardExcludeSettings: options.disregardExcludeSettings + disregardExcludeSettings: options.disregardExcludeSettings, + ignoreSymlinks }; // Filter extraFileResources against global include/exclude patterns - they are already expected to not belong to a workspace @@ -244,7 +247,7 @@ export class QueryBuilder { private getFolderQueryForSearchPath(searchPath: ISearchPathPattern): IFolderQuery { const folder = searchPath.searchPath; - const folderConfig = this.configurationService.getConfiguration(undefined, { resource: folder }); + const folderConfig = this.configurationService.getConfiguration({ resource: folder }); return { folder, includePattern: searchPath.pattern && patternListToIExpression([searchPath.pattern]), @@ -253,7 +256,7 @@ export class QueryBuilder { } private getFolderQueryForRoot(folder: uri, options?: IQueryOptions): IFolderQuery { - const folderConfig = this.configurationService.getConfiguration(undefined, { resource: folder }); + const folderConfig = this.configurationService.getConfiguration({ resource: folder }); return { folder, excludePattern: this.getExcludesForFolder(folderConfig, options), diff --git a/src/vs/workbench/parts/search/common/search.ts b/src/vs/workbench/parts/search/common/search.ts index 6b074a33287..a0bbd0554b4 100644 --- a/src/vs/workbench/parts/search/common/search.ts +++ b/src/vs/workbench/parts/search/common/search.ts @@ -80,7 +80,8 @@ export interface IWorkbenchSearchConfiguration extends ISearchConfiguration { }, exclude: glob.IExpression, useRipgrep: boolean, - useIgnoreFilesByDefault: boolean + useIgnoreFilesByDefault: boolean, + followSymlinks: boolean; }; } diff --git a/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts b/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts index 059f34fb3e3..4cf542b63a9 100644 --- a/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts +++ b/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts @@ -594,6 +594,8 @@ function assertEqualQueries(actual: ISearchQuery, expected: ISearchQuery): void }; }; + delete actual.ignoreSymlinks; + // Avoid comparing URI objects, not a good idea if (expected.folderQueries) { assert.deepEqual(actual.folderQueries.map(folderQueryToCompareObject), expected.folderQueries.map(folderQueryToCompareObject)); diff --git a/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.ts b/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.ts index 5e51b4e6c97..02f11542192 100644 --- a/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.ts +++ b/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.ts @@ -26,7 +26,7 @@ export interface ISnippetsService { _serviceBrand: any; - getSnippets(languageId: LanguageId): TPromise; + getSnippets(languageId: LanguageId): Promise; getSnippetsSync(languageId: LanguageId): Snippet[]; } diff --git a/src/vs/workbench/parts/snippets/electron-browser/snippetsFile.ts b/src/vs/workbench/parts/snippets/electron-browser/snippetsFile.ts index 11bf43c590f..109a7175e8a 100644 --- a/src/vs/workbench/parts/snippets/electron-browser/snippetsFile.ts +++ b/src/vs/workbench/parts/snippets/electron-browser/snippetsFile.ts @@ -7,7 +7,6 @@ import { readFile } from 'vs/base/node/pfs'; import { parse as jsonParse } from 'vs/base/common/json'; -import { TPromise } from 'vs/base/common/winjs.base'; import { SnippetParser, Variable, Placeholder, Text } from 'vs/editor/contrib/snippet/browser/snippetParser'; import { EditorSnippetVariableResolver } from 'vs/editor/contrib/snippet/browser/snippetVariables'; import { forEach } from 'vs/base/common/collections'; @@ -36,8 +35,8 @@ export class SnippetFile { // } - static fromFile(filepath: string, source: string, isFromExtension?: boolean): TPromise { - return readFile(filepath).then(value => { + static fromFile(filepath: string, source: string, isFromExtension?: boolean): Promise { + return Promise.resolve(readFile(filepath)).then(value => { const data = jsonParse(value.toString()); const snippets: Snippet[] = []; if (typeof data === 'object') { diff --git a/src/vs/workbench/parts/snippets/electron-browser/snippetsService.ts b/src/vs/workbench/parts/snippets/electron-browser/snippetsService.ts index c469f02372b..22b1571da3a 100644 --- a/src/vs/workbench/parts/snippets/electron-browser/snippetsService.ts +++ b/src/vs/workbench/parts/snippets/electron-browser/snippetsService.ts @@ -20,7 +20,6 @@ import { join } from 'path'; import { mkdirp } from 'vs/base/node/pfs'; import { watch } from 'fs'; import { SnippetFile } from 'vs/workbench/parts/snippets/electron-browser/snippetsFile'; -import { TPromise } from 'vs/base/common/winjs.base'; import { Snippet, ISnippetsService } from 'vs/workbench/parts/snippets/electron-browser/snippets.contribution'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; import { ExtensionsRegistry, IExtensionPointUser } from 'vs/platform/extensions/common/extensionsRegistry'; @@ -114,21 +113,22 @@ class SnippetsService implements ISnippetsService { dispose(this._disposables); } - async getSnippets(languageId: LanguageId): TPromise { + getSnippets(languageId: LanguageId): Promise { let result: Snippet[] = []; - await TPromise.join([ + return Promise.all([ this._extensionService.onReady(), this._getOrLoadUserSnippets(languageId, result), this._getOrLoadExtensionSnippets(languageId, result) - ]); - return result; + ]).then(() => { + return result; + }); } getSnippetsSync(languageId: LanguageId): Snippet[] { // just kick off snippet loading for this language such // that subseqent calls to this method return more // correct results - this.getSnippets(languageId).done(undefined, undefined); + this.getSnippets(languageId).catch(undefined); // collect and return what we already have let userSnippets = this._userSnippets.get(languageId); @@ -138,8 +138,8 @@ class SnippetsService implements ISnippetsService { // --- extension snippet logic --- - private async _prepExtensionSnippets(): TPromise { - ExtensionsRegistry.registerExtensionPoint('snippets', [languagesExtPoint], schema.snippetsContribution).setHandler(async extensions => { + private _prepExtensionSnippets(): void { + ExtensionsRegistry.registerExtensionPoint('snippets', [languagesExtPoint], schema.snippetsContribution).setHandler(extensions => { for (const extension of extensions) { for (const contribution of extension.value) { if (schema.isValidSnippet(extension, contribution, this._modeService)) { @@ -156,10 +156,11 @@ class SnippetsService implements ISnippetsService { }); } - private async _getOrLoadExtensionSnippets(languageId: LanguageId, bucket: Snippet[]): TPromise { + private _getOrLoadExtensionSnippets(languageId: LanguageId, bucket: Snippet[]): Promise { if (this._extensionSnippets.has(languageId)) { bucket.push(...this._extensionSnippets.get(languageId)); + return undefined; } else if (this._pendingExtensionSnippets.has(languageId)) { const pending = this._pendingExtensionSnippets.get(languageId); @@ -168,22 +169,14 @@ class SnippetsService implements ISnippetsService { const snippets = []; this._extensionSnippets.set(languageId, snippets); - for (const [extension, filepath] of pending) { - let file: SnippetFile; - try { - file = await SnippetFile.fromFile(filepath, extension.description.displayName || extension.description.name, true); - } catch (e) { - extension.collector.warn(localize( - 'badFile', - "The snippet file \"{0}\" could not be read.", - filepath - )); - } - if (file) { + return Promise.all(pending.map(([extension, filepath]) => { + return SnippetFile.fromFile(filepath, extension.description.displayName || extension.description.name, true).then(file => { for (const snippet of file.data) { snippets.push(snippet); bucket.push(snippet); + if (snippet.isBogous) { + // warn about bad tabstop/variable usage extension.collector.warn(localize( 'badVariableUse', "The \"{0}\"-snippet very likely confuses snippet-variables and snippet-placeholders. See https://code.visualstudio.com/docs/editor/userdefinedsnippets#_snippet-syntax for more details.", @@ -191,26 +184,42 @@ class SnippetsService implements ISnippetsService { )); } } - } - } + + }, err => { + // generic error + extension.collector.warn(localize( + 'badFile', + "The snippet file \"{0}\" could not be read.", + filepath + )); + }); + })); + + } else { + return undefined; } } // --- user snippet logic --- - private async _getOrLoadUserSnippets(languageId: LanguageId, bucket: Snippet[]): TPromise { + private _getOrLoadUserSnippets(languageId: LanguageId, bucket: Snippet[]): Promise { let snippets = this._userSnippets.get(languageId); - if (snippets === undefined) { - try { - snippets = (await SnippetFile.fromFile(this._getUserSnippetFilepath(languageId), localize('source.snippet', "User Snippet"))).data; - } catch (e) { - snippets = null; - } - this._userSnippets.set(languageId, snippets); - } - if (snippets) { + // has data bucket.push(...snippets); + return undefined; + + } else if (snippets === undefined) { + // not yet loaded + return SnippetFile.fromFile(this._getUserSnippetFilepath(languageId), localize('source.snippet', "User Snippet")).then(file => { + this._userSnippets.set(languageId, file.data); + }, err => { + this._userSnippets.set(languageId, null); + }); + + } else { + // previous failure + return undefined; } } @@ -291,49 +300,51 @@ export class SnippetSuggestProvider implements ISuggestSupport { // } - async provideCompletionItems(model: IModel, position: Position): TPromise { + provideCompletionItems(model: IModel, position: Position): Promise { const languageId = this._getLanguageIdAtPosition(model, position); - const snippets = await this._snippets.getSnippets(languageId); - const suggestions: SnippetSuggestion[] = []; + return this._snippets.getSnippets(languageId).then(snippets => { - const lowWordUntil = model.getWordUntilPosition(position).word.toLowerCase(); - const lowLineUntil = model.getLineContent(position.lineNumber).substr(Math.max(0, position.column - 100), position.column - 1).toLowerCase(); + const suggestions: SnippetSuggestion[] = []; - for (const snippet of snippets) { + const lowWordUntil = model.getWordUntilPosition(position).word.toLowerCase(); + const lowLineUntil = model.getLineContent(position.lineNumber).substr(Math.max(0, position.column - 100), position.column - 1).toLowerCase(); - const lowPrefix = snippet.prefix.toLowerCase(); - let overwriteBefore = 0; - let accetSnippet = true; + for (const snippet of snippets) { - if (lowWordUntil.length > 0 && startsWith(lowPrefix, lowWordUntil)) { - // cheap match on the (none-empty) current word - overwriteBefore = lowWordUntil.length; - accetSnippet = true; + const lowPrefix = snippet.prefix.toLowerCase(); + let overwriteBefore = 0; + let accetSnippet = true; - } else if (lowLineUntil.length > 0 && lowLineUntil.match(/[^\s]$/)) { - // compute overlap between snippet and (none-empty) line on text - overwriteBefore = overlap(lowLineUntil, snippet.prefix.toLowerCase()); - accetSnippet = overwriteBefore > 0 && !model.getWordAtPosition(new Position(position.lineNumber, position.column - overwriteBefore)); + if (lowWordUntil.length > 0 && startsWith(lowPrefix, lowWordUntil)) { + // cheap match on the (none-empty) current word + overwriteBefore = lowWordUntil.length; + accetSnippet = true; + + } else if (lowLineUntil.length > 0 && lowLineUntil.match(/[^\s]$/)) { + // compute overlap between snippet and (none-empty) line on text + overwriteBefore = overlap(lowLineUntil, snippet.prefix.toLowerCase()); + accetSnippet = overwriteBefore > 0 && !model.getWordAtPosition(new Position(position.lineNumber, position.column - overwriteBefore)); + } + + if (accetSnippet) { + suggestions.push(new SnippetSuggestion(snippet, overwriteBefore)); + } } - if (accetSnippet) { - suggestions.push(new SnippetSuggestion(snippet, overwriteBefore)); + // dismbiguate suggestions with same labels + let lastItem: SnippetSuggestion; + for (const item of suggestions.sort(SnippetSuggestion.compareByLabel)) { + if (lastItem && lastItem.label === item.label) { + // use the disambiguateLabel instead of the actual label + lastItem.label = localize('snippetSuggest.longLabel', "{0}, {1}", lastItem.label, lastItem.snippet.name); + item.label = localize('snippetSuggest.longLabel', "{0}, {1}", item.label, item.snippet.name); + } + lastItem = item; } - } - // dismbiguate suggestions with same labels - let lastItem: SnippetSuggestion; - for (const item of suggestions.sort(SnippetSuggestion.compareByLabel)) { - if (lastItem && lastItem.label === item.label) { - // use the disambiguateLabel instead of the actual label - lastItem.label = localize('snippetSuggest.longLabel', "{0}, {1}", lastItem.label, lastItem.snippet.name); - item.label = localize('snippetSuggest.longLabel', "{0}, {1}", item.label, item.snippet.name); - } - lastItem = item; - } - - return { suggestions }; + return { suggestions }; + }); } resolveCompletionItem?(model: IModel, position: Position, item: ISuggestion): ISuggestion { diff --git a/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.ts b/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.ts index dc7ac48b9b7..2a88a06104f 100644 --- a/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.ts +++ b/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.ts @@ -7,7 +7,7 @@ import { localize } from 'vs/nls'; import { KeyCode } from 'vs/base/common/keyCodes'; -import { RawContextKey, IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { RawContextKey, IContextKeyService, ContextKeyExpr, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ISnippetsService, Snippet } from 'vs/workbench/parts/snippets/electron-browser/snippets.contribution'; import { getNonWhitespacePrefix, SnippetSuggestion } from 'vs/workbench/parts/snippets/electron-browser/snippetsService'; @@ -20,6 +20,7 @@ import { SnippetController2 } from 'vs/editor/contrib/snippet/browser/snippetCon import { showSimpleSuggestions } from 'vs/editor/contrib/suggest/browser/suggest'; import { IConfigurationRegistry, Extensions as ConfigExt } from 'vs/platform/configuration/common/configurationRegistry'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @commonEditorContribution export class TabCompletionController implements editorCommon.IEditorContribution { @@ -31,44 +32,25 @@ export class TabCompletionController implements editorCommon.IEditorContribution return editor.getContribution(TabCompletionController.ID); } - private readonly _editor: editorCommon.ICommonCodeEditor; - private readonly _snippetController: SnippetController2; - private readonly _dispoables: IDisposable[] = []; + private readonly _hasSnippets: IContextKey; + private _snippets: Snippet[] = []; + private _selectionListener: IDisposable; + private _configListener: IDisposable; constructor( - editor: editorCommon.ICommonCodeEditor, + private readonly _editor: editorCommon.ICommonCodeEditor, + @ISnippetsService private readonly _snippetService: ISnippetsService, + @IConfigurationService private readonly _configurationService: IConfigurationService, @IContextKeyService contextKeyService: IContextKeyService, - @ISnippetsService snippetService: ISnippetsService ) { - this._editor = editor; - this._snippetController = SnippetController2.get(editor); - - const hasSnippets = TabCompletionController.ContextKey.bindTo(contextKeyService); - this._dispoables.push(editor.onDidChangeCursorSelection(e => { - - this._snippets.length = 0; - let selectFn: (snippet: Snippet) => boolean; - - if (e.selection.isEmpty()) { - // empty selection -> real text (no whitespace) left of cursor - const prefix = getNonWhitespacePrefix(editor.getModel(), editor.getPosition()); - selectFn = prefix && (snippet => endsWith(prefix, snippet.prefix)); - - } else { - // actual selection -> snippet must be a full match - const selected = editor.getModel().getValueInRange(e.selection); - selectFn = snippet => selected === snippet.prefix; + this._hasSnippets = TabCompletionController.ContextKey.bindTo(contextKeyService); + this._configListener = this._configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('editor.tabCompletion')) { + this._update(); } - - if (selectFn) { - const model = editor.getModel(); - model.tokenizeIfCheap(e.selection.positionLineNumber); - const id = model.getLanguageIdAtPosition(e.selection.positionLineNumber, e.selection.positionColumn); - this._snippets = snippetService.getSnippetsSync(id).filter(selectFn); - } - hasSnippets.set(this._snippets.length > 0); - })); + }); + this._update(); } getId(): string { @@ -76,7 +58,51 @@ export class TabCompletionController implements editorCommon.IEditorContribution } dispose(): void { - dispose(this._dispoables); + dispose(this._configListener); + dispose(this._selectionListener); + } + + private _update(): void { + const enabled = this._configurationService.getValue('editor.tabCompletion'); + if (!enabled) { + dispose(this._selectionListener); + } else { + this._selectionListener = this._editor.onDidChangeCursorSelection(e => this._updateSnippets()); + this._updateSnippets(); + } + } + + private _updateSnippets(): void { + + let selection = this._editor.getSelection(); + let model = this._editor.getModel(); + let selectFn: (snippet: Snippet) => boolean; + + if (!selection || !model) { + // too early + this._hasSnippets.set(false); + this._snippets = undefined; + return; + } + + if (selection.isEmpty()) { + // empty selection -> real text (no whitespace) left of cursor + const prefix = getNonWhitespacePrefix(model, this._editor.getPosition()); + selectFn = prefix && (snippet => endsWith(prefix, snippet.prefix)); + + } else if (selection.startLineNumber === selection.endLineNumber && model.getValueLengthInRange(selection) <= 100) { + // actual selection -> snippet must be a full match + const selected = model.getValueInRange(selection); + selectFn = snippet => selected === snippet.prefix; + } + + if (selectFn) { + model.tokenizeIfCheap(selection.positionLineNumber); + const id = model.getLanguageIdAtPosition(selection.positionLineNumber, selection.positionColumn); + this._snippets = this._snippetService.getSnippetsSync(id).filter(selectFn); + } + + this._hasSnippets.set(this._snippets.length > 0); } performSnippetCompletions(): void { @@ -84,7 +110,7 @@ export class TabCompletionController implements editorCommon.IEditorContribution if (this._snippets.length === 1) { // one -> just insert const [snippet] = this._snippets; - this._snippetController.insert(snippet.codeSnippet, snippet.prefix.length, 0); + SnippetController2.get(this._editor).insert(snippet.codeSnippet, snippet.prefix.length, 0); } else if (this._snippets.length > 1) { // two or more -> show IntelliSense box @@ -104,8 +130,7 @@ CommonEditorRegistry.registerEditorCommand(new TabCompletionCommand({ kbExpr: ContextKeyExpr.and( EditorContextKeys.textFocus, EditorContextKeys.tabDoesNotMoveFocus, - SnippetController2.InSnippetMode.toNegated(), - ContextKeyExpr.has('config.editor.tabCompletion') + SnippetController2.InSnippetMode.toNegated() ), primary: KeyCode.Tab } diff --git a/src/vs/workbench/parts/snippets/test/electron-browser/snippetsService.test.ts b/src/vs/workbench/parts/snippets/test/electron-browser/snippetsService.test.ts index bfeb7ee9116..169053ca38a 100644 --- a/src/vs/workbench/parts/snippets/test/electron-browser/snippetsService.test.ts +++ b/src/vs/workbench/parts/snippets/test/electron-browser/snippetsService.test.ts @@ -12,14 +12,13 @@ import { ModesRegistry } from 'vs/editor/common/modes/modesRegistry'; import { ModeServiceImpl } from 'vs/editor/common/services/modeServiceImpl'; import { Model } from 'vs/editor/common/model/model'; import { ISnippetsService, Snippet } from 'vs/workbench/parts/snippets/electron-browser/snippets.contribution'; -import { TPromise } from 'vs/base/common/winjs.base'; class SimpleSnippetService implements ISnippetsService { _serviceBrand: any; constructor(readonly snippets: Snippet[]) { } getSnippets() { - return TPromise.as(this.getSnippetsSync()); + return Promise.resolve(this.getSnippetsSync()); } getSnippetsSync(): Snippet[] { return this.snippets; @@ -56,31 +55,31 @@ suite('SnippetsService', function () { }); - test('snippet completions - simple', async function () { + test('snippet completions - simple', function () { const provider = new SnippetSuggestProvider(modeService, snippetService); const model = Model.createFromString('', undefined, modeService.getLanguageIdentifier('fooLang')); - const result = await provider.provideCompletionItems(model, new Position(1, 1)); - - assert.equal(result.incomplete, undefined); - assert.equal(result.suggestions.length, 2); + return provider.provideCompletionItems(model, new Position(1, 1)).then(result => { + assert.equal(result.incomplete, undefined); + assert.equal(result.suggestions.length, 2); + }); }); - test('snippet completions - with prefix', async function () { + test('snippet completions - with prefix', function () { const provider = new SnippetSuggestProvider(modeService, snippetService); const model = Model.createFromString('bar', undefined, modeService.getLanguageIdentifier('fooLang')); - const result = await provider.provideCompletionItems(model, new Position(1, 4)); - - assert.equal(result.incomplete, undefined); - assert.equal(result.suggestions.length, 1); - assert.equal(result.suggestions[0].label, 'bar'); - assert.equal(result.suggestions[0].insertText, 'barCodeSnippet'); + return provider.provideCompletionItems(model, new Position(1, 4)).then(result => { + assert.equal(result.incomplete, undefined); + assert.equal(result.suggestions.length, 1); + assert.equal(result.suggestions[0].label, 'bar'); + assert.equal(result.suggestions[0].insertText, 'barCodeSnippet'); + }); }); - test('Cannot use " { + assert.equal(result.suggestions.length, 1); + model.dispose(); - model = Model.createFromString('\t { + assert.equal(result.suggestions.length, 1); + model.dispose(); - model = Model.createFromString('a { + + assert.equal(result.suggestions.length, 0); + model.dispose(); + }); }); - test('No user snippets in suggestions, when inside the code, #30508', async function () { + test('No user snippets in suggestions, when inside the code, #30508', function () { snippetService = new SimpleSnippetService([{ prefix: 'foo', @@ -120,14 +123,15 @@ suite('SnippetsService', function () { const provider = new SnippetSuggestProvider(modeService, snippetService); let model = Model.createFromString('\n\t\n>/head>', undefined, modeService.getLanguageIdentifier('fooLang')); - let result = await provider.provideCompletionItems(model, new Position(1, 1)); - assert.equal(result.suggestions.length, 1); - - result = await provider.provideCompletionItems(model, new Position(2, 2)); - assert.equal(result.suggestions.length, 1); + return provider.provideCompletionItems(model, new Position(1, 1)).then(result => { + assert.equal(result.suggestions.length, 1); + return provider.provideCompletionItems(model, new Position(2, 2)); + }).then(result => { + assert.equal(result.suggestions.length, 1); + }); }); - test('SnippetSuggest - ensure extension snippets come last ', async function () { + test('SnippetSuggest - ensure extension snippets come last ', function () { snippetService = new SimpleSnippetService([{ prefix: 'second', codeSnippet: 'second', @@ -147,11 +151,11 @@ suite('SnippetsService', function () { const provider = new SnippetSuggestProvider(modeService, snippetService); let model = Model.createFromString('', undefined, modeService.getLanguageIdentifier('fooLang')); - let result = await provider.provideCompletionItems(model, new Position(1, 1)); - assert.equal(result.suggestions.length, 2); - - let [first, second] = result.suggestions; - assert.equal(first.label, 'first'); - assert.equal(second.label, 'second'); + return provider.provideCompletionItems(model, new Position(1, 1)).then(result => { + assert.equal(result.suggestions.length, 2); + let [first, second] = result.suggestions; + assert.equal(first.label, 'first'); + assert.equal(second.label, 'second'); + }); }); }); diff --git a/src/vs/workbench/parts/tasks/common/taskTemplates.ts b/src/vs/workbench/parts/tasks/common/taskTemplates.ts index 6165c13bc0a..35a33f94ca9 100644 --- a/src/vs/workbench/parts/tasks/common/taskTemplates.ts +++ b/src/vs/workbench/parts/tasks/common/taskTemplates.ts @@ -54,7 +54,7 @@ const msbuild: TaskEntry = { '\t"tasks": [', '\t\t{', '\t\t\t"taskName": "build",', - '\t\t\t"type": "process",', + '\t\t\t"type": "shell",', '\t\t\t"command": "msbuild",', '\t\t\t"args": [', '\t\t\t\t// Ask msbuild to generate full paths for file names.', 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 1737491b685..a476289267a 100644 --- a/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts +++ b/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts @@ -35,7 +35,7 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IMessageService, IChoiceService } 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 { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; 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'; @@ -63,7 +63,6 @@ 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, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; @@ -551,7 +550,6 @@ class TaskService extends EventEmitter implements ITaskService { private modeService: IModeService; private configurationService: IConfigurationService; - private configurationEditingService: IConfigurationEditingService; private markerService: IMarkerService; private outputService: IOutputService; private messageService: IMessageService; @@ -582,7 +580,6 @@ class TaskService extends EventEmitter implements ITaskService { private _outputChannel: IOutputChannel; constructor( @IModeService modeService: IModeService, @IConfigurationService configurationService: IConfigurationService, - @IConfigurationEditingService configurationEditingService: IConfigurationEditingService, @IMarkerService markerService: IMarkerService, @IOutputService outputService: IOutputService, @IMessageService messageService: IMessageService, @IChoiceService choiceService: IChoiceService, @IWorkbenchEditorService editorService: IWorkbenchEditorService, @@ -604,7 +601,6 @@ class TaskService extends EventEmitter implements ITaskService { super(); this.modeService = modeService; this.configurationService = configurationService; - this.configurationEditingService = configurationEditingService; this.markerService = markerService; this.outputService = outputService; this.messageService = messageService; @@ -624,7 +620,7 @@ class TaskService extends EventEmitter implements ITaskService { this._taskSystemListeners = []; this._outputChannel = this.outputService.getChannel(TaskService.OutputChannelId); this._providers = new Map(); - this.configurationService.onDidUpdateConfiguration(() => { + this.configurationService.onDidChangeConfiguration(() => { if (!this._taskSystem && !this._workspaceTasksPromise) { return; } @@ -1108,32 +1104,25 @@ class TaskService extends EventEmitter implements ITaskService { } 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 if (index === -1) { if (properties.problemMatcher !== void 0) { fileConfig.problemMatcher = properties.problemMatcher; - value.key = 'tasks.problemMatchers'; - value.value = fileConfig.problemMatcher; - promise = this.writeConfiguration(workspaceFolder, value); + promise = this.writeConfiguration(workspaceFolder, 'tasks.problemMatchers', fileConfig.problemMatcher); } else if (properties.group !== void 0) { fileConfig.group = properties.group; - value.key = 'tasks.group'; - value.value = fileConfig.group; - promise = this.writeConfiguration(workspaceFolder, value); + promise = this.writeConfiguration(workspaceFolder, 'tasks.group', fileConfig.group); } } else { if (!Array.isArray(fileConfig.tasks)) { fileConfig.tasks = []; } - value.key = 'tasks.tasks'; - value.value = fileConfig.tasks; if (index === void 0) { fileConfig.tasks.push(toCustomize); } else { fileConfig.tasks[index] = toCustomize; } - promise = this.writeConfiguration(workspaceFolder, value); + promise = this.writeConfiguration(workspaceFolder, 'tasks.tasks', fileConfig.tasks); } }; if (!promise) { @@ -1162,11 +1151,11 @@ class TaskService extends EventEmitter implements ITaskService { }); } - private writeConfiguration(workspaceFolder: IWorkspaceFolder, value: IConfigurationValue): TPromise { + private writeConfiguration(workspaceFolder: IWorkspaceFolder, key: string, value: any): TPromise { if (this.contextService.getWorkbenchState() === WorkbenchState.FOLDER) { - return this.configurationEditingService.writeConfiguration(ConfigurationTarget.WORKSPACE, value); + return this.configurationService.updateValue(key, value, { resource: workspaceFolder.uri }, ConfigurationTarget.WORKSPACE); } else if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) { - return this.configurationEditingService.writeConfiguration(ConfigurationTarget.FOLDER, value, { scopes: { resource: workspaceFolder.uri } }); + return this.configurationService.updateValue(key, value, { resource: workspaceFolder.uri }, ConfigurationTarget.WORKSPACE_FOLDER); } else { return undefined; } @@ -1421,6 +1410,10 @@ class TaskService extends EventEmitter implements ITaskService { let legacyTaskConfigurations = folderTasks.set ? this.getLegacyTaskConfigurations(folderTasks.set) : undefined; let customTasksToDelete: Task[] = []; if (configurations || legacyTaskConfigurations) { + let unUsedConfigurations: Set = new Set(); + if (configurations) { + Object.keys(configurations.byIdentifier).forEach(key => unUsedConfigurations.add(key)); + } for (let task of contributed) { if (!ContributedTask.is(task)) { continue; @@ -1428,6 +1421,7 @@ class TaskService extends EventEmitter implements ITaskService { if (configurations) { let configuringTask = configurations.byIdentifier[task.defines._key]; if (configuringTask) { + unUsedConfigurations.delete(task.defines._key); result.add(key, TaskConfig.createCustomTask(task, configuringTask)); } else { result.add(key, task); @@ -1458,6 +1452,16 @@ class TaskService extends EventEmitter implements ITaskService { } else { result.add(key, ...folderTasks.set.tasks); } + unUsedConfigurations.forEach((value) => { + let configuringTask = configurations.byIdentifier[value]; + this._outputChannel.append(nls.localize( + 'TaskService.noConfiguration', + 'Error: The {0} task detection didn\'t contribute a task for the following configuration:\n{1}\nThe task will be ignored.\n', + configuringTask.configures.type, + JSON.stringify(configuringTask._source.config.element, undefined, 4) + )); + this.showOutput(); + }); } else { result.add(key, ...folderTasks.set.tasks); result.add(key, ...contributed); diff --git a/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts b/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts index 39e7bb10cc6..bf9e700758e 100644 --- a/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts +++ b/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts @@ -397,6 +397,9 @@ export class TerminalTaskSystem extends EventEmitter implements ITaskSystem { }); }); } + if (!terminal) { + return TPromise.wrapError(new Error(`Failed to create terminal for task ${task._label}`)); + } this.terminalService.setActiveInstance(terminal); if (task.command.presentation.reveal === RevealKind.Always || (task.command.presentation.reveal === RevealKind.Silent && task.problemMatchers.length === 0)) { this.terminalService.showPanel(task.command.presentation.focus); diff --git a/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.ts b/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.ts index 0f5e860dc61..dc1412e510e 100644 --- a/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.ts +++ b/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.ts @@ -14,6 +14,8 @@ import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { ContributableActionProvider } from 'vs/workbench/browser/actions'; import { stripWildcards } from 'vs/base/common/strings'; import { matchesFuzzy } from 'vs/base/common/filters'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { PICK_WORKSPACE_FOLDER_COMMAND } from 'vs/workbench/browser/actions/workspaceActions'; export class TerminalEntry extends QuickOpenEntry { @@ -49,7 +51,8 @@ export class CreateTerminal extends QuickOpenEntry { constructor( private label: string, - private terminalService: ITerminalService + private terminalService: ITerminalService, + private commandService: ICommandService ) { super(); } @@ -65,9 +68,14 @@ export class CreateTerminal extends QuickOpenEntry { public run(mode: Mode, context: IEntryRunContext): boolean { if (mode === Mode.OPEN) { setTimeout(() => { - const newTerminal = this.terminalService.createInstance(); - this.terminalService.setActiveInstance(newTerminal); - this.terminalService.showPanel(true); + return this.commandService.executeCommand(PICK_WORKSPACE_FOLDER_COMMAND).then(workspace => { + const instance = this.terminalService.createInstance({ cwd: workspace.uri.fsPath }, true); + if (!instance) { + return TPromise.as(void 0); + } + this.terminalService.setActiveInstance(instance); + return this.terminalService.showPanel(true); + }); }, 0); return true; } @@ -82,6 +90,7 @@ export class TerminalPickerHandler extends QuickOpenHandler { constructor( @ITerminalService private terminalService: ITerminalService, + @ICommandService private commandService: ICommandService, @IPanelService private panelService: IPanelService ) { super(); @@ -92,7 +101,7 @@ export class TerminalPickerHandler extends QuickOpenHandler { const normalizedSearchValueLowercase = stripWildcards(searchValue).toLowerCase(); const terminalEntries: QuickOpenEntry[] = this.getTerminals(); - terminalEntries.push(new CreateTerminal(nls.localize("'workbench.action.terminal.newplus", "$(plus) Create New Integrated Terminal"), this.terminalService)); + terminalEntries.push(new CreateTerminal(nls.localize("'workbench.action.terminal.newplus", "$(plus) Create New Integrated Terminal"), this.terminalService, this.commandService)); const entries = terminalEntries.filter(e => { if (!searchValue) { diff --git a/src/vs/workbench/parts/terminal/common/terminal.ts b/src/vs/workbench/parts/terminal/common/terminal.ts index 93ffb4af4c6..fe4841ac5ec 100644 --- a/src/vs/workbench/parts/terminal/common/terminal.ts +++ b/src/vs/workbench/parts/terminal/common/terminal.ts @@ -58,7 +58,7 @@ export interface ITerminalConfiguration { osx: string[]; windows: string[]; }; - // enableBold: boolean; + enableBold: boolean; rightClickCopyPaste: boolean; cursorBlinking: boolean; cursorStyle: string; diff --git a/src/vs/workbench/parts/terminal/common/terminalService.ts b/src/vs/workbench/parts/terminal/common/terminalService.ts index 305ac98c424..6ca13f5b6fd 100644 --- a/src/vs/workbench/parts/terminal/common/terminalService.ts +++ b/src/vs/workbench/parts/terminal/common/terminalService.ts @@ -43,7 +43,7 @@ export abstract class TerminalService implements ITerminalService { constructor( @IContextKeyService private _contextKeyService: IContextKeyService, - @IConfigurationService private _configurationService: IConfigurationService, + @IConfigurationService protected _configurationService: IConfigurationService, @IPanelService protected _panelService: IPanelService, @IPartService private _partService: IPartService, @ILifecycleService lifecycleService: ILifecycleService @@ -59,7 +59,11 @@ export abstract class TerminalService implements ITerminalService { this._onInstanceTitleChanged = new Emitter(); this._onInstancesChanged = new Emitter(); - this._configurationService.onDidUpdateConfiguration(() => this.updateConfig()); + this._configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('terminal.integrated')) { + this.updateConfig(); + } + }); lifecycleService.onWillShutdown(event => event.veto(this._onWillShutdown())); this._terminalFocusContextKey = KEYBINDING_CONTEXT_TERMINAL_FOCUS.bindTo(this._contextKeyService); this._findWidgetVisible = KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE.bindTo(this._contextKeyService); diff --git a/src/vs/workbench/parts/terminal/electron-browser/media/xterm.css b/src/vs/workbench/parts/terminal/electron-browser/media/xterm.css index 0e554c525ba..31206060c2b 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/media/xterm.css +++ b/src/vs/workbench/parts/terminal/electron-browser/media/xterm.css @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ /** - * xterm.js: xterm, in the browser - * Copyright (c) 2014-2016, SourceLair Private Company (www.sourcelair.com (MIT License) + * Copyright (c) 2014 The xterm.js authors. All rights reserved. * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License) * https://github.com/chjj/term.js + * @license MIT * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -39,7 +39,7 @@ * Default styles for xterm.js */ -.terminal { +.xterm { font-family: courier-new, courier, monospace; font-feature-settings: "liga" 0; position: relative; @@ -48,12 +48,12 @@ -webkit-user-select: none; } -.terminal.focus, -.terminal:focus { +.xterm.focus, +.xterm:focus { outline: none; } -.terminal .xterm-helpers { +.xterm .xterm-helpers { position: absolute; top: 0; /** @@ -63,7 +63,7 @@ z-index: 10; } -.terminal .xterm-helper-textarea { +.xterm .xterm-helper-textarea { /* * HACK: to fix IE's blinking cursor * Move textarea out of the screen to the far left, so that the cursor is not visible. @@ -81,7 +81,7 @@ resize: none; } -.terminal .composition-view { +.xterm .composition-view { /* TODO: Composition position got messed up somewhere */ background: #000; color: #FFF; @@ -91,38 +91,38 @@ z-index: 1; } -.terminal .composition-view.active { +.xterm .composition-view.active { display: block; } -.terminal .xterm-viewport { +.xterm .xterm-viewport { /* On OS X this is required in order for the scroll bar to appear fully opaque */ background-color: #000; overflow-y: scroll; } -.terminal canvas { +.xterm canvas { position: absolute; left: 0; top: 0; } -.terminal .xterm-scroll-area { +.xterm .xterm-scroll-area { visibility: hidden; } -.terminal .xterm-char-measure-element { +.xterm .xterm-char-measure-element { display: inline-block; visibility: hidden; position: absolute; left: -9999em; } -.terminal.enable-mouse-events { +.xterm.enable-mouse-events { /* When mouse events are enabled (eg. tmux), revert to the standard pointer cursor */ cursor: default; } -.terminal:not(.enable-mouse-events) { - cursor: text; +.xterm:not(.enable-mouse-events) { + cursor: text; } 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 f5d947c4492..a59abbfbaa3 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts @@ -30,7 +30,7 @@ import { OpenNextRecentlyUsedEditorInGroupAction, OpenPreviousRecentlyUsedEditor import { EDITOR_FONT_DEFAULTS } from 'vs/editor/common/config/editorOptions'; import { registerColors } from './terminalColorRegistry'; import { NavigateUpAction, NavigateDownAction, NavigateLeftAction, NavigateRightAction } from 'vs/workbench/electron-browser/actions'; -import { QUICKOPEN_ACTION_ID, getQuickNavigateHandler } from 'vs/workbench/browser/parts/quickopen/quickopen'; +import { QUICKOPEN_ACTION_ID, getQuickNavigateHandler, QUICKOPEN_FOCUS_SECONDARY_ACTION_ID } from 'vs/workbench/browser/parts/quickopen/quickopen'; import { IQuickOpenRegistry, Extensions as QuickOpenExtensions, QuickOpenHandlerDescriptor } from 'vs/workbench/browser/quickopen'; import { Scope, IActionBarRegistry, Extensions as ActionBarExtensions } from 'vs/workbench/browser/actions'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; @@ -140,11 +140,11 @@ configurationRegistry.registerConfiguration({ 'type': 'number', 'default': 1 }, - // 'terminal.integrated.enableBold': { - // 'type': 'boolean', - // 'description': nls.localize('terminal.integrated.enableBold', "Whether to enable bold text within the terminal, this requires support from the terminal shell."), - // 'default': true - // }, + 'terminal.integrated.enableBold': { + 'type': 'boolean', + 'description': nls.localize('terminal.integrated.enableBold', "Whether to enable bold text within the terminal, note that this requires support from the terminal shell."), + 'default': true + }, 'terminal.integrated.cursorBlinking': { 'description': nls.localize('terminal.integrated.cursorBlinking', "Controls whether the terminal cursor blinks."), 'type': 'boolean', @@ -185,6 +185,7 @@ configurationRegistry.registerConfiguration({ ToggleTabFocusModeAction.ID, FocusActiveGroupAction.ID, QUICKOPEN_ACTION_ID, + QUICKOPEN_FOCUS_SECONDARY_ACTION_ID, ShowAllCommandsAction.ID, CreateNewTerminalAction.ID, CopyTerminalSelectionAction.ID, @@ -192,6 +193,13 @@ configurationRegistry.registerConfiguration({ FocusActiveTerminalAction.ID, FocusPreviousTerminalAction.ID, FocusNextTerminalAction.ID, + 'workbench.action.tasks.build', + 'workbench.action.tasks.restartTask', + 'workbench.action.tasks.runTask', + 'workbench.action.tasks.showLog', + 'workbench.action.tasks.showTasks', + 'workbench.action.tasks.terminate', + 'workbench.action.tasks.test', 'workbench.action.terminal.focusAtIndex1', 'workbench.action.terminal.focusAtIndex2', 'workbench.action.terminal.focusAtIndex3', diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts index b38714c7e04..ce3950c1dbb 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts @@ -8,7 +8,7 @@ import * as os from 'os'; import { Action, IAction } from 'vs/base/common/actions'; import { EndOfLinePreference } from 'vs/editor/common/editorCommon'; import { ICodeEditorService } from 'vs/editor/common/services/codeEditorService'; -import { ITerminalService, TERMINAL_PANEL_ID } from 'vs/workbench/parts/terminal/common/terminal'; +import { ITerminalService, TERMINAL_PANEL_ID, ITerminalInstance } from 'vs/workbench/parts/terminal/common/terminal'; import { SelectActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { TPromise } from 'vs/base/common/winjs.base'; import { TogglePanelAction } from 'vs/workbench/browser/panel'; @@ -21,6 +21,9 @@ import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; import { ActionBarContributor } from 'vs/workbench/browser/actions'; import { TerminalEntry } from 'vs/workbench/parts/terminal/browser/terminalQuickOpen'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { PICK_WORKSPACE_FOLDER_COMMAND } from 'vs/workbench/browser/actions/workspaceActions'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; export const TERMINAL_PICKER_PREFIX = 'term '; @@ -200,19 +203,39 @@ export class CreateNewTerminalAction extends Action { constructor( id: string, label: string, - @ITerminalService private terminalService: ITerminalService + @ITerminalService private terminalService: ITerminalService, + @ICommandService private commandService: ICommandService, + @IWorkspaceContextService private workspaceContextService: IWorkspaceContextService ) { super(id, label); this.class = 'terminal-action new'; } public run(event?: any): TPromise { - const instance = this.terminalService.createInstance(undefined, true); - if (!instance) { - return TPromise.as(void 0); + const folders = this.workspaceContextService.getWorkspace().folders; + + let instancePromise: TPromise; + if (folders.length <= 1) { + // Allow terminal service to handle the path when there is only a + // single root + instancePromise = TPromise.as(this.terminalService.createInstance(undefined, true)); + } else { + instancePromise = this.commandService.executeCommand(PICK_WORKSPACE_FOLDER_COMMAND).then(workspace => { + if (!workspace) { + // Don't create the instance if the workspace picker was canceled + return null; + } + return this.terminalService.createInstance({ cwd: workspace.uri.fsPath }, true); + }); } - this.terminalService.setActiveInstance(instance); - return this.terminalService.showPanel(true); + + return instancePromise.then(instance => { + if (!instance) { + return TPromise.as(void 0); + } + this.terminalService.setActiveInstance(instance); + return this.terminalService.showPanel(true); + }); } } diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts index b1793339026..62281821b81 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts @@ -28,6 +28,8 @@ interface IFullTerminalConfiguration { const DEFAULT_LINE_HEIGHT = 1.0; +const MINIMUM_FONT_SIZE = 6; + /** * Encapsulates terminal configuration logic, the primary purpose of this file is so that platform * specific test cases can be written. @@ -99,10 +101,7 @@ export class TerminalConfigHelper implements ITerminalConfigHelper { } } - let fontSize = this._toInteger(terminalConfig.fontSize, 0); - if (fontSize <= 0) { - fontSize = EDITOR_FONT_DEFAULTS.fontSize; - } + let fontSize = this._toInteger(terminalConfig.fontSize, MINIMUM_FONT_SIZE, EDITOR_FONT_DEFAULTS.fontSize); const lineHeight = terminalConfig.lineHeight ? Math.max(terminalConfig.lineHeight, 1) : DEFAULT_LINE_HEIGHT; if (excludeDimensions) { @@ -123,8 +122,8 @@ export class TerminalConfigHelper implements ITerminalConfigHelper { public mergeDefaultShellPathAndArgs(shell: IShellLaunchConfig): void { // Check whether there is a workspace setting const platformKey = platform.isWindows ? 'windows' : platform.isMacintosh ? 'osx' : 'linux'; - const shellConfigValue = this._workspaceConfigurationService.lookup(`terminal.integrated.shell.${platformKey}`); - const shellArgsConfigValue = this._workspaceConfigurationService.lookup(`terminal.integrated.shellArgs.${platformKey}`); + const shellConfigValue = this._workspaceConfigurationService.inspect(`terminal.integrated.shell.${platformKey}`); + const shellArgsConfigValue = this._workspaceConfigurationService.inspect(`terminal.integrated.shellArgs.${platformKey}`); // Check if workspace setting exists and whether it's whitelisted let isWorkspaceShellAllowed = false; @@ -180,10 +179,10 @@ export class TerminalConfigHelper implements ITerminalConfigHelper { } } - private _toInteger(source: any, minimum?: number): number { + private _toInteger(source: any, minimum: number, fallback: number): number { let r = parseInt(source, 10); if (isNaN(r)) { - r = 0; + return fallback; } if (typeof minimum === 'number') { r = Math.max(minimum, r); diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts index 7916f16cc40..9a96d6c8f5a 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts @@ -266,7 +266,8 @@ export class TerminalInstance implements ITerminalInstance { theme: this._getXtermTheme(), fontFamily: font.fontFamily, fontSize: font.fontSize, - lineHeight: font.lineHeight + lineHeight: font.lineHeight, + enableBold: this._configHelper.config.enableBold }); if (this._shellLaunchConfig.initialText) { this._xterm.writeln(this._shellLaunchConfig.initialText); @@ -511,14 +512,16 @@ export class TerminalInstance implements ITerminalInstance { // background since scrollTop changes take no effect but the terminal's position does // change since the number of visible rows decreases. this._xterm.emit('scroll', this._xterm.buffer.ydisp); - // Force a layout when the instance becomes invisible. This is particularly important - // for ensuring that terminals that are created in the background by an extension will - // correctly get correct character measurements in order to render to the screen (see - // #34554). - const computedStyle = window.getComputedStyle(this._container); - const width = parseInt(computedStyle.getPropertyValue('width').replace('px', ''), 10); - const height = parseInt(computedStyle.getPropertyValue('height').replace('px', ''), 10); - this.layout(new Dimension(width, height)); + if (this._container) { + // Force a layout when the instance becomes invisible. This is particularly important + // for ensuring that terminals that are created in the background by an extension will + // correctly get correct character measurements in order to render to the screen (see + // #34554). + const computedStyle = window.getComputedStyle(this._container); + const width = parseInt(computedStyle.getPropertyValue('width').replace('px', ''), 10); + const height = parseInt(computedStyle.getPropertyValue('height').replace('px', ''), 10); + this.layout(new Dimension(width, height)); + } } } @@ -921,21 +924,31 @@ export class TerminalInstance implements ITerminalInstance { if (!terminalWidth) { return; } + if (this._xterm) { const font = this._configHelper.getFont(); - if (this._xterm.getOption('lineHeight') !== font.lineHeight) { - this._xterm.setOption('lineHeight', font.lineHeight); - } - if (this._xterm.getOption('fontSize') !== font.fontSize) { - this._xterm.setOption('fontSize', font.fontSize); - } - if (this._xterm.getOption('fontFamily') !== font.fontFamily) { - this._xterm.setOption('fontFamily', font.fontFamily); + + // Only apply these settings when the terminal is visible so that + // the characters are measured correctly. + if (this._isVisible) { + if (this._xterm.getOption('lineHeight') !== font.lineHeight) { + this._xterm.setOption('lineHeight', font.lineHeight); + } + if (this._xterm.getOption('fontSize') !== font.fontSize) { + this._xterm.setOption('fontSize', font.fontSize); + } + if (this._xterm.getOption('fontFamily') !== font.fontFamily) { + this._xterm.setOption('fontFamily', font.fontFamily); + } + if (this._xterm.getOption('enableBold') !== this._configHelper.config.enableBold) { + this._xterm.setOption('enableBold', this._configHelper.config.enableBold); + } } this._xterm.resize(this._cols, this._rows); this._xterm.element.style.width = terminalWidth + 'px'; } + this._processReady.then(() => { if (this._process && this._process.connected) { // The child process could aready be terminated diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.ts index 83b76d604ac..8650646c7ad 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.ts @@ -14,6 +14,7 @@ import { IOpenerService } from 'vs/platform/opener/common/opener'; import { TerminalWidgetManager } from 'vs/workbench/parts/terminal/browser/terminalWidgetManager'; import { TPromise } from 'vs/base/common/winjs.base'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ITerminalService } from 'vs/workbench/parts/terminal/common/terminal'; const pathPrefix = '(\\.\\.?|\\~)'; const pathSeparatorClause = '\\/'; @@ -67,7 +68,8 @@ export class TerminalLinkHandler { private _initialCwd: string, @IOpenerService private _openerService: IOpenerService, @IWorkbenchEditorService private _editorService: IWorkbenchEditorService, - @IConfigurationService private _configurationService: IConfigurationService + @IConfigurationService private _configurationService: IConfigurationService, + @ITerminalService private _terminalService: ITerminalService ) { const baseLocalLinkClause = _platform === platform.Platform.Windows ? winLocalLinkClause : unixLocalLinkClause; // Append line and column number regex @@ -120,6 +122,9 @@ export class TerminalLinkHandler { event.preventDefault(); // Require correct modifier on click if (!this._isLinkActivationModifierDown(event)) { + // If the modifier is not pressed, the terminal should be + // focused if it's not already + this._terminalService.getActiveInstance().focus(true); return false; } return handler(uri); diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts index 56a3523d7e2..a67a1bceefb 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts @@ -19,7 +19,7 @@ import { ITerminalService, ITerminalFont, TERMINAL_PANEL_ID } from 'vs/workbench import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService'; import { TerminalFindWidget } from './terminalFindWidget'; import { editorHoverBackground, editorHoverBorder, editorForeground } from 'vs/platform/theme/common/colorRegistry'; -import { KillTerminalAction, CreateNewTerminalAction, SwitchTerminalInstanceAction, SwitchTerminalInstanceActionItem, CopyTerminalSelectionAction, TerminalPasteAction, ClearTerminalAction, SelectAllTerminalAction } from 'vs/workbench/parts/terminal/electron-browser/terminalActions'; +import { KillTerminalAction, SwitchTerminalInstanceAction, SwitchTerminalInstanceActionItem, CopyTerminalSelectionAction, TerminalPasteAction, ClearTerminalAction, SelectAllTerminalAction, CreateNewTerminalAction } from 'vs/workbench/parts/terminal/electron-browser/terminalActions'; import { Panel } from 'vs/workbench/browser/panel'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { TPromise } from 'vs/base/common/winjs.base'; @@ -74,7 +74,11 @@ export class TerminalPanel extends Panel { this._terminalService.setContainers(this.getContainer().getHTMLElement(), this._terminalContainer); this._register(this.themeService.onThemeChange(theme => this._updateTheme(theme))); - this._register(this._configurationService.onDidUpdateConfiguration(() => this._updateFont())); + this._register(this._configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('terminal.integrated') || e.affectsConfiguration('editor.fontFamily')) { + this._updateFont(); + } + })); this._updateFont(); this._updateTheme(); @@ -129,7 +133,7 @@ export class TerminalPanel extends Panel { if (!this._contextMenuActions) { this._copyContextMenuAction = this._instantiationService.createInstance(CopyTerminalSelectionAction, CopyTerminalSelectionAction.ID, nls.localize('copy', "Copy")); this._contextMenuActions = [ - this._instantiationService.createInstance(CreateNewTerminalAction, CreateNewTerminalAction.ID, nls.localize('createNewTerminal', "New Terminal")), + this._instantiationService.createInstance(CreateNewTerminalAction, CreateNewTerminalAction.ID, CreateNewTerminalAction.PANEL_LABEL), new Separator(), this._copyContextMenuAction, this._instantiationService.createInstance(TerminalPasteAction, TerminalPasteAction.ID, nls.localize('paste', "Paste")), @@ -302,8 +306,6 @@ export class TerminalPanel extends Panel { this._font = this._terminalService.configHelper.getFont(); // TODO: Can we support ligatures? // dom.toggleClass(this._parentDomElement, 'enable-ligatures', this._terminalService.configHelper.config.fontLigatures); - // TODO: How to handle Disable bold? - // dom.toggleClass(this._parentDomElement, 'disable-bold', !this._terminalService.configHelper.config.enableBold); this.layout(new Dimension(this._parentDomElement.offsetWidth, this._parentDomElement.offsetHeight)); } diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalService.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalService.ts index 512c65ed2d4..66d5340d584 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalService.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalService.ts @@ -11,8 +11,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IPartService } from 'vs/workbench/services/part/common/partService'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; +import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { IQuickOpenService, IPickOpenEntry, IPickOptions } from 'vs/platform/quickOpen/common/quickOpen'; import { ITerminalInstance, ITerminalService, IShellLaunchConfig, ITerminalConfigHelper, NEVER_SUGGEST_SELECT_WINDOWS_SHELL_STORAGE_KEY, TERMINAL_PANEL_ID } from 'vs/workbench/parts/terminal/common/terminal'; import { TerminalService as AbstractTerminalService } from 'vs/workbench/parts/terminal/common/terminalService'; @@ -39,7 +38,6 @@ export class TerminalService extends AbstractTerminalService implements ITermina @IInstantiationService private _instantiationService: IInstantiationService, @IWindowService private _windowService: IWindowService, @IQuickOpenService private _quickOpenService: IQuickOpenService, - @IConfigurationEditingService private _configurationEditingService: IConfigurationEditingService, @IChoiceService private _choiceService: IChoiceService, @IStorageService private _storageService: IStorageService, @IMessageService private _messageService: IMessageService @@ -163,8 +161,7 @@ export class TerminalService extends AbstractTerminalService implements ITermina return null; } const shell = value.description; - const configChange = { key: 'terminal.integrated.shell.windows', value: shell }; - return this._configurationEditingService.writeConfiguration(ConfigurationTarget.USER, configChange).then(() => shell); + return this._configurationService.updateValue('terminal.integrated.shell.windows', shell, ConfigurationTarget.USER).then(() => shell); }); }); } diff --git a/src/vs/workbench/parts/terminal/test/electron-browser/terminalConfigHelper.test.ts b/src/vs/workbench/parts/terminal/test/electron-browser/terminalConfigHelper.test.ts index 3eecd534b8c..be088071437 100644 --- a/src/vs/workbench/parts/terminal/test/electron-browser/terminalConfigHelper.test.ts +++ b/src/vs/workbench/parts/terminal/test/electron-browser/terminalConfigHelper.test.ts @@ -6,7 +6,7 @@ 'use strict'; import * as assert from 'assert'; -import { IConfigurationService, getConfigurationValue, IConfigurationValue, IConfigurationOverrides } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationService, getConfigurationValue, IConfigurationOverrides } from 'vs/platform/configuration/common/configuration'; import { Platform } from 'vs/base/common/platform'; import { TPromise } from 'vs/base/common/winjs.base'; import { TerminalConfigHelper } from 'vs/workbench/parts/terminal/electron-browser/terminalConfigHelper'; @@ -17,13 +17,14 @@ class MockConfigurationService implements IConfigurationService { public _serviceBrand: any; public serviceId = IConfigurationService; public constructor(private configuration: any = {}) { } - public reloadConfiguration(section?: string): TPromise { return TPromise.as(this.getConfiguration()); } - public lookup(key: string, overrides?: IConfigurationOverrides): IConfigurationValue { return { value: getConfigurationValue(this.getConfiguration(), key), default: getConfigurationValue(this.getConfiguration(), key), user: getConfigurationValue(this.getConfiguration(), key), workspace: void 0, folder: void 0 }; } - public keys() { return { default: [], user: [], workspace: [], folder: [] }; } - public values() { return {}; } + public inspect(key: string, overrides?: IConfigurationOverrides): any { return { value: getConfigurationValue(this.getConfiguration(), key), default: getConfigurationValue(this.getConfiguration(), key), user: getConfigurationValue(this.getConfiguration(), key), workspace: void 0, workspaceFolder: void 0 }; } + public keys() { return { default: [], user: [], workspace: [], workspaceFolder: [] }; } public getConfiguration(): any { return this.configuration; } + public getValue(key: string, overrides?: IConfigurationOverrides): T { return getConfigurationValue(this.getConfiguration(), key); } + public updateValue(): TPromise { return null; } public getConfigurationData(): any { return null; } - public onDidUpdateConfiguration() { return { dispose() { } }; } + public onDidChangeConfiguration() { return { dispose() { } }; } + public reloadConfiguration() { return null; } } suite('Workbench - TerminalConfigHelper', () => { @@ -77,23 +78,22 @@ suite('Workbench - TerminalConfigHelper', () => { configurationService = new MockConfigurationService({ editor: { fontFamily: 'foo', - fontSize: 1 + fontSize: 9 }, terminal: { integrated: { fontFamily: 'bar', - fontSize: 2 + fontSize: 10 } } }); configHelper = new TerminalConfigHelper(Platform.Linux, configurationService, null, null, null); configHelper.panelContainer = fixture; - assert.equal(configHelper.getFont().fontSize, 2, 'terminal.integrated.fontSize should be selected over editor.fontSize'); + assert.equal(configHelper.getFont().fontSize, 10, 'terminal.integrated.fontSize should be selected over editor.fontSize'); configurationService = new MockConfigurationService({ editor: { - fontFamily: 'foo', - fontSize: 0 + fontFamily: 'foo' }, terminal: { integrated: { @@ -104,23 +104,22 @@ suite('Workbench - TerminalConfigHelper', () => { }); configHelper = new TerminalConfigHelper(Platform.Linux, configurationService, null, null, null); configHelper.panelContainer = fixture; - assert.equal(configHelper.getFont().fontSize, EDITOR_FONT_DEFAULTS.fontSize, 'The default editor font size should be used when editor.fontSize is 0 and terminal.integrated.fontSize not set'); + assert.equal(configHelper.getFont().fontSize, 6, 'The minimum terminal font size should be used when terminal.integrated.fontSize less than it'); configurationService = new MockConfigurationService({ editor: { fontFamily: 'foo', - fontSize: 0 }, terminal: { integrated: { fontFamily: 0, - fontSize: -10 + fontSize: null } } }); configHelper = new TerminalConfigHelper(Platform.Linux, configurationService, null, null, null); configHelper.panelContainer = fixture; - assert.equal(configHelper.getFont().fontSize, EDITOR_FONT_DEFAULTS.fontSize, 'The default editor font size should be used when editor.fontSize is < 0 and terminal.integrated.fontSize not set'); + assert.equal(configHelper.getFont().fontSize, EDITOR_FONT_DEFAULTS.fontSize, 'The default editor font size should be used when terminal.integrated.fontSize is not set'); }); test('TerminalConfigHelper - getFont lineHeight', function () { diff --git a/src/vs/workbench/parts/terminal/test/electron-browser/terminalLinkHandler.test.ts b/src/vs/workbench/parts/terminal/test/electron-browser/terminalLinkHandler.test.ts index 77d9fed7145..491637a485c 100644 --- a/src/vs/workbench/parts/terminal/test/electron-browser/terminalLinkHandler.test.ts +++ b/src/vs/workbench/parts/terminal/test/electron-browser/terminalLinkHandler.test.ts @@ -35,7 +35,7 @@ interface LinkFormatInfo { suite('Workbench - TerminalLinkHandler', () => { suite('localLinkRegex', () => { test('Windows', () => { - const terminalLinkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Windows, null, null, null, null); + const terminalLinkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Windows, null, null, null, null, null); function testLink(link: string, linkUrl: string, lineNo?: string, columnNo?: string) { assert.equal(terminalLinkHandler.extractLinkUrl(link), linkUrl); assert.equal(terminalLinkHandler.extractLinkUrl(`:${link}:`), linkUrl); @@ -105,7 +105,7 @@ suite('Workbench - TerminalLinkHandler', () => { }); test('Linux', () => { - const terminalLinkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Linux, null, null, null, null); + const terminalLinkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Linux, null, null, null, null, null); function testLink(link: string, linkUrl: string, lineNo?: string, columnNo?: string) { assert.equal(terminalLinkHandler.extractLinkUrl(link), linkUrl); assert.equal(terminalLinkHandler.extractLinkUrl(`:${link}:`), linkUrl); @@ -169,7 +169,7 @@ suite('Workbench - TerminalLinkHandler', () => { suite('preprocessPath', () => { test('Windows', () => { - const linkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Windows, 'C:\\base', null, null, null); + const linkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Windows, 'C:\\base', null, null, null, null); let stub = sinon.stub(path, 'join', function (arg1, arg2) { return arg1 + '\\' + arg2; @@ -182,7 +182,7 @@ suite('Workbench - TerminalLinkHandler', () => { }); test('Linux', () => { - const linkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Linux, '/base', null, null, null); + const linkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Linux, '/base', null, null, null, null); let stub = sinon.stub(path, 'join', function (arg1, arg2) { return arg1 + '/' + arg2; @@ -195,7 +195,7 @@ suite('Workbench - TerminalLinkHandler', () => { }); test('No Workspace', () => { - const linkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Linux, null, null, null, null); + const linkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Linux, null, null, null, null, null); assert.equal(linkHandler.preprocessPath('./src/file1'), null); assert.equal(linkHandler.preprocessPath('src/file2'), null); diff --git a/src/vs/workbench/parts/themes/electron-browser/themes.contribution.ts b/src/vs/workbench/parts/themes/electron-browser/themes.contribution.ts index 2c44384ef13..d8f6bebd750 100644 --- a/src/vs/workbench/parts/themes/electron-browser/themes.contribution.ts +++ b/src/vs/workbench/parts/themes/electron-browser/themes.contribution.ts @@ -20,11 +20,11 @@ import { VIEWLET_ID, IExtensionsViewlet } from 'vs/workbench/parts/extensions/co import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { Delayer } from 'vs/base/common/async'; -import { ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { IColorRegistry, Extensions as ColorRegistryExtensions } from 'vs/platform/theme/common/colorRegistry'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { Color } from 'vs/base/common/color'; +import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; export class SelectColorThemeAction extends Action { @@ -60,7 +60,7 @@ export class SelectColorThemeAction extends Action { } let target = null; if (applyTheme) { - let confValue = this.configurationService.lookup(COLOR_THEME_SETTING); + let confValue = this.configurationService.inspect(COLOR_THEME_SETTING); target = typeof confValue.workspace !== 'undefined' ? ConfigurationTarget.WORKSPACE : ConfigurationTarget.USER; } @@ -126,7 +126,7 @@ class SelectIconThemeAction extends Action { } let target = null; if (applyTheme) { - let confValue = this.configurationService.lookup(ICON_THEME_SETTING); + let confValue = this.configurationService.inspect(ICON_THEME_SETTING); target = typeof confValue.workspace !== 'undefined' ? ConfigurationTarget.WORKSPACE : ConfigurationTarget.USER; } this.themeService.setFileIconTheme(theme && theme.id, target).done(null, diff --git a/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.ts b/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.ts index 21dbeb9472c..a008c9c954f 100644 --- a/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.ts +++ b/src/vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution.ts @@ -35,7 +35,7 @@ class UnsupportedWorkspaceSettingsContribution implements IWorkbenchContribution @IStorageService private storageService: IStorageService ) { lifecycleService.onShutdown(this.dispose, this); - this.toDispose.push(this.workspaceConfigurationService.onDidUpdateConfiguration(e => this.checkWorkspaceSettings())); + this.toDispose.push(this.workspaceConfigurationService.onDidChangeConfiguration(e => this.checkWorkspaceSettings())); } getId(): string { diff --git a/src/vs/workbench/parts/update/electron-browser/update.ts b/src/vs/workbench/parts/update/electron-browser/update.ts index 9795b43c6ed..9a42bcaf34b 100644 --- a/src/vs/workbench/parts/update/electron-browser/update.ts +++ b/src/vs/workbench/parts/update/electron-browser/update.ts @@ -17,7 +17,7 @@ import pkg from 'vs/platform/node/package'; import product from 'vs/platform/node/product'; import URI from 'vs/base/common/uri'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { IActivityBarService, NumberBadge } from 'vs/workbench/services/activity/common/activityBarService'; +import { IActivityService, NumberBadge } from 'vs/workbench/services/activity/common/activity'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ReleaseNotesInput } from 'vs/workbench/parts/update/electron-browser/releaseNotesInput'; import { IGlobalActivity } from 'vs/workbench/common/activity'; @@ -32,6 +32,7 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag import { IUpdateService, State as UpdateState } from 'vs/platform/update/common/update'; import * as semver from 'semver'; import { OS, isLinux, isWindows } from 'vs/base/common/platform'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; class ApplyUpdateAction extends Action { constructor( @IUpdateService private updateService: IUpdateService) { @@ -267,8 +268,13 @@ export class Win3264BitContribution implements IWorkbenchContribution { @IStorageService storageService: IStorageService, @IInstantiationService instantiationService: IInstantiationService, @IMessageService messageService: IMessageService, - @IWorkbenchEditorService editorService: IWorkbenchEditorService + @IWorkbenchEditorService editorService: IWorkbenchEditorService, + @IEnvironmentService environmentService: IEnvironmentService ) { + if (environmentService.disableUpdates) { + return; + } + const neverShowAgain = new NeverShowAgain(Win3264BitContribution.KEY, storageService); if (!neverShowAgain.shouldShow()) { @@ -323,7 +329,7 @@ export class UpdateContribution implements IGlobalActivity { @IMessageService private messageService: IMessageService, @IUpdateService private updateService: IUpdateService, @IWorkbenchEditorService editorService: IWorkbenchEditorService, - @IActivityBarService private activityBarService: IActivityBarService + @IActivityService private activityService: IActivityService ) { const onUpdateAvailable = isLinux ? mapEvent(updateService.onUpdateAvailable, e => e.version) @@ -363,7 +369,7 @@ export class UpdateContribution implements IGlobalActivity { if (isUpdateAvailable) { const badge = new NumberBadge(1, () => nls.localize('updateIsReady', "New {0} update available.", product.nameShort)); - this.badgeDisposable = this.activityBarService.showActivity(this.id, badge); + this.badgeDisposable = this.activityService.showActivity(this.id, badge); } } diff --git a/src/vs/workbench/parts/watermark/electron-browser/watermark.ts b/src/vs/workbench/parts/watermark/electron-browser/watermark.ts index 5420ab82b04..8acbac70b41 100644 --- a/src/vs/workbench/parts/watermark/electron-browser/watermark.ts +++ b/src/vs/workbench/parts/watermark/electron-browser/watermark.ts @@ -100,6 +100,7 @@ const folderEntries = [ ]; const UNBOUND = nls.localize('watermark.unboundCommand', "unbound"); +const WORKBENCH_TIPS_ENABLED_KEY = 'workbench.tips.enabled'; export class WatermarkContribution implements IWorkbenchContribution { @@ -120,19 +121,21 @@ export class WatermarkContribution implements IWorkbenchContribution { lifecycleService.onShutdown(this.dispose, this); this.partService.joinCreation().then(() => { - this.enabled = this.configurationService.lookup('workbench.tips.enabled').value; + this.enabled = this.configurationService.getValue(WORKBENCH_TIPS_ENABLED_KEY); if (this.enabled) { this.create(); } }); - this.toDispose.push(this.configurationService.onDidUpdateConfiguration(e => { - const enabled = this.configurationService.lookup('workbench.tips.enabled').value; - if (enabled !== this.enabled) { - this.enabled = enabled; - if (this.enabled) { - this.create(); - } else { - this.destroy(); + this.toDispose.push(this.configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration(WORKBENCH_TIPS_ENABLED_KEY)) { + const enabled = this.configurationService.getValue(WORKBENCH_TIPS_ENABLED_KEY); + if (enabled !== this.enabled) { + this.enabled = enabled; + if (this.enabled) { + this.create(); + } else { + this.destroy(); + } } } })); 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 93be1d9e534..dff17826c3b 100644 --- a/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts +++ b/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts @@ -19,8 +19,7 @@ import { onUnexpectedError, isPromiseCanceledError } from 'vs/base/common/errors import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows'; import { TPromise } from 'vs/base/common/winjs.base'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; +import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { localize } from 'vs/nls'; import { Action } from 'vs/base/common/actions'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -83,9 +82,9 @@ export class WelcomePageContribution implements IWorkbenchContribution { } function isWelcomePageEnabled(configurationService: IConfigurationService) { - const startupEditor = configurationService.lookup(configurationKey); + const startupEditor = configurationService.inspect(configurationKey); if (!startupEditor.user && !startupEditor.workspace) { - const welcomeEnabled = configurationService.lookup(oldConfigurationKey); + const welcomeEnabled = configurationService.inspect(oldConfigurationKey); if (welcomeEnabled.value !== undefined && welcomeEnabled.value !== null) { return welcomeEnabled.value; } @@ -237,7 +236,6 @@ class WelcomePage { @IWindowsService private windowsService: IWindowsService, @IWorkspaceContextService private contextService: IWorkspaceContextService, @IConfigurationService private configurationService: IConfigurationService, - @IConfigurationEditingService private configurationEditingService: IConfigurationEditingService, @IEnvironmentService private environmentService: IEnvironmentService, @IMessageService private messageService: IMessageService, @IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService, @@ -279,7 +277,7 @@ class WelcomePage { showOnStartup.setAttribute('checked', 'checked'); } showOnStartup.addEventListener('click', e => { - this.configurationEditingService.writeConfiguration(ConfigurationTarget.USER, { key: configurationKey, value: showOnStartup.checked ? 'welcomePage' : 'newUntitledFile' }); + this.configurationService.updateValue(configurationKey, showOnStartup.checked ? 'welcomePage' : 'newUntitledFile', ConfigurationTarget.USER); }); recentlyOpened.then(({ workspaces }) => { diff --git a/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.ts b/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.ts index 3795d6aabd1..84757bd9881 100644 --- a/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.ts +++ b/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.ts @@ -285,7 +285,7 @@ export class WalkThroughPart extends BaseEditor { } private getArrowScrollHeight() { - let fontSize = this.configurationService.lookup('editor.fontSize').value; + let fontSize = this.configurationService.getValue('editor.fontSize'); if (typeof fontSize !== 'number' || fontSize < 1) { fontSize = 12; } @@ -401,7 +401,7 @@ export class WalkThroughPart extends BaseEditor { } })); - this.contentDisposables.push(this.configurationService.onDidUpdateConfiguration(() => { + this.contentDisposables.push(this.configurationService.onDidChangeConfiguration(() => { if (snippet.textEditorModel) { editor.updateOptions(this.getEditorOptions(snippet.textEditorModel.getModeId())); } @@ -452,7 +452,11 @@ export class WalkThroughPart extends BaseEditor { }); this.updateSizeClasses(); this.multiCursorModifier(); - this.contentDisposables.push(this.configurationService.onDidUpdateConfiguration(() => this.multiCursorModifier())); + this.contentDisposables.push(this.configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('editor.multiCursorModifier')) { + this.multiCursorModifier(); + } + })); if (input.onReady) { input.onReady(innerContent); } @@ -510,8 +514,8 @@ export class WalkThroughPart extends BaseEditor { private multiCursorModifier() { const labels = UILabelProvider.modifierLabels[OS]; - const setting = this.configurationService.lookup('editor.multiCursorModifier'); - const modifier = labels[setting.value === 'ctrlCmd' ? (OS === OperatingSystem.Macintosh ? 'metaKey' : 'ctrlKey') : 'altKey']; + const value = this.configurationService.getValue('editor.multiCursorModifier'); + const modifier = labels[value === 'ctrlCmd' ? (OS === OperatingSystem.Macintosh ? 'metaKey' : 'ctrlKey') : 'altKey']; const keys = this.content.querySelectorAll('.multi-cursor-modifier'); Array.prototype.forEach.call(keys, (key: Element) => { while (key.firstChild) { diff --git a/src/vs/workbench/services/activity/browser/activityService.ts b/src/vs/workbench/services/activity/browser/activityService.ts new file mode 100644 index 00000000000..4f328baa8ae --- /dev/null +++ b/src/vs/workbench/services/activity/browser/activityService.ts @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { IPanelService } from 'vs/workbench/services/panel/common/panelService'; +import { ActivitybarPart } from 'vs/workbench/browser/parts/activitybar/activitybarPart'; +import { PanelPart } from 'vs/workbench/browser/parts/panel/panelPart'; +import { IActivityService, IBadge } from 'vs/workbench/services/activity/common/activity'; +import { IDisposable } from 'vs/base/common/lifecycle'; + +export class ActivityService implements IActivityService { + + public _serviceBrand: any; + + constructor( + private activitybarPart: ActivitybarPart, + private panelPart: PanelPart, + @IPanelService private panelService: IPanelService + ) { } + + public showActivity(compositeOrActionId: string, badge: IBadge, clazz?: string): IDisposable { + if (this.panelService.getPanels().filter(p => p.id === compositeOrActionId).length) { + return this.panelPart.showActivity(compositeOrActionId, badge, clazz); + } + + return this.activitybarPart.showActivity(compositeOrActionId, badge, clazz); + } +} diff --git a/src/vs/workbench/services/activity/common/activityBarService.ts b/src/vs/workbench/services/activity/common/activity.ts similarity index 67% rename from src/vs/workbench/services/activity/common/activityBarService.ts rename to src/vs/workbench/services/activity/common/activity.ts index 35732c5c1c4..e2f5286ba2e 100644 --- a/src/vs/workbench/services/activity/common/activityBarService.ts +++ b/src/vs/workbench/services/activity/common/activity.ts @@ -58,33 +58,13 @@ export class IconBadge extends BaseBadge { export class ProgressBadge extends BaseBadge { } -export const IActivityBarService = createDecorator('activityBarService'); +export const IActivityService = createDecorator('activityService'); -export interface IActivityBarService { +export interface IActivityService { _serviceBrand: any; /** - * Show activity in the activitybar for the given viewlet or global action. + * Show activity in the panel for the given panel or in the activitybar for the given viewlet or global action. */ - showActivity(viewletOrActionId: string, badge: IBadge, clazz?: string): IDisposable; - - /** - * Unpins a viewlet from the activitybar. - */ - unpin(viewletId: string): void; - - /** - * Pin a viewlet inside the activity bar. - */ - pin(viewletId: string): void; - - /** - * Find out if a viewlet is pinned in the activity bar. - */ - isPinned(viewletId: string): boolean; - - /** - * Reorder viewlet ordering by moving a viewlet to the location of another viewlet. - */ - move(viewletId: string, toViewletId: string): void; + showActivity(compositeOrActionId: string, badge: IBadge, clazz?: string): IDisposable; } diff --git a/src/vs/workbench/services/backup/test/node/backupFileService.test.ts b/src/vs/workbench/services/backup/test/node/backupFileService.test.ts index 65a97a110a8..23420f77df0 100644 --- a/src/vs/workbench/services/backup/test/node/backupFileService.test.ts +++ b/src/vs/workbench/services/backup/test/node/backupFileService.test.ts @@ -19,7 +19,7 @@ import { FileService } from 'vs/workbench/services/files/node/fileService'; import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; import { parseArgs } from 'vs/platform/environment/node/argv'; import { RawTextSource } from 'vs/editor/common/model/textSource'; -import { TestContextService, TestTextResourceConfigurationService } from 'vs/workbench/test/workbenchTestServices'; +import { TestContextService, TestTextResourceConfigurationService, getRandomTestPath } from 'vs/workbench/test/workbenchTestServices'; import { Workspace, toWorkspaceFolders } from 'vs/platform/workspace/common/workspace'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; @@ -34,7 +34,7 @@ class TestEnvironmentService extends EnvironmentService { get backupWorkspacesPath(): string { return this._backupWorkspacesPath; } } -const parentDir = path.join(os.tmpdir(), 'vsctests', 'service'); +const parentDir = getRandomTestPath(os.tmpdir(), 'vsctests', 'backupfileservice'); const backupHome = path.join(parentDir, 'Backups'); const workspacesJsonPath = path.join(backupHome, 'workspaces.json'); diff --git a/src/vs/workbench/services/configuration/common/configuration.ts b/src/vs/workbench/services/configuration/common/configuration.ts index 28c1b711fed..3d4eb1394b2 100644 --- a/src/vs/workbench/services/configuration/common/configuration.ts +++ b/src/vs/workbench/services/configuration/common/configuration.ts @@ -13,14 +13,17 @@ export const WORKSPACE_CONFIG_DEFAULT_PATH = `${WORKSPACE_CONFIG_FOLDER_DEFAULT_ export const IWorkspaceConfigurationService = createDecorator('configurationService'); export interface IWorkspaceConfigurationService extends IConfigurationService { - /** * Returns untrusted configuration keys for the current workspace. */ getUnsupportedWorkspaceKeys(): string[]; - } +export const defaultSettingsSchemaId = 'vscode://schemas/settings/default'; +export const userSettingsSchemaId = 'vscode://schemas/settings/user'; +export const workspaceSettingsSchemaId = 'vscode://schemas/settings/workspace'; +export const folderSettingsSchemaId = 'vscode://schemas/settings/folder'; + export const TASKS_CONFIGURATION_KEY = 'tasks'; export const LAUNCH_CONFIGURATION_KEY = 'launch'; diff --git a/src/vs/workbench/services/configuration/common/configurationEditing.ts b/src/vs/workbench/services/configuration/common/configurationEditing.ts deleted file mode 100644 index 5cd14ca3229..00000000000 --- a/src/vs/workbench/services/configuration/common/configurationEditing.ts +++ /dev/null @@ -1,104 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; - -import { TPromise } from 'vs/base/common/winjs.base'; -import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; -import { IConfigurationOverrides } from 'vs/platform/configuration/common/configuration'; - -export const IConfigurationEditingService = createDecorator('configurationEditingService'); - -export enum ConfigurationEditingErrorCode { - - /** - * Error when trying to write a configuration key that is not registered. - */ - ERROR_UNKNOWN_KEY, - - /** - * Error when trying to write an invalid folder configuration key to folder settings. - */ - ERROR_INVALID_FOLDER_CONFIGURATION, - - /** - * Error when trying to write to user target but not supported for provided key. - */ - ERROR_INVALID_USER_TARGET, - - /** - * Error when trying to write a configuration key to folder target - */ - ERROR_INVALID_FOLDER_TARGET, - - /** - * Error when trying to write to the workspace configuration without having a workspace opened. - */ - ERROR_NO_WORKSPACE_OPENED, - - /** - * Error when trying to write and save to the configuration file while it is dirty in the editor. - */ - ERROR_CONFIGURATION_FILE_DIRTY, - - /** - * Error when trying to write to a configuration file that contains JSON errors. - */ - ERROR_INVALID_CONFIGURATION -} - -export class ConfigurationEditingError extends Error { - constructor(message: string, public code: ConfigurationEditingErrorCode) { - super(message); - } -} - -export enum ConfigurationTarget { - - /** - * Targets the user configuration file for writing. - */ - USER, - - /** - * Targets the workspace configuration file for writing. This only works if a workspace is opened. - */ - WORKSPACE, - - /** - * Targets the folder configuration file for writing. This only works if a workspace is opened. - */ - FOLDER -} - -export interface IConfigurationValue { - key: string; - value: any; -} - -export interface IConfigurationEditingOptions { - /** - * If `true`, do not saves the configuration. Default is `false`. - */ - donotSave?: boolean; - /** - * If `true`, do not notifies the error to user by showing the message box. Default is `false`. - */ - donotNotifyError?: boolean; - /** - * Scope of configuration to be written into. - */ - scopes?: IConfigurationOverrides; -} - -export interface IConfigurationEditingService { - - _serviceBrand: ServiceIdentifier; - - /** - * Allows to write the configuration value to either the user or workspace configuration file and save it if asked to save. - * The returned promise will be in error state in any of the error cases from [ConfigurationEditingErrorCode](#ConfigurationEditingErrorCode) - */ - writeConfiguration(target: ConfigurationTarget, value: IConfigurationValue, options?: IConfigurationEditingOptions): TPromise; -} \ No newline at end of file diff --git a/src/vs/workbench/services/configuration/common/configurationExtensionPoint.ts b/src/vs/workbench/services/configuration/common/configurationExtensionPoint.ts new file mode 100644 index 00000000000..7614f64f2e3 --- /dev/null +++ b/src/vs/workbench/services/configuration/common/configurationExtensionPoint.ts @@ -0,0 +1,212 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import * as objects from 'vs/base/common/objects'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import { ExtensionsRegistry, ExtensionMessageCollector } from 'vs/platform/extensions/common/extensionsRegistry'; +import { IConfigurationNode, IConfigurationRegistry, Extensions, editorConfigurationSchemaId, IDefaultConfigurationExtension, validateProperty, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; +import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; +import { workspaceSettingsSchemaId } from 'vs/workbench/services/configuration/common/configuration'; + +const configurationRegistry = Registry.as(Extensions.Configuration); + +const configurationEntrySchema: IJSONSchema = { + type: 'object', + defaultSnippets: [{ body: { title: '', properties: {} } }], + properties: { + title: { + description: nls.localize('vscode.extension.contributes.configuration.title', 'A summary of the settings. This label will be used in the settings file as separating comment.'), + type: 'string' + }, + properties: { + description: nls.localize('vscode.extension.contributes.configuration.properties', 'Description of the configuration properties.'), + type: 'object', + additionalProperties: { + anyOf: [ + { $ref: 'http://json-schema.org/draft-04/schema#' }, + { + type: 'object', + properties: { + isExecutable: { + type: 'boolean' + }, + scope: { + type: 'string', + enum: ['window', 'resource'], + default: 'window', + enumDescriptions: [ + nls.localize('scope.window.description', "Window specific configuration, which can be configured in the User or Workspace settings."), + nls.localize('scope.resource.description', "Resource specific configuration, which can be configured in the User, Workspace or Folder settings.") + ], + description: nls.localize('scope.description', "Scope in which the configuration is applicable. Available scopes are `window` and `resource`.") + } + } + } + ] + } + } + } +}; + + +// BEGIN VSCode extension point `configuration` +const configurationExtPoint = ExtensionsRegistry.registerExtensionPoint('configuration', [], { + description: nls.localize('vscode.extension.contributes.configuration', 'Contributes configuration settings.'), + oneOf: [ + configurationEntrySchema, + { + type: 'array', + items: configurationEntrySchema + } + ] +}); +configurationExtPoint.setHandler(extensions => { + const configurations: IConfigurationNode[] = []; + + function handleConfiguration(node: IConfigurationNode, id: string, collector: ExtensionMessageCollector) { + let configuration = objects.clone(node); + + if (configuration.title && (typeof configuration.title !== 'string')) { + collector.error(nls.localize('invalid.title', "'configuration.title' must be a string")); + } + + validateProperties(configuration, collector); + + configuration.id = id; + configurations.push(configuration); + }; + + for (let extension of extensions) { + const collector = extension.collector; + const value = extension.value; + const id = extension.description.id; + if (!Array.isArray(value)) { + handleConfiguration(value, id, collector); + } else { + value.forEach(v => handleConfiguration(v, id, collector)); + } + } + configurationRegistry.registerConfigurations(configurations, false); +}); +// END VSCode extension point `configuration` + +// BEGIN VSCode extension point `configurationDefaults` +const defaultConfigurationExtPoint = ExtensionsRegistry.registerExtensionPoint('configurationDefaults', [], { + description: nls.localize('vscode.extension.contributes.defaultConfiguration', 'Contributes default editor configuration settings by language.'), + type: 'object', + defaultSnippets: [{ body: {} }], + patternProperties: { + '\\[.*\\]$': { + type: 'object', + default: {}, + $ref: editorConfigurationSchemaId, + } + } +}); +defaultConfigurationExtPoint.setHandler(extensions => { + const defaultConfigurations: IDefaultConfigurationExtension[] = extensions.map(extension => { + const id = extension.description.id; + const name = extension.description.name; + const defaults = objects.clone(extension.value); + return { + id, name, defaults + }; + }); + configurationRegistry.registerDefaultConfigurations(defaultConfigurations); +}); +// END VSCode extension point `configurationDefaults` + +function validateProperties(configuration: IConfigurationNode, collector: ExtensionMessageCollector): void { + let properties = configuration.properties; + if (properties) { + if (typeof properties !== 'object') { + collector.error(nls.localize('invalid.properties', "'configuration.properties' must be an object")); + configuration.properties = {}; + } + for (let key in properties) { + 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]; + } + } + } + let subNodes = configuration.allOf; + if (subNodes) { + collector.error(nls.localize('invalid.allOf', "'configuration.allOf' is deprecated and should no longer be used. Instead, pass multiple configuration sections as an array to the 'configuration' contribution point.")); + for (let node of subNodes) { + validateProperties(node, collector); + } + } +} + +const jsonRegistry = Registry.as(JSONExtensions.JSONContribution); +jsonRegistry.registerSchema('vscode://schemas/workspaceConfig', { + default: { + folders: [ + { + path: '' + } + ], + settings: { + } + }, + required: ['folders'], + properties: { + 'folders': { + minItems: 0, + uniqueItems: true, + description: nls.localize('workspaceConfig.folders.description', "List of folders to be loaded in the workspace."), + items: { + type: 'object', + default: { path: '' }, + 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. ") + } + }, + 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: workspaceSettingsSchemaId + }, + 'extensions': { + type: 'object', + default: {}, + description: nls.localize('workspaceConfig.extensions.description', "Workspace extensions"), + $ref: 'vscode://schemas/extensions' + } + }, + additionalProperties: false, + errorMessage: nls.localize('unknownWorkspaceProperty', "Unknown workspace configuration property") +}); \ No newline at end of file diff --git a/src/vs/workbench/services/configuration/common/configurationModels.ts b/src/vs/workbench/services/configuration/common/configurationModels.ts index dd7f73103ed..033e039225b 100644 --- a/src/vs/workbench/services/configuration/common/configurationModels.ts +++ b/src/vs/workbench/services/configuration/common/configurationModels.ts @@ -4,22 +4,26 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { clone } from 'vs/base/common/objects'; -import { CustomConfigurationModel, toValuesTree } from 'vs/platform/configuration/common/model'; -import { ConfigurationModel } from 'vs/platform/configuration/common/configuration'; +import { clone, equals } from 'vs/base/common/objects'; +import { compare, toValuesTree, IConfigurationChangeEvent, ConfigurationTarget, IConfigurationModel, IConfigurationOverrides } from 'vs/platform/configuration/common/configuration'; +import { ConfigurationModel, Configuration as BaseConfiguration, CustomConfigurationModel, ConfigurationChangeEvent } from 'vs/platform/configuration/common/configurationModels'; import { Registry } from 'vs/platform/registry/common/platform'; import { IConfigurationRegistry, IConfigurationPropertySchema, Extensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; import { WORKSPACE_STANDALONE_CONFIGURATIONS } from 'vs/workbench/services/configuration/common/configuration'; import { IStoredWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces'; +import { Workspace } from 'vs/platform/workspace/common/workspace'; +import { StrictResourceMap } from 'vs/base/common/map'; +import URI from 'vs/base/common/uri'; +import { distinct } from 'vs/base/common/arrays'; -export class WorkspaceConfigurationModel extends CustomConfigurationModel { +export class WorkspaceConfigurationModel extends CustomConfigurationModel { - private _raw: T; + private _raw: any; private _folders: IStoredWorkspaceFolder[]; - private _worksapaceSettings: ConfigurationModel; - private _tasksConfiguration: ConfigurationModel; - private _launchConfiguration: ConfigurationModel; - private _workspaceConfiguration: ConfigurationModel; + private _worksapaceSettings: ConfigurationModel; + private _tasksConfiguration: ConfigurationModel; + private _launchConfiguration: ConfigurationModel; + private _workspaceConfiguration: ConfigurationModel; public update(content: string): void { super.update(content); @@ -31,11 +35,11 @@ export class WorkspaceConfigurationModel extends CustomConfigurationModel return this._folders; } - get workspaceConfiguration(): ConfigurationModel { + get workspaceConfiguration(): ConfigurationModel { return this._workspaceConfiguration; } - protected processRaw(raw: T): void { + protected processRaw(raw: any): void { this._raw = raw; this._folders = (this._raw['folders'] || []) as IStoredWorkspaceFolder[]; @@ -46,27 +50,27 @@ export class WorkspaceConfigurationModel extends CustomConfigurationModel super.processRaw(raw); } - private parseConfigurationModel(section: string): ConfigurationModel { + private parseConfigurationModel(section: string): ConfigurationModel { const rawSection = this._raw[section] || {}; const contents = toValuesTree(rawSection, message => console.error(`Conflict in section '${section}' of workspace configuration file ${message}`)); - return new ConfigurationModel(contents, Object.keys(rawSection)); + return new ConfigurationModel(contents, Object.keys(rawSection)); } - private consolidate(): ConfigurationModel { + private consolidate(): ConfigurationModel { const keys: string[] = [...this._worksapaceSettings.keys, ...this._tasksConfiguration.keys.map(key => `tasks.${key}`), ...this._launchConfiguration.keys.map(key => `launch.${key}`)]; - const mergedContents = new ConfigurationModel({}, keys) + const mergedContents = new ConfigurationModel({}, keys) .merge(this._worksapaceSettings) .merge(this._tasksConfiguration) .merge(this._launchConfiguration); - return new ConfigurationModel(mergedContents.contents, keys, mergedContents.overrides); + return new ConfigurationModel(mergedContents.contents, keys, mergedContents.overrides); } } -export class ScopedConfigurationModel extends CustomConfigurationModel { +export class ScopedConfigurationModel extends CustomConfigurationModel { constructor(content: string, name: string, public readonly scope: string) { super(null, name); @@ -82,14 +86,14 @@ export class ScopedConfigurationModel extends CustomConfigurationModel { } -export class FolderSettingsModel extends CustomConfigurationModel { +export class FolderSettingsModel extends CustomConfigurationModel { - private _raw: T; + private _raw: any; private _unsupportedKeys: string[]; - protected processRaw(raw: T): void { + protected processRaw(raw: any): void { this._raw = raw; - const processedRaw = {}; + const processedRaw = {}; this._unsupportedKeys = []; const configurationProperties = Registry.as(Extensions.Configuration).getConfigurationProperties(); for (let key in raw) { @@ -118,16 +122,16 @@ export class FolderSettingsModel extends CustomConfigurationModel { return !propertySchema.isExecutable; } - public createWorkspaceConfigurationModel(): ConfigurationModel { + public createWorkspaceConfigurationModel(): ConfigurationModel { return this.createScopedConfigurationModel(ConfigurationScope.WINDOW); } - public createFolderScopedConfigurationModel(): ConfigurationModel { + public createFolderScopedConfigurationModel(): ConfigurationModel { return this.createScopedConfigurationModel(ConfigurationScope.RESOURCE); } - private createScopedConfigurationModel(scope: ConfigurationScope): ConfigurationModel { - const workspaceRaw = {}; + private createScopedConfigurationModel(scope: ConfigurationScope): ConfigurationModel { + const workspaceRaw = {}; const configurationProperties = Registry.as(Extensions.Configuration).getConfigurationProperties(); for (let key in this._raw) { if (this.getScope(key, configurationProperties) === scope) { @@ -145,15 +149,15 @@ export class FolderSettingsModel extends CustomConfigurationModel { } } -export class FolderConfigurationModel extends CustomConfigurationModel { +export class FolderConfigurationModel extends CustomConfigurationModel { - constructor(public readonly workspaceSettingsConfig: FolderSettingsModel, private scopedConfigs: ScopedConfigurationModel[], private scope: ConfigurationScope) { + constructor(public readonly workspaceSettingsConfig: FolderSettingsModel, private scopedConfigs: ScopedConfigurationModel[], private scope: ConfigurationScope) { super(); this.consolidate(); } private consolidate(): void { - this._contents = {}; + this._contents = {}; this._overrides = []; this.doMerge(this, ConfigurationScope.WINDOW === this.scope ? this.workspaceSettingsConfig : this.workspaceSettingsConfig.createFolderScopedConfigurationModel()); @@ -178,4 +182,185 @@ export class FolderConfigurationModel extends CustomConfigurationModel { this.workspaceSettingsConfig.reprocess(); this.consolidate(); } +} + +export class Configuration extends BaseConfiguration { + + constructor( + defaults: ConfigurationModel, + user: ConfigurationModel, + workspaceConfiguration: ConfigurationModel, + protected folders: StrictResourceMap, + memoryConfiguration: ConfigurationModel, + memoryConfigurationByResource: StrictResourceMap, + private readonly _workspace: Workspace) { + super(defaults, user, workspaceConfiguration, folders, memoryConfiguration, memoryConfigurationByResource); + } + + getSection(section: string = '', overrides: IConfigurationOverrides = {}): C { + return super.getSection(section, overrides, this._workspace); + } + + getValue(key: string, overrides: IConfigurationOverrides = {}): any { + return super.getValue(key, overrides, this._workspace); + } + + lookup(key: string, overrides: IConfigurationOverrides = {}): { + default: C, + user: C, + workspace: C, + workspaceFolder: C + memory?: C + value: C, + } { + return super.lookup(key, overrides, this._workspace); + } + + keys(): { + default: string[]; + user: string[]; + workspace: string[]; + workspaceFolder: string[]; + } { + return super.keys(this._workspace); + } + + updateDefaultConfiguration(defaults: ConfigurationModel): void { + this._defaults = defaults; + this.merge(); + } + + updateUserConfiguration(user: ConfigurationModel): ConfigurationChangeEvent { + const { added, updated, removed } = compare(this._user, user); + let changedKeys = [...added, ...updated, ...removed]; + if (changedKeys.length) { + const oldConfiguartion = new Configuration(this._defaults, this._user, this._workspaceConfiguration, this.folders, this._memoryConfiguration, this._memoryConfigurationByResource, this._workspace); + + this._user = user; + this.merge(); + + changedKeys = changedKeys.filter(key => !equals(oldConfiguartion.getValue(key), this.getValue(key))); + } + return new ConfigurationChangeEvent().change(changedKeys); + } + + updateWorkspaceConfiguration(workspaceConfiguration: ConfigurationModel): ConfigurationChangeEvent { + const { added, updated, removed } = compare(this._workspaceConfiguration, workspaceConfiguration); + let changedKeys = [...added, ...updated, ...removed]; + if (changedKeys.length) { + const oldConfiguartion = new Configuration(this._defaults, this._user, this._workspaceConfiguration, this.folders, this._memoryConfiguration, this._memoryConfigurationByResource, this._workspace); + + this._workspaceConfiguration = workspaceConfiguration; + this.merge(); + + changedKeys = changedKeys.filter(key => !equals(oldConfiguartion.getValue(key), this.getValue(key))); + } + return new ConfigurationChangeEvent().change(changedKeys); + } + + updateFolderConfiguration(resource: URI, configuration: FolderConfigurationModel): ConfigurationChangeEvent { + const currentFolderConfiguration = this.folders.get(resource); + + if (currentFolderConfiguration) { + const { added, updated, removed } = compare(currentFolderConfiguration, configuration); + let changedKeys = [...added, ...updated, ...removed]; + if (changedKeys.length) { + const oldConfiguartion = new Configuration(this._defaults, this._user, this._workspaceConfiguration, this.folders, this._memoryConfiguration, this._memoryConfigurationByResource, this._workspace); + + this.folders.set(resource, configuration); + this.mergeFolder(resource); + + changedKeys = changedKeys.filter(key => !equals(oldConfiguartion.getValue(key, { resource }), this.getValue(key, { resource }))); + } + return new ConfigurationChangeEvent().change(changedKeys, resource); + } + + this.folders.set(resource, configuration); + this.mergeFolder(resource); + return new ConfigurationChangeEvent().change(configuration.keys, resource); + } + + deleteFolderConfiguration(folder: URI): ConfigurationChangeEvent { + if (this._workspace && this._workspace.folders.length > 0 && this._workspace.folders[0].uri.toString() === folder.toString()) { + // Do not remove workspace configuration + return new ConfigurationChangeEvent(); + } + + const keys = this.folders.get(folder).keys; + this.folders.delete(folder); + this._foldersConsolidatedConfigurations.delete(folder); + return new ConfigurationChangeEvent().change(keys, folder); + } + + getFolderConfigurationModel(folder: URI): FolderConfigurationModel { + return this.folders.get(folder); + } + + compare(other: Configuration): string[] { + let from = other.allKeys(); + let to = this.allKeys(); + + const added = to.filter(key => from.indexOf(key) === -1); + const removed = from.filter(key => to.indexOf(key) === -1); + const updated = []; + + for (const key of from) { + const value1 = this.getValue(key); + const value2 = other.getValue(key); + if (!equals(value1, value2)) { + updated.push(key); + } + } + + return [...added, ...removed, ...updated]; + } + + allKeys(): string[] { + let keys = this.keys(); + let all = [...keys.default, ...keys.user, ...keys.workspace]; + for (const resource of this.folders.keys()) { + all.push(...this.folders.get(resource).keys); + } + return distinct(all); + } +} + +export class WorkspaceConfigurationChangeEvent implements IConfigurationChangeEvent { + + constructor(private configurationChangeEvent: IConfigurationChangeEvent, private workspace: Workspace) { } + + get changedConfiguration(): IConfigurationModel { + return this.configurationChangeEvent.changedConfiguration; + } + + get changedConfigurationByResource(): StrictResourceMap { + return this.configurationChangeEvent.changedConfigurationByResource; + } + + get affectedKeys(): string[] { + return this.configurationChangeEvent.affectedKeys; + } + + get source(): ConfigurationTarget { + return this.configurationChangeEvent.source; + } + + get sourceConfig(): any { + return this.configurationChangeEvent.sourceConfig; + } + + affectsConfiguration(config: string, resource?: URI): boolean { + if (this.configurationChangeEvent.affectsConfiguration(config, resource)) { + return true; + } + + if (resource && this.workspace) { + let workspaceFolder = this.workspace.getFolder(resource); + if (workspaceFolder) { + return this.configurationChangeEvent.affectsConfiguration(config, workspaceFolder.uri); + } + } + + return false; + } } \ No newline at end of file diff --git a/src/vs/workbench/services/configuration/node/configuration.ts b/src/vs/workbench/services/configuration/node/configuration.ts index 74556f8b5ab..f19c0052711 100644 --- a/src/vs/workbench/services/configuration/node/configuration.ts +++ b/src/vs/workbench/services/configuration/node/configuration.ts @@ -2,48 +2,28 @@ * 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 * 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 * as objects from 'vs/base/common/objects'; +import { readFile } from 'vs/base/node/pfs'; import * as errors from 'vs/base/common/errors'; import * as collections from 'vs/base/common/collections'; import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; 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, 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'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { CustomConfigurationModel } from 'vs/platform/configuration/common/model'; +import { CustomConfigurationModel, ConfigurationModel } from 'vs/platform/configuration/common/configurationModels'; import { WorkspaceConfigurationModel, ScopedConfigurationModel, FolderConfigurationModel, FolderSettingsModel } from 'vs/workbench/services/configuration/common/configurationModels'; -import { IConfigurationServiceEvent, ConfigurationSource, IConfigurationKeys, IConfigurationValue, ConfigurationModel, IConfigurationOverrides, Configuration as BaseConfiguration, IConfigurationValues, IConfigurationData } from 'vs/platform/configuration/common/configuration'; -import { IWorkspaceConfigurationService, WORKSPACE_CONFIG_FOLDER_DEFAULT_NAME, WORKSPACE_STANDALONE_CONFIGURATIONS, WORKSPACE_CONFIG_DEFAULT_PATH, TASKS_CONFIGURATION_KEY, LAUNCH_CONFIGURATION_KEY } from 'vs/workbench/services/configuration/common/configuration'; -import { ConfigurationService as GlobalConfigurationService } from 'vs/platform/configuration/node/configurationService'; -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, settingsSchema, resourceSettingsSchema } from 'vs/platform/configuration/common/configurationRegistry'; -import { createHash } from 'crypto'; -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'; -import { ICommandService } from 'vs/platform/commands/common/commands'; -import product from 'vs/platform/node/product'; -import pkg from 'vs/platform/node/package'; +import { WORKSPACE_STANDALONE_CONFIGURATIONS, WORKSPACE_CONFIG_DEFAULT_PATH, TASKS_CONFIGURATION_KEY, LAUNCH_CONFIGURATION_KEY } from 'vs/workbench/services/configuration/common/configuration'; +import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; +import { IStoredWorkspace, IStoredWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces'; +import * as extfs from 'vs/base/node/extfs'; +import { JSONEditingService } from 'vs/workbench/services/configuration/node/jsonEditingService'; -const defaultSettingsSchemaId = 'vscode://schemas/settings/default'; -const userSettingsSchemaId = 'vscode://schemas/settings/user'; -const workspaceSettingsSchemaId = 'vscode://schemas/settings/workspace'; -const folderSettingsSchemaId = 'vscode://schemas/settings/folder'; +// node.hs helper functions interface IStat { resource: URI; @@ -56,581 +36,57 @@ interface IContent { value: string; } -interface IWorkspaceConfiguration { - workspace: T; - consolidated: any; +function resolveContents(resources: URI[]): TPromise { + const contents: IContent[] = []; + + return TPromise.join(resources.map(resource => { + return resolveContent(resource).then(content => { + contents.push(content); + }); + })).then(() => contents); } -type IWorkspaceFoldersConfiguration = { [rootFolder: string]: { folders: string[]; } }; +function resolveContent(resource: URI): TPromise { + return readFile(resource.fsPath).then(contents => ({ resource, value: contents.toString() })); +} -const configurationRegistry = Registry.as(Extensions.Configuration); - -const configurationEntrySchema: IJSONSchema = { - type: 'object', - defaultSnippets: [{ body: { title: '', properties: {} } }], - properties: { - title: { - description: nls.localize('vscode.extension.contributes.configuration.title', 'A summary of the settings. This label will be used in the settings file as separating comment.'), - type: 'string' - }, - properties: { - description: nls.localize('vscode.extension.contributes.configuration.properties', 'Description of the configuration properties.'), - type: 'object', - additionalProperties: { - anyOf: [ - { $ref: 'http://json-schema.org/draft-04/schema#' }, - { - type: 'object', - properties: { - isExecutable: { - type: 'boolean' - }, - scope: { - type: 'string', - enum: ['window', 'resource'], - default: 'window', - enumDescriptions: [ - nls.localize('scope.window.description', "Window specific configuration, which can be configured in the User or Workspace settings."), - nls.localize('scope.resource.description', "Resource specific configuration, which can be configured in the User, Workspace or Folder settings.") - ], - description: nls.localize('scope.description', "Scope in which the configuration is applicable. Available scopes are `window` and `resource`.") - } - } - } - ] +function resolveStat(resource: URI): TPromise { + return new TPromise((c, e) => { + extfs.readdir(resource.fsPath, (error, children) => { + if (error) { + if ((error).code === 'ENOTDIR') { + c({ resource }); + } else { + e(error); + } + } else { + c({ + resource, + isDirectory: true, + children: children.map(child => { return { resource: URI.file(paths.join(resource.fsPath, child)) }; }) + }); } - } - } -}; - - -// BEGIN VSCode extension point `configuration` -const configurationExtPoint = ExtensionsRegistry.registerExtensionPoint('configuration', [], { - description: nls.localize('vscode.extension.contributes.configuration', 'Contributes configuration settings.'), - oneOf: [ - configurationEntrySchema, - { - type: 'array', - items: configurationEntrySchema - } - ] -}); -configurationExtPoint.setHandler(extensions => { - const configurations: IConfigurationNode[] = []; - - function handleConfiguration(node: IConfigurationNode, id: string, collector: ExtensionMessageCollector) { - let configuration = objects.clone(node); - - if (configuration.title && (typeof configuration.title !== 'string')) { - collector.error(nls.localize('invalid.title', "'configuration.title' must be a string")); - } - - validateProperties(configuration, collector); - - configuration.id = id; - configurations.push(configuration); - }; - - for (let extension of extensions) { - const collector = extension.collector; - const value = extension.value; - const id = extension.description.id; - if (!Array.isArray(value)) { - handleConfiguration(value, id, collector); - } else { - value.forEach(v => handleConfiguration(v, id, collector)); - } - } - configurationRegistry.registerConfigurations(configurations, false); -}); -// END VSCode extension point `configuration` - -// BEGIN VSCode extension point `configurationDefaults` -const defaultConfigurationExtPoint = ExtensionsRegistry.registerExtensionPoint('configurationDefaults', [], { - description: nls.localize('vscode.extension.contributes.defaultConfiguration', 'Contributes default editor configuration settings by language.'), - type: 'object', - defaultSnippets: [{ body: {} }], - patternProperties: { - '\\[.*\\]$': { - type: 'object', - default: {}, - $ref: editorConfigurationSchemaId, - } - } -}); -defaultConfigurationExtPoint.setHandler(extensions => { - const defaultConfigurations: IDefaultConfigurationExtension[] = extensions.map(extension => { - const id = extension.description.id; - const name = extension.description.name; - const defaults = objects.clone(extension.value); - return { - id, name, defaults - }; + }); }); - configurationRegistry.registerDefaultConfigurations(defaultConfigurations); -}); -// END VSCode extension point `configurationDefaults` - -function validateProperties(configuration: IConfigurationNode, collector: ExtensionMessageCollector): void { - let properties = configuration.properties; - if (properties) { - if (typeof properties !== 'object') { - collector.error(nls.localize('invalid.properties', "'configuration.properties' must be an object")); - configuration.properties = {}; - } - for (let key in properties) { - 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]; - } - } - } - let subNodes = configuration.allOf; - if (subNodes) { - collector.error(nls.localize('invalid.allOf', "'configuration.allOf' is deprecated and should no longer be used. Instead, pass multiple configuration sections as an array to the 'configuration' contribution point.")); - for (let node of subNodes) { - validateProperties(node, collector); - } - } } -const contributionRegistry = Registry.as(JSONExtensions.JSONContribution); -contributionRegistry.registerSchema('vscode://schemas/workspaceConfig', { - default: { - folders: [ - { - path: '' - } - ], - settings: { - } - }, - required: ['folders'], - properties: { - 'folders': { - minItems: 0, - uniqueItems: true, - description: nls.localize('workspaceConfig.folders.description', "List of folders to be loaded in the workspace."), - items: { - type: 'object', - default: { path: '' }, - 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. ") - } - }, - 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: workspaceSettingsSchemaId - }, - 'extensions': { - type: 'object', - default: {}, - description: nls.localize('workspaceConfig.extensions.description', "Workspace extensions"), - $ref: 'vscode://schemas/extensions' - } - }, - additionalProperties: false, - errorMessage: nls.localize('unknownWorkspaceProperty', "Unknown workspace configuration property") -}); - -export class WorkspaceService extends Disposable implements IWorkspaceConfigurationService, IWorkspaceContextService { - - public _serviceBrand: any; - - private workspace: Workspace; - private _configuration: Configuration; - private baseConfigurationService: GlobalConfigurationService; - private workspaceConfiguration: WorkspaceConfiguration; - private cachedFolderConfigs: StrictResourceMap>; - - 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 _onDidChangeWorkspaceName: Emitter = this._register(new Emitter()); - public readonly onDidChangeWorkspaceName: Event = this._onDidChangeWorkspaceName.event; - - protected readonly _onDidChangeWorkbenchState: Emitter = this._register(new Emitter()); - public readonly onDidChangeWorkbenchState: Event = this._onDidChangeWorkbenchState.event; - - constructor(private environmentService: IEnvironmentService, private workspacesService: IWorkspacesService, private workspaceSettingsRootFolder: string = WORKSPACE_CONFIG_FOLDER_DEFAULT_NAME) { - super(); - - this.workspaceConfiguration = this._register(new WorkspaceConfiguration()); - this._register(this.workspaceConfiguration.onDidUpdateConfiguration(() => this.onWorkspaceConfigurationChanged())); - - 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(): Workspace { - return this.workspace; - } - - public getWorkbenchState(): WorkbenchState { - // Workspace has configuration file - if (this.workspace.configuration) { - return WorkbenchState.WORKSPACE; - } - - // Folder has single root - if (this.workspace.folders.length === 1) { - return WorkbenchState.FOLDER; - } - - // Empty - return WorkbenchState.EMPTY; - } - - public getWorkspaceFolder(resource: URI): IWorkspaceFolder { - return this.workspace.getFolder(resource); - } - - public isInsideWorkspace(resource: URI): boolean { - return !!this.getWorkspaceFolder(resource); - } - - public isCurrentWorkspace(workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): boolean { - switch (this.getWorkbenchState()) { - case WorkbenchState.FOLDER: - return isSingleFolderWorkspaceIdentifier(workspaceIdentifier) && this.pathEquals(this.workspace.folders[0].uri.fsPath, workspaceIdentifier); - case WorkbenchState.WORKSPACE: - return isWorkspaceIdentifier(workspaceIdentifier) && this.workspace.id === workspaceIdentifier.id; - } - return false; - } - - public getConfigurationData(): IConfigurationData { - return this._configuration.toData(); - } - - public getConfiguration(section?: string, overrides?: IConfigurationOverrides): C { - return this._configuration.getValue(section, overrides); - } - - public lookup(key: string, overrides?: IConfigurationOverrides): IConfigurationValue { - return this._configuration.lookup(key, overrides); - } - - public keys(overrides?: IConfigurationOverrides): IConfigurationKeys { - return this._configuration.keys(overrides); - } - - public values(): IConfigurationValues { - return this._configuration.values(); - } - - public reloadConfiguration(section?: string): TPromise { - const current = this._configuration; - // Reload and reinitialize to ensure we are hitting the disk - return this.baseConfigurationService.reloadConfiguration() - .then(() => { - if (this.workspace.configuration) { - return this.workspaceConfiguration.load(this.workspace.configuration) - .then(() => this.initializeConfiguration(false)); - } - return this.initializeConfiguration(false); - }) - .then(() => { - // Check and trigger - if (!this._configuration.equals(current)) { - this.triggerConfigurationChange(); - } - return this.getConfiguration(section); - }); - } - - public getUnsupportedWorkspaceKeys(): string[] { - return this.getWorkbenchState() === WorkbenchState.FOLDER ? this._configuration.getFolderConfigurationModel(this.workspace.folders[0].uri).workspaceSettingsConfig.unsupportedKeys : []; - } - - public handleWorkspaceFileEvents(event: FileChangesEvent): void { - TPromise.join(this.workspace.folders.map(folder => this.cachedFolderConfigs.get(folder.uri).handleWorkspaceFileEvents(event))) // handle file event for each folder - .then(folderConfigurations => - folderConfigurations.map((configuration, index) => ({ configuration, folder: this.workspace.folders[index] })) - .filter(folderConfiguration => !!folderConfiguration.configuration) // Filter folders which are not impacted by events - .map(folderConfiguration => this.updateFolderConfiguration(folderConfiguration.folder, folderConfiguration.configuration, true)) // Update the configuration of impacted folders - .reduce((result, value) => result || value, false)) // Check if the effective configuration of folder is changed - .then(changed => changed ? this.triggerConfigurationChange() : void 0); // Trigger event if changed - } - - public initialize(arg: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IWindowConfiguration): TPromise { - return this.createWorkspace(arg) - .then(workspace => this.setWorkspace(workspace)) - .then(() => this.initializeConfiguration(true)); - } - - private createWorkspace(arg: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IWindowConfiguration): TPromise { - if (isWorkspaceIdentifier(arg)) { - return this.createMulitFolderWorkspace(arg); - } - - if (isSingleFolderWorkspaceIdentifier(arg)) { - return this.createSingleFolderWorkspace(arg); - } - - return this.createEmptyWorkspace(arg); - } - - private createMulitFolderWorkspace(workspaceIdentifier: IWorkspaceIdentifier): TPromise { - const workspaceConfigPath = URI.file(workspaceIdentifier.configPath); - return this.workspaceConfiguration.load(workspaceConfigPath) - .then(() => { - const workspaceConfigurationModel = this.workspaceConfiguration.workspaceConfigurationModel; - const workspaceFolders = toWorkspaceFolders(workspaceConfigurationModel.folders, URI.file(paths.dirname(workspaceConfigPath.fsPath))); - const workspaceId = workspaceIdentifier.id; - const workspaceName = getWorkspaceLabel({ id: workspaceId, configPath: workspaceConfigPath.fsPath }, this.environmentService); - return new Workspace(workspaceId, workspaceName, workspaceFolders, workspaceConfigPath); - }); - } - - private createSingleFolderWorkspace(singleFolderWorkspaceIdentifier: ISingleFolderWorkspaceIdentifier): TPromise { - const folderPath = URI.file(singleFolderWorkspaceIdentifier); - return stat(folderPath.fsPath) - .then(workspaceStat => { - const ctime = isLinux ? workspaceStat.ino : workspaceStat.birthtime.getTime(); // On Linux, birthtime is ctime, so we cannot use it! We use the ino instead! - const id = createHash('md5').update(folderPath.fsPath).update(ctime ? String(ctime) : '').digest('hex'); - const folder = URI.file(folderPath.fsPath); - return new Workspace(id, paths.basename(folderPath.fsPath), toWorkspaceFolders([{ path: folder.fsPath }]), null, ctime); - }); - } - - private createEmptyWorkspace(configuration: IWindowConfiguration): TPromise { - let id = configuration.backupPath ? URI.from({ path: paths.basename(configuration.backupPath), scheme: 'empty' }).toString() : ''; - return TPromise.as(new Workspace(id)); - } - - private setWorkspace(workspace: Workspace): void { - if (!this.workspace) { - this.workspace = workspace; - return; - } - - const currentState = this.getWorkbenchState(); - const currentWorkspacePath = this.workspace.configuration ? this.workspace.configuration.fsPath : void 0; - const currentFolders = this.workspace.folders; - - this.workspace.update(workspace); - - const newState = this.getWorkbenchState(); - if (newState !== currentState) { - this._onDidChangeWorkbenchState.fire(newState); - } - - const newWorkspacePath = this.workspace.configuration ? this.workspace.configuration.fsPath : void 0; - if (newWorkspacePath !== currentWorkspacePath || newState !== currentState) { - this._onDidChangeWorkspaceName.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())); - result.changed = newFolders.filter(newFolder => currentFolders.some(currentFolder => newFolder.uri.toString() === currentFolder.uri.toString() && newFolder.name !== currentFolder.name)); - - return result; - } - - private initializeConfiguration(trigger: boolean = true): TPromise { - this.registerConfigurationSchemas(); - this.resetCaches(); - return this.updateConfiguration() - .then(() => { - if (trigger) { - this.triggerConfigurationChange(); - } - }); - } - - private resetCaches(): void { - this.cachedFolderConfigs = new StrictResourceMap>(); - this._configuration = new Configuration(this.baseConfigurationService.configuration(), new ConfigurationModel(), new StrictResourceMap>(), this.getWorkbenchState() !== WorkbenchState.EMPTY ? this.workspace : null); //TODO: @Sandy Avoid passing null - this.initCachesForFolders(this.workspace.folders); - } - - 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: 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) { - this.workspace.folders.forEach(folder => this._configuration.getFolderConfigurationModel(folder.uri).update()); - } - if (this._configuration.updateBaseConfiguration(this.baseConfigurationService.configuration())) { - this._onDidUpdateConfiguration.fire({ source, sourceConfig }); - } - } - } - - 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 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 => { - if (configurationChanged) { - this.triggerConfigurationChange(); - } - this._onDidChangeWorkspaceFolders.fire(changes); - }); - } else { - const configurationChanged = this.updateWorkspaceConfiguration(true); - if (configurationChanged) { - this.triggerConfigurationChange(); - } - } - } - } - - private onFoldersChanged(): TPromise { - let configurationChangedOnRemoval = false; - - // Remove the configurations of deleted folders - for (const key of this.cachedFolderConfigs.keys()) { - if (!this.workspace.folders.filter(folder => folder.uri.toString() === key.toString())[0]) { - this.cachedFolderConfigs.delete(key); - if (this._configuration.deleteFolderConfiguration(key)) { - configurationChangedOnRemoval = true; - } - } - } - - // Initialize the newly added folders - const toInitialize = this.workspace.folders.filter(folder => !this.cachedFolderConfigs.has(folder.uri)); - if (toInitialize.length) { - this.initCachesForFolders(toInitialize); - return this.updateConfiguration(toInitialize) - .then(changed => configurationChangedOnRemoval || changed); - } else if (configurationChangedOnRemoval) { - this.updateWorkspaceConfiguration(false); - return TPromise.as(true); - } - return TPromise.as(false); - } - - 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 - configurationChanged = this.updateWorkspaceConfiguration(compare) || configurationChanged; - } - return configurationChanged; - } - - private updateWorkspaceConfiguration(compare: boolean): boolean { - const workbennchState = this.getWorkbenchState(); - if (workbennchState === WorkbenchState.EMPTY) { - return false; - } - - const workspaceConfiguration = workbennchState === WorkbenchState.WORKSPACE ? this.workspaceConfiguration.workspaceConfigurationModel.workspaceConfiguration : this._configuration.getFolderConfigurationModel(this.workspace.folders[0].uri); - return this._configuration.updateWorkspaceConfiguration(workspaceConfiguration, compare); - } - - private triggerConfigurationChange(): void { - if (this.getWorkbenchState() === WorkbenchState.EMPTY) { - this._onDidUpdateConfiguration.fire({ source: ConfigurationSource.User, sourceConfig: this._configuration.user.contents }); - } else { - this._onDidUpdateConfiguration.fire({ source: ConfigurationSource.Workspace, sourceConfig: this.workspace.folders.length ? this._configuration.getFolderConfigurationModel(this.workspace.folders[0].uri).contents : void 0 }); // TODO@Sandeep debt? - } - } - - private pathEquals(path1: string, path2: string): boolean { - if (!isLinux) { - path1 = path1.toLowerCase(); - path2 = path2.toLowerCase(); - } - - return path1 === path2; - } -} - -class WorkspaceConfiguration extends Disposable { +export class WorkspaceConfiguration extends Disposable { private _workspaceConfigPath: URI; - private _workspaceConfigurationWatcher: ConfigWatcher>; + private _workspaceConfigurationWatcher: ConfigWatcher; private _workspaceConfigurationWatcherDisposables: IDisposable[] = []; private _onDidUpdateConfiguration: Emitter = this._register(new Emitter()); public readonly onDidUpdateConfiguration: Event = this._onDidUpdateConfiguration.event; - load(workspaceConfigPath: URI): TPromise { if (this._workspaceConfigPath && this._workspaceConfigPath.fsPath === workspaceConfigPath.fsPath) { - return this._reload(); + return this.reload(); } this._workspaceConfigPath = workspaceConfigPath; - this._workspaceConfigurationWatcherDisposables = dispose(this._workspaceConfigurationWatcherDisposables); + this.stopListeningToWatcher(); return new TPromise((c, e) => { this._workspaceConfigurationWatcher = new ConfigWatcher(this._workspaceConfigPath.fsPath, { changeBufferDelay: 300, @@ -642,17 +98,42 @@ class WorkspaceConfiguration extends Disposable { return workspaceConfigurationModel; }, initCallback: () => c(null) }); - this._workspaceConfigurationWatcherDisposables.push(this._workspaceConfigurationWatcher); - this._workspaceConfigurationWatcher.onDidUpdateConfiguration(() => this._onDidUpdateConfiguration.fire(), this, this._workspaceConfigurationWatcherDisposables); + this.listenToWatcher(); }); } - get workspaceConfigurationModel(): WorkspaceConfigurationModel { + private get workspaceConfigurationModel(): WorkspaceConfigurationModel { return this._workspaceConfigurationWatcher ? this._workspaceConfigurationWatcher.getConfig() : new WorkspaceConfigurationModel(); } - private _reload(): TPromise { - return new TPromise(c => this._workspaceConfigurationWatcher.reload(() => c(null))); + reload(): TPromise { + this.stopListeningToWatcher(); + return new TPromise(c => this._workspaceConfigurationWatcher.reload(() => { + this.listenToWatcher(); + c(null); + })); + } + + getFolders(): IStoredWorkspaceFolder[] { + return this.workspaceConfigurationModel.folders; + } + + setFolders(folders: IStoredWorkspaceFolder[], jsonEditingService: JSONEditingService): TPromise { + return jsonEditingService.write(this._workspaceConfigPath, { key: 'folders', value: folders }, true) + .then(() => this.reload()); + } + + getConfiguration(): ConfigurationModel { + return this.workspaceConfigurationModel.workspaceConfiguration; + } + + private listenToWatcher() { + this._workspaceConfigurationWatcherDisposables.push(this._workspaceConfigurationWatcher); + this._workspaceConfigurationWatcher.onDidUpdateConfiguration(() => this._onDidUpdateConfiguration.fire(), this, this._workspaceConfigurationWatcherDisposables); + } + + private stopListeningToWatcher() { + this._workspaceConfigurationWatcherDisposables = dispose(this._workspaceConfigurationWatcherDisposables); } dispose(): void { @@ -661,15 +142,15 @@ class WorkspaceConfiguration extends Disposable { } } -class FolderConfiguration extends Disposable { +export class FolderConfiguration extends Disposable { private static RELOAD_CONFIGURATION_DELAY = 50; - private bulkFetchFromWorkspacePromise: TPromise; - private workspaceFilePathToConfiguration: { [relativeWorkspacePath: string]: TPromise> }; + private bulkFetchFromWorkspacePromise: TPromise; + private workspaceFilePathToConfiguration: { [relativeWorkspacePath: string]: TPromise }; private reloadConfigurationScheduler: RunOnceScheduler; - private reloadConfigurationEventEmitter: Emitter> = new Emitter>(); + private reloadConfigurationEventEmitter: Emitter = new Emitter(); constructor(private folder: URI, private configFolderRelativePath: string, private scope: ConfigurationScope) { super(); @@ -678,17 +159,17 @@ class FolderConfiguration extends Disposable { this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this.loadConfiguration().then(configuration => this.reloadConfigurationEventEmitter.fire(configuration), errors.onUnexpectedError), FolderConfiguration.RELOAD_CONFIGURATION_DELAY)); } - loadConfiguration(): TPromise> { + loadConfiguration(): TPromise { // Load workspace locals return this.loadWorkspaceConfigFiles().then(workspaceConfigFiles => { // Consolidate (support *.json files in the workspace settings folder) - const workspaceSettingsConfig = >workspaceConfigFiles[WORKSPACE_CONFIG_DEFAULT_PATH] || new FolderSettingsModel(null); - const otherConfigModels = Object.keys(workspaceConfigFiles).filter(key => key !== WORKSPACE_CONFIG_DEFAULT_PATH).map(key => >workspaceConfigFiles[key]); - return new FolderConfigurationModel(workspaceSettingsConfig, otherConfigModels, this.scope); + const workspaceSettingsConfig = workspaceConfigFiles[WORKSPACE_CONFIG_DEFAULT_PATH] || new FolderSettingsModel(null); + const otherConfigModels = Object.keys(workspaceConfigFiles).filter(key => key !== WORKSPACE_CONFIG_DEFAULT_PATH).map(key => workspaceConfigFiles[key]); + return new FolderConfigurationModel(workspaceSettingsConfig, otherConfigModels, this.scope); }); } - private loadWorkspaceConfigFiles(): TPromise<{ [relativeWorkspacePath: string]: ConfigurationModel }> { + private loadWorkspaceConfigFiles(): TPromise<{ [relativeWorkspacePath: string]: ConfigurationModel }> { // once: when invoked for the first time we fetch json files that contribute settings if (!this.bulkFetchFromWorkspacePromise) { this.bulkFetchFromWorkspacePromise = resolveStat(this.toResource(this.configFolderRelativePath)).then(stat => { @@ -715,7 +196,7 @@ class FolderConfiguration extends Disposable { return this.bulkFetchFromWorkspacePromise.then(() => TPromise.join(this.workspaceFilePathToConfiguration)); } - public handleWorkspaceFileEvents(event: FileChangesEvent): TPromise> { + public handleWorkspaceFileEvents(event: FileChangesEvent): TPromise { const events = event.changes; let affectedByChanges = false; @@ -773,18 +254,18 @@ class FolderConfiguration extends Disposable { }); } - private createConfigModel(content: IContent): ConfigurationModel { + private createConfigModel(content: IContent): ConfigurationModel { const path = this.toFolderRelativePath(content.resource); if (path === WORKSPACE_CONFIG_DEFAULT_PATH) { - return new FolderSettingsModel(content.value, content.resource.toString()); + return new FolderSettingsModel(content.value, content.resource.toString()); } else { const matches = /\/([^\.]*)*\.json/.exec(path); if (matches && matches[1]) { - return new ScopedConfigurationModel(content.value, content.resource.toString(), matches[1]); + return new ScopedConfigurationModel(content.value, content.resource.toString(), matches[1]); } } - return new CustomConfigurationModel(null); + return new CustomConfigurationModel(null); } private isWorkspaceConfigurationFile(folderRelativePath: string): boolean { @@ -814,209 +295,4 @@ class FolderConfiguration extends Disposable { return false; } -} - -// node.hs helper functions - -function resolveContents(resources: URI[]): TPromise { - const contents: IContent[] = []; - - return TPromise.join(resources.map(resource => { - return resolveContent(resource).then(content => { - contents.push(content); - }); - })).then(() => contents); -} - -function resolveContent(resource: URI): TPromise { - return readFile(resource.fsPath).then(contents => ({ resource, value: contents.toString() })); -} - -function resolveStat(resource: URI): TPromise { - return new TPromise((c, e) => { - extfs.readdir(resource.fsPath, (error, children) => { - if (error) { - if ((error).code === 'ENOTDIR') { - c({ resource }); - } else { - e(error); - } - } else { - c({ - resource, - isDirectory: true, - children: children.map(child => { return { resource: URI.file(paths.join(resource.fsPath, child)) }; }) - }); - } - }); - }); -} - -export class Configuration extends BaseConfiguration { - - constructor(private _baseConfiguration: BaseConfiguration, workspaceConfiguration: ConfigurationModel, protected folders: StrictResourceMap>, workspace: Workspace) { - super(_baseConfiguration.defaults, _baseConfiguration.user, workspaceConfiguration, folders, workspace); - } - - updateBaseConfiguration(baseConfiguration: BaseConfiguration): boolean { - const current = new Configuration(this._baseConfiguration, this._workspaceConfiguration, this.folders, this._workspace); - - this._baseConfiguration = baseConfiguration; - this._defaults = this._baseConfiguration.defaults; - this._user = this._baseConfiguration.user; - this.merge(); - - return !this.equals(current); - } - - updateWorkspaceConfiguration(workspaceConfiguration: ConfigurationModel, compare: boolean = true): boolean { - const current = new Configuration(this._baseConfiguration, this._workspaceConfiguration, this.folders, this._workspace); - - this._workspaceConfiguration = workspaceConfiguration; - this.merge(); - - return compare && !this.equals(current); - } - - updateFolderConfiguration(resource: URI, configuration: FolderConfigurationModel, compare: boolean): boolean { - const current = this.getValue(null, { resource }); - - this.folders.set(resource, configuration); - this.mergeFolder(resource); - - return compare && !objects.equals(current, this.getValue(null, { resource })); - } - - deleteFolderConfiguration(folder: URI): boolean { - if (this._workspace && this._workspace.folders.length > 0 && this._workspace.folders[0].uri.toString() === folder.toString()) { - // Do not remove workspace configuration - return false; - } - - const changed = this.folders.get(folder).keys.length > 0; - this.folders.delete(folder); - this._foldersConsolidatedConfigurations.delete(folder); - return changed; - } - - getFolderConfigurationModel(folder: URI): FolderConfigurationModel { - return >this.folders.get(folder); - } - - equals(other: any): boolean { - if (!other || !(other instanceof Configuration)) { - return false; - } - - if (!objects.equals(this.getValue(), other.getValue())) { - return false; - } - - if (this._foldersConsolidatedConfigurations.size !== other._foldersConsolidatedConfigurations.size) { - return false; - } - - for (const resource of this._foldersConsolidatedConfigurations.keys()) { - if (!objects.equals(this.getValue(null, { resource }), other.getValue(null, { resource }))) { - return false; - } - } - - return true; - } -} - -interface IExportedConfigurationNode { - name: string; - description: string; - default: any; - type: string | string[]; - enum?: any[]; - enumDescriptions?: string[]; -} - -interface IConfigurationExport { - settings: IExportedConfigurationNode[]; - buildTime: number; - commit: string; - version: number; -} - -export class DefaultConfigurationExportHelper { - - constructor( - @IEnvironmentService environmentService: IEnvironmentService, - @IExtensionService private extensionService: IExtensionService, - @ICommandService private commandService: ICommandService) { - if (environmentService.args['export-default-configuration']) { - this.writeConfigModelAndQuit(environmentService.args['export-default-configuration']); - } - } - - private writeConfigModelAndQuit(targetPath: string): TPromise { - return this.extensionService.onReady() - .then(() => this.writeConfigModel(targetPath)) - .then(() => this.commandService.executeCommand('workbench.action.quit')) - .then(() => { }); - } - - private writeConfigModel(targetPath: string): TPromise { - const config = this.getConfigModel(); - - const resultString = JSON.stringify(config, undefined, ' '); - return writeFile(targetPath, resultString); - } - - private getConfigModel(): IConfigurationExport { - const configurations = Registry.as(Extensions.Configuration).getConfigurations().slice(); - const settings: IExportedConfigurationNode[] = []; - const processConfig = (config: IConfigurationNode) => { - if (config.properties) { - for (let name in config.properties) { - const prop = config.properties[name]; - const propDetails: IExportedConfigurationNode = { - name, - description: prop.description, - default: prop.default, - type: prop.type - }; - - if (prop.enum) { - propDetails.enum = prop.enum; - } - - if (prop.enumDescriptions) { - propDetails.enumDescriptions = prop.enumDescriptions; - } - - settings.push(propDetails); - } - } - - if (config.allOf) { - config.allOf.forEach(processConfig); - } - }; - - configurations.forEach(processConfig); - - const result: IConfigurationExport = { - settings: settings.sort((a, b) => a.name.localeCompare(b.name)), - buildTime: Date.now(), - commit: product.commit, - version: versionStringToNumber(pkg.version) - }; - - return result; - } -} - -function versionStringToNumber(versionStr: string): number { - const semverRegex = /(\d+)\.(\d+)\.(\d+)/; - const match = versionStr.match(semverRegex); - if (!match) { - return 0; - } - - return parseInt(match[1], 10) * 10000 + parseInt(match[2], 10) * 100 + parseInt(match[3], 10); -} +} \ No newline at end of file diff --git a/src/vs/workbench/services/configuration/node/configurationEditingService.ts b/src/vs/workbench/services/configuration/node/configurationEditingService.ts index 9787cfcbc1c..e4b1cd4e7a0 100644 --- a/src/vs/workbench/services/configuration/node/configurationEditingService.ts +++ b/src/vs/workbench/services/configuration/node/configurationEditingService.ts @@ -23,17 +23,79 @@ import { Selection } from 'vs/editor/common/core/selection'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; -import { IConfigurationService, IConfigurationOverrides } from 'vs/platform/configuration/common/configuration'; -import { keyFromOverrideIdentifier } from 'vs/platform/configuration/common/model'; +import { IConfigurationService, IConfigurationOverrides, keyFromOverrideIdentifier, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { WORKSPACE_CONFIG_DEFAULT_PATH, WORKSPACE_STANDALONE_CONFIGURATIONS, TASKS_CONFIGURATION_KEY, LAUNCH_CONFIGURATION_KEY } from 'vs/workbench/services/configuration/common/configuration'; import { IFileService } from 'vs/platform/files/common/files'; -import { IConfigurationEditingService, ConfigurationEditingErrorCode, ConfigurationEditingError, ConfigurationTarget, IConfigurationValue, IConfigurationEditingOptions } from 'vs/workbench/services/configuration/common/configurationEditing'; import { ITextModelService, ITextEditorModel } from 'vs/editor/common/services/resolverService'; import { OVERRIDE_PROPERTY_PATTERN, IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; import { IChoiceService, IMessageService, Severity } from 'vs/platform/message/common/message'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +export enum ConfigurationEditingErrorCode { + + /** + * Error when trying to write a configuration key that is not registered. + */ + ERROR_UNKNOWN_KEY, + + /** + * Error when trying to write an invalid folder configuration key to folder settings. + */ + ERROR_INVALID_FOLDER_CONFIGURATION, + + /** + * Error when trying to write to user target but not supported for provided key. + */ + ERROR_INVALID_USER_TARGET, + + /** + * Error when trying to write a configuration key to folder target + */ + ERROR_INVALID_FOLDER_TARGET, + + /** + * Error when trying to write to the workspace configuration without having a workspace opened. + */ + ERROR_NO_WORKSPACE_OPENED, + + /** + * Error when trying to write and save to the configuration file while it is dirty in the editor. + */ + ERROR_CONFIGURATION_FILE_DIRTY, + + /** + * Error when trying to write to a configuration file that contains JSON errors. + */ + ERROR_INVALID_CONFIGURATION +} + +export class ConfigurationEditingError extends Error { + constructor(message: string, public code: ConfigurationEditingErrorCode) { + super(message); + } +} + +export interface IConfigurationValue { + key: string; + value: any; +} + +export interface IConfigurationEditingOptions { + /** + * If `true`, do not saves the configuration. Default is `false`. + */ + donotSave?: boolean; + /** + * If `true`, do not notifies the error to user by showing the message box. Default is `false`. + */ + donotNotifyError?: boolean; + /** + * Scope of configuration to be written into. + */ + scopes?: IConfigurationOverrides; +} + interface IConfigurationEditOperation extends IConfigurationValue { target: ConfigurationTarget; jsonPath: json.JSONPath; @@ -51,7 +113,7 @@ interface ConfigurationEditingOptions extends IConfigurationEditingOptions { force?: boolean; } -export class ConfigurationEditingService implements IConfigurationEditingService { +export class ConfigurationEditingService { public _serviceBrand: any; @@ -95,9 +157,7 @@ export class ConfigurationEditingService implements IConfigurationEditingService private writeToBuffer(model: editorCommon.IModel, operation: IConfigurationEditOperation, save: boolean): TPromise { const edit = this.getEdits(model, operation)[0]; if (this.applyEditsToBuffer(edit, model) && save) { - return this.textFileService.save(operation.resource, { skipSaveParticipants: true /* programmatic change */ }) - // Reload the configuration so that we make sure all parties are updated - .then(() => this.configurationService.reloadConfiguration()); + return this.textFileService.save(operation.resource, { skipSaveParticipants: true /* programmatic change */ }); } return TPromise.as(null); } @@ -193,7 +253,7 @@ export class ConfigurationEditingService implements IConfigurationEditingService case ConfigurationTarget.WORKSPACE: this.commandService.executeCommand('workbench.action.openWorkspaceSettings'); break; - case ConfigurationTarget.FOLDER: + case ConfigurationTarget.WORKSPACE_FOLDER: if (operation.resource) { const workspaceFolder = this.contextService.getWorkspaceFolder(operation.resource); if (workspaceFolder) { @@ -237,7 +297,7 @@ export class ConfigurationEditingService implements IConfigurationEditingService return nls.localize('errorInvalidConfiguration', "Unable to write into user settings. Please open **User Settings** file to correct errors/warnings in it and try again."); case ConfigurationTarget.WORKSPACE: return nls.localize('errorInvalidConfigurationWorkspace', "Unable to write into workspace settings. Please open **Workspace Settings** file to correct errors/warnings in the file and try again."); - case ConfigurationTarget.FOLDER: + case ConfigurationTarget.WORKSPACE_FOLDER: const workspaceFolderName = this.contextService.getWorkspaceFolder(operation.resource).name; return nls.localize('errorInvalidConfigurationFolder', "Unable to write into folder settings. Please open **Folder Settings** file under **{0}** folder to correct errors/warnings in it and try again.", workspaceFolderName); } @@ -255,7 +315,7 @@ export class ConfigurationEditingService implements IConfigurationEditingService return nls.localize('errorConfigurationFileDirty', "Unable to write into user settings because the file is dirty. Please save the **User Settings** file and try again."); case ConfigurationTarget.WORKSPACE: return nls.localize('errorConfigurationFileDirtyWorkspace', "Unable to write into workspace settings because the file is dirty. Please save the **Workspace Settings** file and try again."); - case ConfigurationTarget.FOLDER: + case ConfigurationTarget.WORKSPACE_FOLDER: const workspaceFolderName = this.contextService.getWorkspaceFolder(operation.resource).name; return nls.localize('errorConfigurationFileDirtyFolder', "Unable to write into folder settings because the file is dirty. Please save the **Folder Settings** file under **{0}** folder and try again.", workspaceFolderName); } @@ -270,9 +330,10 @@ export class ConfigurationEditingService implements IConfigurationEditingService return nls.localize('userTarget', "User Settings"); case ConfigurationTarget.WORKSPACE: return nls.localize('workspaceTarget', "Workspace Settings"); - case ConfigurationTarget.FOLDER: + case ConfigurationTarget.WORKSPACE_FOLDER: return nls.localize('folderTarget', "Folder Settings"); } + return ''; } private getEdits(model: editorCommon.IModel, edit: IConfigurationEditOperation): Edit[] { @@ -328,11 +389,11 @@ export class ConfigurationEditingService implements IConfigurationEditingService } // Target cannot be workspace or folder if no workspace opened - if ((target === ConfigurationTarget.WORKSPACE || target === ConfigurationTarget.FOLDER) && this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) { + if ((target === ConfigurationTarget.WORKSPACE || target === ConfigurationTarget.WORKSPACE_FOLDER) && this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) { return this.wrapError(ConfigurationEditingErrorCode.ERROR_NO_WORKSPACE_OPENED, target, operation); } - if (target === ConfigurationTarget.FOLDER) { + if (target === ConfigurationTarget.WORKSPACE_FOLDER) { if (!operation.resource) { return this.wrapError(ConfigurationEditingErrorCode.ERROR_INVALID_FOLDER_TARGET, target, operation); } @@ -419,7 +480,7 @@ export class ConfigurationEditingService implements IConfigurationEditingService } } - if (target === ConfigurationTarget.FOLDER) { + if (target === ConfigurationTarget.WORKSPACE_FOLDER) { if (resource) { const folder = this.contextService.getWorkspaceFolder(resource); if (folder) { diff --git a/src/vs/workbench/services/configuration/node/configurationService.ts b/src/vs/workbench/services/configuration/node/configurationService.ts new file mode 100644 index 00000000000..d2a28dd40b2 --- /dev/null +++ b/src/vs/workbench/services/configuration/node/configurationService.ts @@ -0,0 +1,736 @@ +/*--------------------------------------------------------------------------------------------- + * 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 * as paths from 'vs/base/common/paths'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { dirname } from 'path'; +import * as assert from 'vs/base/common/assert'; +import Event, { Emitter } from 'vs/base/common/event'; +import { StrictResourceMap } from 'vs/base/common/map'; +import { equals } from 'vs/base/common/objects'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { Queue } from 'vs/base/common/async'; +import { stat, writeFile } from 'vs/base/node/pfs'; +import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; +import { IWorkspaceContextService, Workspace, WorkbenchState, IWorkspaceFolder, toWorkspaceFolders, IWorkspaceFoldersChangeEvent } from 'vs/platform/workspace/common/workspace'; +import { FileChangesEvent } from 'vs/platform/files/common/files'; +import { isLinux } from 'vs/base/common/platform'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { ConfigurationModel, ConfigurationChangeEvent, AllKeysConfigurationChangeEvent } from 'vs/platform/configuration/common/configurationModels'; +import { IConfigurationChangeEvent, ConfigurationTarget, IConfigurationOverrides, keyFromOverrideIdentifier, isConfigurationOverrides, IConfigurationData } from 'vs/platform/configuration/common/configuration'; +import { FolderConfigurationModel, Configuration, WorkspaceConfigurationChangeEvent } from 'vs/workbench/services/configuration/common/configurationModels'; +import { IWorkspaceConfigurationService, WORKSPACE_CONFIG_FOLDER_DEFAULT_NAME, defaultSettingsSchemaId, userSettingsSchemaId, workspaceSettingsSchemaId, folderSettingsSchemaId } from 'vs/workbench/services/configuration/common/configuration'; +import { ConfigurationService as GlobalConfigurationService } from 'vs/platform/configuration/node/configurationService'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IConfigurationNode, IConfigurationRegistry, Extensions, ConfigurationScope, settingsSchema, resourceSettingsSchema } from 'vs/platform/configuration/common/configurationRegistry'; +import { createHash } from 'crypto'; +import { getWorkspaceLabel, IWorkspacesService, IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, IStoredWorkspaceFolder, isStoredWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces'; +import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; +import { IExtensionService } from 'vs/platform/extensions/common/extensions'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import product from 'vs/platform/node/product'; +import pkg from 'vs/platform/node/package'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ConfigurationEditingService } from 'vs/workbench/services/configuration/node/configurationEditingService'; +import { WorkspaceConfiguration, FolderConfiguration } from 'vs/workbench/services/configuration/node/configuration'; +import { JSONEditingService } from 'vs/workbench/services/configuration/node/jsonEditingService'; +import { Schemas } from 'vs/base/common/network'; +import { massageFolderPathForWorkspace } from 'vs/platform/workspaces/node/workspaces'; + +export class WorkspaceService extends Disposable implements IWorkspaceConfigurationService, IWorkspaceContextService { + + public _serviceBrand: any; + + private workspace: Workspace; + private _configuration: Configuration; + private baseConfigurationService: GlobalConfigurationService; + private workspaceConfiguration: WorkspaceConfiguration; + private cachedFolderConfigs: StrictResourceMap; + + private workspaceEditingQueue: Queue; + + protected readonly _onDidChangeConfiguration: Emitter = this._register(new Emitter()); + public readonly onDidChangeConfiguration: Event = this._onDidChangeConfiguration.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; + + protected readonly _onDidChangeWorkbenchState: Emitter = this._register(new Emitter()); + public readonly onDidChangeWorkbenchState: Event = this._onDidChangeWorkbenchState.event; + + private configurationEditingService: ConfigurationEditingService; + private jsonEditingService: JSONEditingService; + + constructor(private environmentService: IEnvironmentService, private workspacesService: IWorkspacesService, private workspaceSettingsRootFolder: string = WORKSPACE_CONFIG_FOLDER_DEFAULT_NAME) { + super(); + + this.workspaceConfiguration = this._register(new WorkspaceConfiguration()); + this._register(this.workspaceConfiguration.onDidUpdateConfiguration(() => this.onWorkspaceConfigurationChanged())); + + this.baseConfigurationService = this._register(new GlobalConfigurationService(environmentService)); + this._register(this.baseConfigurationService.onDidChangeConfiguration(e => this.onBaseConfigurationChanged(e))); + this._register(Registry.as(Extensions.Configuration).onDidRegisterConfiguration(e => this.registerConfigurationSchemas())); + + this.workspaceEditingQueue = new Queue(); + } + + // Workspace Context Service Impl + + public getWorkspace(): Workspace { + return this.workspace; + } + + public getWorkbenchState(): WorkbenchState { + // Workspace has configuration file + if (this.workspace.configuration) { + return WorkbenchState.WORKSPACE; + } + + // Folder has single root + if (this.workspace.folders.length === 1) { + return WorkbenchState.FOLDER; + } + + // Empty + return WorkbenchState.EMPTY; + } + + public getWorkspaceFolder(resource: URI): IWorkspaceFolder { + return this.workspace.getFolder(resource); + } + + public addFolders(foldersToAdd: URI[]): TPromise { + assert.ok(this.jsonEditingService, 'Workbench is not initialized yet'); + return this.workspaceEditingQueue.queue(() => this.doAddFolders(foldersToAdd)); + } + + public removeFolders(foldersToRemove: URI[]): TPromise { + assert.ok(this.jsonEditingService, 'Workbench is not initialized yet'); + return this.workspaceEditingQueue.queue(() => this.doRemoveFolders(foldersToRemove)); + } + + public isInsideWorkspace(resource: URI): boolean { + return !!this.getWorkspaceFolder(resource); + } + + public isCurrentWorkspace(workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): boolean { + switch (this.getWorkbenchState()) { + case WorkbenchState.FOLDER: + return isSingleFolderWorkspaceIdentifier(workspaceIdentifier) && this.pathEquals(this.workspace.folders[0].uri.fsPath, workspaceIdentifier); + case WorkbenchState.WORKSPACE: + return isWorkspaceIdentifier(workspaceIdentifier) && this.workspace.id === workspaceIdentifier.id; + } + return false; + } + + private doAddFolders(foldersToAdd: URI[]): TPromise { + if (this.getWorkbenchState() !== WorkbenchState.WORKSPACE) { + return TPromise.as(void 0); // we need a workspace to begin with + } + + const currentWorkspaceFolders = this.getWorkspace().folders; + const currentWorkspaceFolderUris = currentWorkspaceFolders.map(folder => folder.uri); + const currentStoredFolders = currentWorkspaceFolders.map(folder => folder.raw); + + const storedFoldersToAdd: IStoredWorkspaceFolder[] = []; + + const workspaceConfigFolder = dirname(this.getWorkspace().configuration.fsPath); + + foldersToAdd.forEach(folderToAdd => { + if (this.contains(currentWorkspaceFolderUris, folderToAdd)) { + return; // already existing + } + + // File resource: use "path" property + if (folderToAdd.scheme === Schemas.file) { + storedFoldersToAdd.push({ + path: massageFolderPathForWorkspace(folderToAdd.fsPath, workspaceConfigFolder, currentStoredFolders) + }); + } + + // Any other resource: use "uri" property + else { + storedFoldersToAdd.push({ + uri: folderToAdd.toString(true) + }); + } + }); + + if (storedFoldersToAdd.length > 0) { + return this.setFolders([...currentStoredFolders, ...storedFoldersToAdd]); + } + + return TPromise.as(void 0); + } + + private doRemoveFolders(foldersToRemove: URI[]): TPromise { + if (this.getWorkbenchState() !== WorkbenchState.WORKSPACE) { + return TPromise.as(void 0); // we need a workspace to begin with + } + + const currentWorkspaceFolders = this.getWorkspace().folders; + const currentStoredFolders = currentWorkspaceFolders.map(folder => folder.raw); + + const newStoredFolders: IStoredWorkspaceFolder[] = currentStoredFolders.filter((folder, index) => { + if (!isStoredWorkspaceFolder(folder)) { + return true; // keep entries which are unrelated + } + + return !this.contains(foldersToRemove, currentWorkspaceFolders[index].uri); // keep entries which are unrelated + }); + + if (newStoredFolders.length !== currentStoredFolders.length) { + return this.setFolders(newStoredFolders); + } + + return TPromise.as(void 0); + } + + private setFolders(folders: IStoredWorkspaceFolder[]): TPromise { + return this.workspaceConfiguration.setFolders(folders, this.jsonEditingService) + .then(() => this.onWorkspaceConfigurationChanged()); + } + + private contains(resources: URI[], toCheck: URI): boolean { + return resources.some(resource => { + if (isLinux) { + return resource.toString() === toCheck.toString(); + } + + return resource.toString().toLowerCase() === toCheck.toString().toLowerCase(); + }); + } + + // Workspace Configuration Service Impl + + getConfigurationData(): IConfigurationData { + return this._configuration.toData(); + } + + getConfiguration(): T + getConfiguration(section: string): T + getConfiguration(overrides: IConfigurationOverrides): T + getConfiguration(section: string, overrides: IConfigurationOverrides): T + getConfiguration(arg1?: any, arg2?: any): any { + const section = typeof arg1 === 'string' ? arg1 : void 0; + const overrides = isConfigurationOverrides(arg1) ? arg1 : isConfigurationOverrides(arg2) ? arg2 : void 0; + return this._configuration.getSection(section, overrides); + } + + getValue(key: string, overrides?: IConfigurationOverrides): T { + return this._configuration.getValue(key, overrides); + } + + updateValue(key: string, value: any): TPromise + updateValue(key: string, value: any, overrides: IConfigurationOverrides): TPromise + updateValue(key: string, value: any, target: ConfigurationTarget): TPromise + updateValue(key: string, value: any, overrides: IConfigurationOverrides, target: ConfigurationTarget): TPromise + updateValue(key: string, value: any, overrides: IConfigurationOverrides, target: ConfigurationTarget, donotNotifyError: boolean): TPromise + updateValue(key: string, value: any, arg3?: any, arg4?: any, donotNotifyError?: any): TPromise { + assert.ok(this.configurationEditingService, 'Workbench is not initialized yet'); + const overrides = isConfigurationOverrides(arg3) ? arg3 : void 0; + const target = this.deriveConfigurationTarget(key, value, overrides, overrides ? arg4 : arg3); + return target ? this.writeConfigurationValue(key, value, target, overrides, donotNotifyError) + : TPromise.as(null); + } + + reloadConfiguration(folder?: IWorkspaceFolder, key?: string): TPromise { + if (folder) { + return this.reloadWorkspaceFolderConfiguration(folder, key); + } + return this.reloadUserConfiguration() + .then(() => this.loadConfiguration()); + } + + inspect(key: string, overrides?: IConfigurationOverrides): { + default: T, + user: T, + workspace: T, + workspaceFolder: T, + memory?: T, + value: T + } { + return this._configuration.lookup(key); + } + + keys(): { + default: string[]; + user: string[]; + workspace: string[]; + workspaceFolder: string[]; + } { + return this._configuration.keys(); + } + + getUnsupportedWorkspaceKeys(): string[] { + return this.getWorkbenchState() === WorkbenchState.FOLDER ? this._configuration.getFolderConfigurationModel(this.workspace.folders[0].uri).workspaceSettingsConfig.unsupportedKeys : []; + } + + initialize(arg: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IWindowConfiguration): TPromise { + return this.createWorkspace(arg) + .then(workspace => this.setWorkspace(workspace)) + .then(() => this.initializeConfiguration()); + } + + setInstantiationService(instantiationService: IInstantiationService): void { + this.configurationEditingService = instantiationService.createInstance(ConfigurationEditingService); + this.jsonEditingService = instantiationService.createInstance(JSONEditingService); + } + + handleWorkspaceFileEvents(event: FileChangesEvent): TPromise { + switch (this.getWorkbenchState()) { + case WorkbenchState.FOLDER: + return this.onSingleFolderFileChanges(event); + case WorkbenchState.WORKSPACE: + return this.onWorkspaceFileChanges(event); + } + return TPromise.as(void 0); + } + + private createWorkspace(arg: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IWindowConfiguration): TPromise { + if (isWorkspaceIdentifier(arg)) { + return this.createMulitFolderWorkspace(arg); + } + + if (isSingleFolderWorkspaceIdentifier(arg)) { + return this.createSingleFolderWorkspace(arg); + } + + return this.createEmptyWorkspace(arg); + } + + private createMulitFolderWorkspace(workspaceIdentifier: IWorkspaceIdentifier): TPromise { + const workspaceConfigPath = URI.file(workspaceIdentifier.configPath); + return this.workspaceConfiguration.load(workspaceConfigPath) + .then(() => { + const workspaceFolders = toWorkspaceFolders(this.workspaceConfiguration.getFolders(), URI.file(paths.dirname(workspaceConfigPath.fsPath))); + const workspaceId = workspaceIdentifier.id; + const workspaceName = getWorkspaceLabel({ id: workspaceId, configPath: workspaceConfigPath.fsPath }, this.environmentService); + return new Workspace(workspaceId, workspaceName, workspaceFolders, workspaceConfigPath); + }); + } + + private createSingleFolderWorkspace(singleFolderWorkspaceIdentifier: ISingleFolderWorkspaceIdentifier): TPromise { + const folderPath = URI.file(singleFolderWorkspaceIdentifier); + return stat(folderPath.fsPath) + .then(workspaceStat => { + const ctime = isLinux ? workspaceStat.ino : workspaceStat.birthtime.getTime(); // On Linux, birthtime is ctime, so we cannot use it! We use the ino instead! + const id = createHash('md5').update(folderPath.fsPath).update(ctime ? String(ctime) : '').digest('hex'); + const folder = URI.file(folderPath.fsPath); + return new Workspace(id, paths.basename(folderPath.fsPath), toWorkspaceFolders([{ path: folder.fsPath }]), null, ctime); + }); + } + + private createEmptyWorkspace(configuration: IWindowConfiguration): TPromise { + let id = configuration.backupPath ? URI.from({ path: paths.basename(configuration.backupPath), scheme: 'empty' }).toString() : ''; + return TPromise.as(new Workspace(id)); + } + + private setWorkspace(workspace: Workspace): void { + if (!this.workspace) { + this.workspace = workspace; + return; + } + + const currentState = this.getWorkbenchState(); + const currentWorkspacePath = this.workspace.configuration ? this.workspace.configuration.fsPath : void 0; + const currentFolders = this.workspace.folders; + + this.workspace.update(workspace); + + const newState = this.getWorkbenchState(); + if (newState !== currentState) { + this._onDidChangeWorkbenchState.fire(newState); + } + + const newWorkspacePath = this.workspace.configuration ? this.workspace.configuration.fsPath : void 0; + if (newWorkspacePath !== currentWorkspacePath || newState !== currentState) { + this._onDidChangeWorkspaceName.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())); + result.changed = newFolders.filter(newFolder => currentFolders.some(currentFolder => newFolder.uri.toString() === currentFolder.uri.toString() && newFolder.name !== currentFolder.name)); + + return result; + } + + private initializeConfiguration(): TPromise { + this.registerConfigurationSchemas(); + return this.loadConfiguration(); + } + + private reloadUserConfiguration(key?: string): TPromise { + return this.baseConfigurationService.reloadConfiguration(); + } + + private reloadWorkspaceConfiguration(key?: string): TPromise { + const workbenchState = this.getWorkbenchState(); + if (workbenchState === WorkbenchState.FOLDER) { + return this.onWorkspaceFolderConfigurationChanged(this.workspace.folders[0], key); + } + if (workbenchState === WorkbenchState.WORKSPACE) { + return this.workspaceConfiguration.reload().then(() => this.onWorkspaceConfigurationChanged()); + } + return TPromise.as(null); + } + + private reloadWorkspaceFolderConfiguration(folder: IWorkspaceFolder, key?: string): TPromise { + return this.onWorkspaceFolderConfigurationChanged(folder, key); + } + + private loadConfiguration(): TPromise { + // reset caches + this.cachedFolderConfigs = new StrictResourceMap(); + + const folders = this.workspace.folders; + return this.loadFolderConfigurations(folders) + .then((folderConfigurations) => { + + let workspaceConfiguration = this.getWorkspaceConfigurationModel(folderConfigurations); + const folderConfigurationModels = new StrictResourceMap(); + folderConfigurations.forEach((folderConfiguration, index) => folderConfigurationModels.set(folders[index].uri, folderConfiguration)); + + const currentConfiguration = this._configuration; + this._configuration = new Configuration(this.baseConfigurationService.configuration.defaults, this.baseConfigurationService.configuration.user, workspaceConfiguration, folderConfigurationModels, new ConfigurationModel(), new StrictResourceMap(), this.getWorkbenchState() !== WorkbenchState.EMPTY ? this.workspace : null); //TODO: Sandy Avoid passing null + + if (currentConfiguration) { + const changedKeys = this._configuration.compare(currentConfiguration); + this.triggerConfigurationChange(new ConfigurationChangeEvent().change(changedKeys), ConfigurationTarget.WORKSPACE); + } else { + this._onDidChangeConfiguration.fire(new AllKeysConfigurationChangeEvent(this._configuration.allKeys(), ConfigurationTarget.WORKSPACE, this.getTargetConfiguration(ConfigurationTarget.WORKSPACE))); + } + }); + } + + private getWorkspaceConfigurationModel(folderConfigurations: FolderConfigurationModel[]): ConfigurationModel { + switch (this.getWorkbenchState()) { + case WorkbenchState.FOLDER: + return folderConfigurations[0]; + case WorkbenchState.WORKSPACE: + return this.workspaceConfiguration.getConfiguration(); + default: + return new ConfigurationModel(); + } + } + + private registerConfigurationSchemas(): void { + if (this.workspace) { + const jsonRegistry = Registry.as(JSONExtensions.JSONContribution); + jsonRegistry.registerSchema(defaultSettingsSchemaId, settingsSchema); + jsonRegistry.registerSchema(userSettingsSchemaId, settingsSchema); + + if (WorkbenchState.WORKSPACE === this.getWorkbenchState()) { + jsonRegistry.registerSchema(workspaceSettingsSchemaId, settingsSchema); + jsonRegistry.registerSchema(folderSettingsSchemaId, resourceSettingsSchema); + } else { + jsonRegistry.registerSchema(workspaceSettingsSchemaId, settingsSchema); + jsonRegistry.registerSchema(folderSettingsSchemaId, settingsSchema); + } + } + } + + private onBaseConfigurationChanged(e: IConfigurationChangeEvent): void { + if (this.workspace && this._configuration) { + if (e.source === ConfigurationTarget.DEFAULT) { + this.workspace.folders.forEach(folder => this._configuration.getFolderConfigurationModel(folder.uri).update()); + this._configuration.updateDefaultConfiguration(this.baseConfigurationService.configuration.defaults); + this.triggerConfigurationChange(new ConfigurationChangeEvent().change(e.affectedKeys), e.source); + } else { + let keys = this._configuration.updateUserConfiguration(this.baseConfigurationService.configuration.user); + this.triggerConfigurationChange(keys, e.source); + } + } + } + + private onWorkspaceConfigurationChanged(): TPromise { + if (this.workspace && this.workspace.configuration && this._configuration) { + const workspaceConfigurationChangeEvent = this._configuration.updateWorkspaceConfiguration(this.workspaceConfiguration.getConfiguration()); + let configuredFolders = toWorkspaceFolders(this.workspaceConfiguration.getFolders(), URI.file(paths.dirname(this.workspace.configuration.fsPath))); + const changes = this.compareFolders(this.workspace.folders, configuredFolders); + if (changes.added.length || changes.removed.length || changes.changed.length) { + this.workspace.folders = configuredFolders; + return this.onFoldersChanged() + .then(foldersConfigurationChangeEvent => { + this.triggerConfigurationChange(foldersConfigurationChangeEvent.change(workspaceConfigurationChangeEvent), ConfigurationTarget.WORKSPACE_FOLDER); + this._onDidChangeWorkspaceFolders.fire(changes); + }); + } else { + this.triggerConfigurationChange(workspaceConfigurationChangeEvent, ConfigurationTarget.WORKSPACE); + } + } + return TPromise.as(null); + } + + private onWorkspaceFileChanges(event: FileChangesEvent): TPromise { + return TPromise.join(this.workspace.folders.map(folder => + // handle file event for each folder + this.cachedFolderConfigs.get(folder.uri).handleWorkspaceFileEvents(event) + // Update folder configuration if handled + .then(folderConfiguration => folderConfiguration ? this._configuration.updateFolderConfiguration(folder.uri, folderConfiguration) : new ConfigurationChangeEvent())) + ).then(changeEvents => { + const consolidateChangeEvent = changeEvents.reduce((consolidated, e) => consolidated.change(e), new ConfigurationChangeEvent()); + this.triggerConfigurationChange(consolidateChangeEvent, ConfigurationTarget.WORKSPACE_FOLDER); + }); + } + + private onSingleFolderFileChanges(event: FileChangesEvent): TPromise { + const folder = this.workspace.folders[0]; + return this.cachedFolderConfigs.get(folder.uri).handleWorkspaceFileEvents(event) + .then(folderConfiguration => { + if (folderConfiguration) { + // File change handled + this._configuration.updateFolderConfiguration(folder.uri, folderConfiguration); + const workspaceChangedKeys = this._configuration.updateWorkspaceConfiguration(folderConfiguration); + this.triggerConfigurationChange(workspaceChangedKeys, ConfigurationTarget.WORKSPACE); + } + }); + } + + private onWorkspaceFolderConfigurationChanged(folder: IWorkspaceFolder, key?: string): TPromise { + this.disposeFolderConfiguration(folder); + return this.loadFolderConfigurations([folder]) + .then(([folderConfiguration]) => { + const folderChangedKeys = this._configuration.updateFolderConfiguration(folder.uri, folderConfiguration); + if (this.getWorkbenchState() === WorkbenchState.FOLDER) { + const workspaceChangedKeys = this._configuration.updateWorkspaceConfiguration(folderConfiguration); + this.triggerConfigurationChange(workspaceChangedKeys, ConfigurationTarget.WORKSPACE); + } else { + this.triggerConfigurationChange(folderChangedKeys, ConfigurationTarget.WORKSPACE_FOLDER); + } + }); + } + + private onFoldersChanged(): TPromise { + let changeEvent = new ConfigurationChangeEvent(); + + // Remove the configurations of deleted folders + for (const key of this.cachedFolderConfigs.keys()) { + if (!this.workspace.folders.filter(folder => folder.uri.toString() === key.toString())[0]) { + this.cachedFolderConfigs.delete(key); + changeEvent = changeEvent.change(this._configuration.deleteFolderConfiguration(key)); + } + } + + const toInitialize = this.workspace.folders.filter(folder => !this.cachedFolderConfigs.has(folder.uri)); + if (toInitialize.length) { + return this.loadFolderConfigurations(toInitialize) + .then(folderConfigurations => { + folderConfigurations.forEach((folderConfiguration, index) => { + changeEvent = changeEvent.change(this._configuration.updateFolderConfiguration(toInitialize[index].uri, folderConfiguration)); + }); + return changeEvent; + }); + } + return TPromise.as(changeEvent); + } + + private loadFolderConfigurations(folders: IWorkspaceFolder[]): TPromise { + return TPromise.join([...folders.map(folder => { + const folderConfiguration = new FolderConfiguration(folder.uri, this.workspaceSettingsRootFolder, this.getWorkbenchState() === WorkbenchState.WORKSPACE ? ConfigurationScope.RESOURCE : ConfigurationScope.WINDOW); + this.cachedFolderConfigs.set(folder.uri, this._register(folderConfiguration)); + return folderConfiguration.loadConfiguration(); + })]); + } + + private writeConfigurationValue(key: string, value: any, target: ConfigurationTarget, overrides: IConfigurationOverrides, donotNotifyError: boolean): TPromise { + if (target === ConfigurationTarget.DEFAULT) { + return TPromise.wrapError(new Error('Invalid configuration target')); + } + + if (target === ConfigurationTarget.MEMORY) { + this._configuration.updateValue(key, value, overrides); + this.triggerConfigurationChange(new ConfigurationChangeEvent().change(overrides.overrideIdentifier ? [keyFromOverrideIdentifier(overrides.overrideIdentifier)] : [key], overrides.resource), target); + return TPromise.as(null); + } + + return this.configurationEditingService.writeConfiguration(target, { key, value }, { scopes: overrides, donotNotifyError }) + .then(() => { + switch (target) { + case ConfigurationTarget.USER: + return this.reloadUserConfiguration(); + case ConfigurationTarget.WORKSPACE: + return this.reloadWorkspaceConfiguration(); + case ConfigurationTarget.WORKSPACE_FOLDER: + const workspaceFolder = overrides && overrides.resource ? this.workspace.getFolder(overrides.resource) : null; + if (workspaceFolder) { + return this.reloadWorkspaceFolderConfiguration(this.workspace.getFolder(overrides.resource), key); + } + } + return null; + }); + } + + private deriveConfigurationTarget(key: string, value: any, overrides: IConfigurationOverrides, target: ConfigurationTarget): ConfigurationTarget { + if (target) { + return target; + } + + if (value === void 0) { + // Ignore. But expected is to remove the value from all targets + return void 0; + } + + const inspect = this.inspect(key, overrides); + if (equals(value, inspect.value)) { + // No change. So ignore. + return void 0; + } + + if (inspect.workspaceFolder !== void 0) { + return ConfigurationTarget.WORKSPACE_FOLDER; + } + + if (inspect.workspace !== void 0) { + return ConfigurationTarget.WORKSPACE; + } + + return ConfigurationTarget.USER; + } + + private triggerConfigurationChange(configurationEvent: ConfigurationChangeEvent, target: ConfigurationTarget): void { + if (configurationEvent.affectedKeys.length) { + configurationEvent.telemetryData(target, this.getTargetConfiguration(target)); + this._onDidChangeConfiguration.fire(new WorkspaceConfigurationChangeEvent(configurationEvent, this.workspace)); + } + } + + private getTargetConfiguration(target: ConfigurationTarget): any { + switch (target) { + case ConfigurationTarget.DEFAULT: + return this._configuration.defaults.contents; + case ConfigurationTarget.USER: + return this._configuration.user.contents; + case ConfigurationTarget.WORKSPACE: + return this._configuration.workspace.contents; + } + return {}; + } + + private pathEquals(path1: string, path2: string): boolean { + if (!isLinux) { + path1 = path1.toLowerCase(); + path2 = path2.toLowerCase(); + } + + return path1 === path2; + } + + private disposeFolderConfiguration(folder: IWorkspaceFolder): void { + const folderConfiguration = this.cachedFolderConfigs.get(folder.uri); + if (folderConfiguration) { + folderConfiguration.dispose(); + } + } +} + +interface IExportedConfigurationNode { + name: string; + description: string; + default: any; + type: string | string[]; + enum?: any[]; + enumDescriptions?: string[]; +} + +interface IConfigurationExport { + settings: IExportedConfigurationNode[]; + buildTime: number; + commit: string; + version: number; +} + +export class DefaultConfigurationExportHelper { + + constructor( + @IEnvironmentService environmentService: IEnvironmentService, + @IExtensionService private extensionService: IExtensionService, + @ICommandService private commandService: ICommandService) { + if (environmentService.args['export-default-configuration']) { + this.writeConfigModelAndQuit(environmentService.args['export-default-configuration']); + } + } + + private writeConfigModelAndQuit(targetPath: string): TPromise { + return this.extensionService.onReady() + .then(() => this.writeConfigModel(targetPath)) + .then(() => this.commandService.executeCommand('workbench.action.quit')) + .then(() => { }); + } + + private writeConfigModel(targetPath: string): TPromise { + const config = this.getConfigModel(); + + const resultString = JSON.stringify(config, undefined, ' '); + return writeFile(targetPath, resultString); + } + + private getConfigModel(): IConfigurationExport { + const configurations = Registry.as(Extensions.Configuration).getConfigurations().slice(); + const settings: IExportedConfigurationNode[] = []; + const processConfig = (config: IConfigurationNode) => { + if (config.properties) { + for (let name in config.properties) { + const prop = config.properties[name]; + const propDetails: IExportedConfigurationNode = { + name, + description: prop.description, + default: prop.default, + type: prop.type + }; + + if (prop.enum) { + propDetails.enum = prop.enum; + } + + if (prop.enumDescriptions) { + propDetails.enumDescriptions = prop.enumDescriptions; + } + + settings.push(propDetails); + } + } + + if (config.allOf) { + config.allOf.forEach(processConfig); + } + }; + + configurations.forEach(processConfig); + + const result: IConfigurationExport = { + settings: settings.sort((a, b) => a.name.localeCompare(b.name)), + buildTime: Date.now(), + commit: product.commit, + version: versionStringToNumber(pkg.version) + }; + + return result; + } +} + +function versionStringToNumber(versionStr: string): number { + const semverRegex = /(\d+)\.(\d+)\.(\d+)/; + const match = versionStr.match(semverRegex); + if (!match) { + return 0; + } + + return parseInt(match[1], 10) * 10000 + parseInt(match[2], 10) * 100 + parseInt(match[3], 10); +} \ No newline at end of file diff --git a/src/vs/workbench/services/configuration/test/common/configurationModels.test.ts b/src/vs/workbench/services/configuration/test/common/configurationModels.test.ts index a6755918258..781260d8502 100644 --- a/src/vs/workbench/services/configuration/test/common/configurationModels.test.ts +++ b/src/vs/workbench/services/configuration/test/common/configurationModels.test.ts @@ -5,8 +5,13 @@ 'use strict'; import * as assert from 'assert'; -import { FolderConfigurationModel, ScopedConfigurationModel, FolderSettingsModel } from 'vs/workbench/services/configuration/common/configurationModels'; +import { join } from 'vs/base/common/paths'; +import { FolderConfigurationModel, ScopedConfigurationModel, FolderSettingsModel, WorkspaceConfigurationChangeEvent } from 'vs/workbench/services/configuration/common/configurationModels'; import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; +import { Workspace, WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import URI from 'vs/base/common/uri'; +import { ConfigurationChangeEvent } from 'vs/platform/configuration/common/configurationModels'; +import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; suite('ConfigurationService - Model', () => { @@ -17,7 +22,7 @@ suite('ConfigurationService - Model', () => { const testObject = new FolderConfigurationModel(settingsConfig, [], ConfigurationScope.WINDOW); - assert.equal(testObject.getContentsFor('task'), undefined); + assert.equal(testObject.getSectionContents('task'), undefined); }); test('Test consolidate (settings and tasks)', () => { @@ -94,4 +99,99 @@ suite('ConfigurationService - Model', () => { assert.deepEqual(new FolderConfigurationModel(settingsConfig, [launchConfig, tasksConfig], ConfigurationScope.WINDOW).contents, expected); assert.deepEqual(new FolderConfigurationModel(settingsConfig, [tasksConfig, launchConfig], ConfigurationScope.WINDOW).contents, expected); }); +}); + +suite('WorkspaceConfigurationChangeEvent', () => { + + test('changeEvent affecting workspace folders', () => { + let configurationChangeEvent = new ConfigurationChangeEvent(); + configurationChangeEvent.change(['window.title']); + configurationChangeEvent.change(['window.zoomLevel'], URI.file('folder1')); + configurationChangeEvent.change(['workbench.editor.enablePreview'], URI.file('folder2')); + configurationChangeEvent.change(['window.restoreFullscreen'], URI.file('folder1')); + configurationChangeEvent.change(['window.restoreWindows'], URI.file('folder2')); + configurationChangeEvent.telemetryData(ConfigurationTarget.WORKSPACE, {}); + + let testObject = new WorkspaceConfigurationChangeEvent(configurationChangeEvent, new Workspace('id', 'name', + [new WorkspaceFolder({ index: 0, name: '1', uri: URI.file('folder1') }), + new WorkspaceFolder({ index: 1, name: '2', uri: URI.file('folder2') }), + new WorkspaceFolder({ index: 2, name: '3', uri: URI.file('folder3') })])); + + assert.deepEqual(testObject.affectedKeys, ['window.title', 'window.zoomLevel', 'window.restoreFullscreen', 'workbench.editor.enablePreview', 'window.restoreWindows']); + assert.equal(testObject.source, ConfigurationTarget.WORKSPACE); + + assert.ok(testObject.affectsConfiguration('window.zoomLevel')); + assert.ok(testObject.affectsConfiguration('window.zoomLevel', URI.file('folder1'))); + assert.ok(testObject.affectsConfiguration('window.zoomLevel', URI.file(join('folder1', 'file1')))); + assert.ok(!testObject.affectsConfiguration('window.zoomLevel', URI.file('file1'))); + assert.ok(!testObject.affectsConfiguration('window.zoomLevel', URI.file('file2'))); + assert.ok(!testObject.affectsConfiguration('window.zoomLevel', URI.file(join('folder2', 'file2')))); + assert.ok(!testObject.affectsConfiguration('window.zoomLevel', URI.file(join('folder3', 'file3')))); + + assert.ok(testObject.affectsConfiguration('window.restoreFullscreen')); + assert.ok(testObject.affectsConfiguration('window.restoreFullscreen', URI.file(join('folder1', 'file1')))); + assert.ok(testObject.affectsConfiguration('window.restoreFullscreen', URI.file('folder1'))); + assert.ok(!testObject.affectsConfiguration('window.restoreFullscreen', URI.file('file1'))); + assert.ok(!testObject.affectsConfiguration('window.restoreFullscreen', URI.file('file2'))); + assert.ok(!testObject.affectsConfiguration('window.restoreFullscreen', URI.file(join('folder2', 'file2')))); + assert.ok(!testObject.affectsConfiguration('window.restoreFullscreen', URI.file(join('folder3', 'file3')))); + + assert.ok(testObject.affectsConfiguration('window.restoreWindows')); + assert.ok(testObject.affectsConfiguration('window.restoreWindows', URI.file('folder2'))); + assert.ok(testObject.affectsConfiguration('window.restoreWindows', URI.file(join('folder2', 'file2')))); + assert.ok(!testObject.affectsConfiguration('window.restoreWindows', URI.file('file2'))); + assert.ok(!testObject.affectsConfiguration('window.restoreWindows', URI.file(join('folder1', 'file1')))); + assert.ok(!testObject.affectsConfiguration('window.restoreWindows', URI.file(join('folder3', 'file3')))); + + assert.ok(testObject.affectsConfiguration('window.title')); + assert.ok(testObject.affectsConfiguration('window.title', URI.file('folder1'))); + assert.ok(testObject.affectsConfiguration('window.title', URI.file(join('folder1', 'file1')))); + assert.ok(testObject.affectsConfiguration('window.title', URI.file('folder2'))); + assert.ok(testObject.affectsConfiguration('window.title', URI.file(join('folder2', 'file2')))); + assert.ok(testObject.affectsConfiguration('window.title', URI.file('folder3'))); + assert.ok(testObject.affectsConfiguration('window.title', URI.file(join('folder3', 'file3')))); + assert.ok(testObject.affectsConfiguration('window.title', URI.file('file1'))); + assert.ok(testObject.affectsConfiguration('window.title', URI.file('file2'))); + assert.ok(testObject.affectsConfiguration('window.title', URI.file('file3'))); + + assert.ok(testObject.affectsConfiguration('window')); + assert.ok(testObject.affectsConfiguration('window', URI.file('folder1'))); + assert.ok(testObject.affectsConfiguration('window', URI.file(join('folder1', 'file1')))); + assert.ok(testObject.affectsConfiguration('window', URI.file('folder2'))); + assert.ok(testObject.affectsConfiguration('window', URI.file(join('folder2', 'file2')))); + assert.ok(testObject.affectsConfiguration('window', URI.file('folder3'))); + assert.ok(testObject.affectsConfiguration('window', URI.file(join('folder3', 'file3')))); + assert.ok(testObject.affectsConfiguration('window', URI.file('file1'))); + assert.ok(testObject.affectsConfiguration('window', URI.file('file2'))); + assert.ok(testObject.affectsConfiguration('window', URI.file('file3'))); + + assert.ok(testObject.affectsConfiguration('workbench.editor.enablePreview')); + assert.ok(testObject.affectsConfiguration('workbench.editor.enablePreview', URI.file('folder2'))); + assert.ok(testObject.affectsConfiguration('workbench.editor.enablePreview', URI.file(join('folder2', 'file2')))); + assert.ok(!testObject.affectsConfiguration('workbench.editor.enablePreview', URI.file('folder1'))); + assert.ok(!testObject.affectsConfiguration('workbench.editor.enablePreview', URI.file(join('folder1', 'file1')))); + assert.ok(!testObject.affectsConfiguration('workbench.editor.enablePreview', URI.file('folder3'))); + + assert.ok(testObject.affectsConfiguration('workbench.editor')); + assert.ok(testObject.affectsConfiguration('workbench.editor', URI.file('folder2'))); + assert.ok(testObject.affectsConfiguration('workbench.editor', URI.file(join('folder2', 'file2')))); + assert.ok(!testObject.affectsConfiguration('workbench.editor', URI.file('folder1'))); + assert.ok(!testObject.affectsConfiguration('workbench.editor', URI.file(join('folder1', 'file1')))); + assert.ok(!testObject.affectsConfiguration('workbench.editor', URI.file('folder3'))); + + assert.ok(testObject.affectsConfiguration('workbench')); + assert.ok(testObject.affectsConfiguration('workbench', URI.file('folder2'))); + assert.ok(testObject.affectsConfiguration('workbench', URI.file(join('folder2', 'file2')))); + assert.ok(!testObject.affectsConfiguration('workbench', URI.file('folder1'))); + assert.ok(!testObject.affectsConfiguration('workbench', URI.file('folder3'))); + + assert.ok(!testObject.affectsConfiguration('files')); + assert.ok(!testObject.affectsConfiguration('files', URI.file('folder1'))); + assert.ok(!testObject.affectsConfiguration('files', URI.file(join('folder1', 'file1')))); + assert.ok(!testObject.affectsConfiguration('files', URI.file('folder2'))); + assert.ok(!testObject.affectsConfiguration('files', URI.file(join('folder2', 'file2')))); + assert.ok(!testObject.affectsConfiguration('files', URI.file('folder3'))); + assert.ok(!testObject.affectsConfiguration('files', URI.file(join('folder3', 'file3')))); + }); + }); \ No newline at end of file diff --git a/src/vs/workbench/services/configuration/test/node/configuration.test.ts b/src/vs/workbench/services/configuration/test/node/configuration.test.ts deleted file mode 100644 index ea5d59653ca..00000000000 --- a/src/vs/workbench/services/configuration/test/node/configuration.test.ts +++ /dev/null @@ -1,495 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * 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 assert = require('assert'); -import os = require('os'); -import path = require('path'); -import fs = require('fs'); -import * as sinon from 'sinon'; -import URI from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { ParsedArgs } from 'vs/platform/environment/common/environment'; -import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; -import { parseArgs } from 'vs/platform/environment/node/argv'; -import extfs = require('vs/base/node/extfs'); -import uuid = require('vs/base/common/uuid'); -import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; -import { WorkspaceService } from 'vs/workbench/services/configuration/node/configuration'; -import { FileChangeType, FileChangesEvent } from 'vs/platform/files/common/files'; -import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; - -class SettingsTestEnvironmentService extends EnvironmentService { - - constructor(args: ParsedArgs, _execPath: string, private customAppSettingsHome) { - super(args, _execPath); - } - - get appSettingsPath(): string { return this.customAppSettingsHome; } -} - -function createWorkspace(callback: (workspaceDir: string, globalSettingsFile: string, cleanUp: (callback: () => void) => void) => void): void { - const id = uuid.generateUuid(); - const parentDir = path.join(os.tmpdir(), 'vsctests', id); - const workspaceDir = path.join(parentDir, 'workspaceconfig', id); - const workspaceSettingsDir = path.join(workspaceDir, '.vscode'); - const globalSettingsFile = path.join(workspaceDir, 'config.json'); - - extfs.mkdirp(workspaceSettingsDir, 493, (error) => { - callback(workspaceDir, globalSettingsFile, (callback) => extfs.del(parentDir, os.tmpdir(), () => { }, callback)); - }); -} - -function setUpFolder(folderName: string): TPromise<{ parentDir: string, workspaceDir: string, workspaceService: WorkspaceService }> { - const id = uuid.generateUuid(); - const parentDir = path.join(os.tmpdir(), 'vsctests', id); - const workspaceDir = path.join(parentDir, folderName); - const workspaceSettingsDir = path.join(workspaceDir, '.vscode'); - const globalSettingsFile = path.join(workspaceDir, 'config.json'); - - return new TPromise((c, e) => { - extfs.mkdirp(workspaceSettingsDir, 493, (error) => { - if (error) { - e(error); - return null; - } - const environmentService = new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, globalSettingsFile); - const workspaceService = new WorkspaceService(environmentService, null); - workspaceService.initialize(workspaceDir).then(() => c({ parentDir, workspaceDir, workspaceService })); - }); - }); -} - -function createService(workspaceDir: string, globalSettingsFile: string): TPromise { - const environmentService = new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, globalSettingsFile); - const service = new WorkspaceService(environmentService, null); - - return service.initialize(workspaceDir).then(() => service); -} - -suite('WorkspaceContextService - Folder', () => { - - let workspaceName = `testWorkspace${uuid.generateUuid()}`, parentResource: string, workspaceResource: string, workspaceContextService: IWorkspaceContextService; - - setup(() => { - return setUpFolder(workspaceName) - .then(({ parentDir, workspaceDir, workspaceService }) => { - parentResource = parentDir; - workspaceResource = workspaceDir; - workspaceContextService = workspaceService; - }); - }); - - teardown(done => { - if (workspaceContextService) { - (workspaceContextService).dispose(); - } - if (parentResource) { - extfs.del(parentResource, os.tmpdir(), () => { }, done); - } - }); - - test('getWorkspace()', () => { - const actual = workspaceContextService.getWorkspace(); - - assert.equal(actual.folders.length, 1); - 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.ok(!actual.configuration); - }); - - test('getWorkbenchState()', () => { - const actual = workspaceContextService.getWorkbenchState(); - - assert.equal(actual, WorkbenchState.FOLDER); - }); - - test('getWorkspaceFolder()', () => { - const actual = workspaceContextService.getWorkspaceFolder(URI.file(path.join(workspaceResource, 'a'))); - - assert.equal(actual, workspaceContextService.getWorkspace().folders[0]); - }); - - test('isCurrentWorkspace() => true', () => { - assert.ok(workspaceContextService.isCurrentWorkspace(workspaceResource)); - }); - - test('isCurrentWorkspace() => false', () => { - assert.ok(!workspaceContextService.isCurrentWorkspace(workspaceResource + 'abc')); - }); -}); - -suite('WorkspaceContextService - Folder', () => { -}); - -suite('WorkspaceConfigurationService - Node', () => { - - test('defaults', (done: () => void) => { - interface ITestSetting { - workspace: { - service: { - testSetting: string; - } - }; - } - - const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); - configurationRegistry.registerConfiguration({ - 'id': '_test_workspace', - 'type': 'object', - 'properties': { - 'workspace.service.testSetting': { - 'type': 'string', - 'default': 'isSet' - } - } - }); - - configurationRegistry.registerConfiguration({ - 'id': '_test', - 'type': 'object', - 'properties': { - 'workspaceLookup.service.testSetting': { - 'type': 'string', - 'default': 'isSet' - } - } - }); - - createWorkspace((workspaceDir, globalSettingsFile, cleanUp) => { - return createService(workspaceDir, globalSettingsFile).then(service => { - const config = service.getConfiguration(); - assert.equal(config.workspace.service.testSetting, 'isSet'); - - service.dispose(); - - cleanUp(done); - }); - }); - }); - - test('globals', (done: () => void) => { - createWorkspace((workspaceDir, globalSettingsFile, cleanUp) => { - return createService(workspaceDir, globalSettingsFile).then(service => { - fs.writeFileSync(globalSettingsFile, '{ "testworkbench.editor.tabs": true }'); - - service.reloadConfiguration().then(() => { - const config = service.getConfiguration<{ testworkbench: { editor: { tabs: boolean } } }>(); - assert.equal(config.testworkbench.editor.tabs, true); - - service.dispose(); - - cleanUp(done); - }); - }); - }); - }); - - test('reload configuration emits events', (done: () => void) => { - createWorkspace((workspaceDir, globalSettingsFile, cleanUp) => { - return createService(workspaceDir, globalSettingsFile).then(service => { - fs.writeFileSync(globalSettingsFile, '{ "testworkbench.editor.tabs": true }'); - - return service.initialize(workspaceDir).then(() => { - service.onDidUpdateConfiguration(event => { - const config = service.getConfiguration<{ testworkbench: { editor: { tabs: boolean } } }>(); - assert.equal(config.testworkbench.editor.tabs, false); - - service.dispose(); - - cleanUp(done); - }); - - fs.writeFileSync(globalSettingsFile, '{ "testworkbench.editor.tabs": false }'); - - // this has to trigger the event since the config changes - service.reloadConfiguration().done(); - }); - - }); - }); - }); - - test('globals override defaults', (done: () => void) => { - interface ITestSetting { - workspace: { - service: { - testSetting: string; - } - }; - } - - createWorkspace((workspaceDir, globalSettingsFile, cleanUp) => { - return createService(workspaceDir, globalSettingsFile).then(service => { - fs.writeFileSync(globalSettingsFile, '{ "workspace.service.testSetting": "isChanged" }'); - - service.reloadConfiguration().then(() => { - const config = service.getConfiguration(); - assert.equal(config.workspace.service.testSetting, 'isChanged'); - - service.dispose(); - - cleanUp(done); - }); - }); - }); - }); - - test('workspace settings', (done: () => void) => { - createWorkspace((workspaceDir, globalSettingsFile, cleanUp) => { - return createService(workspaceDir, globalSettingsFile).then(service => { - fs.writeFileSync(path.join(workspaceDir, '.vscode', 'settings.json'), '{ "testworkbench.editor.icons": true }'); - - service.reloadConfiguration().then(() => { - const config = service.getConfiguration<{ testworkbench: { editor: { icons: boolean } } }>(); - assert.equal(config.testworkbench.editor.icons, true); - - service.dispose(); - - cleanUp(done); - }); - }); - }); - }); - - test('workspace settings override user settings', (done: () => void) => { - createWorkspace((workspaceDir, globalSettingsFile, cleanUp) => { - return createService(workspaceDir, globalSettingsFile).then(service => { - fs.writeFileSync(globalSettingsFile, '{ "testworkbench.editor.icons": false, "testworkbench.other.setting": true }'); - fs.writeFileSync(path.join(workspaceDir, '.vscode', 'settings.json'), '{ "testworkbench.editor.icons": true }'); - - service.reloadConfiguration().then(() => { - const config = service.getConfiguration<{ testworkbench: { editor: { icons: boolean }, other: { setting: string } } }>(); - assert.equal(config.testworkbench.editor.icons, true); - assert.equal(config.testworkbench.other.setting, true); - - service.dispose(); - - cleanUp(done); - }); - }); - }); - }); - - test('workspace change triggers event', (done: () => void) => { - createWorkspace((workspaceDir, globalSettingsFile, cleanUp) => { - return createService(workspaceDir, globalSettingsFile).then(service => { - service.onDidUpdateConfiguration(event => { - const config = service.getConfiguration<{ testworkbench: { editor: { icons: boolean } } }>(); - assert.equal(config.testworkbench.editor.icons, true); - assert.equal(service.getConfiguration().testworkbench.editor.icons, true); - - service.dispose(); - - cleanUp(done); - }); - - const settingsFile = path.join(workspaceDir, '.vscode', 'settings.json'); - fs.writeFileSync(settingsFile, '{ "testworkbench.editor.icons": true }'); - - const event = new FileChangesEvent([{ resource: URI.file(settingsFile), type: FileChangeType.ADDED }]); - service.handleWorkspaceFileEvents(event); - }); - }); - }); - - test('workspace reload should triggers event if content changed', (done: () => void) => { - createWorkspace((workspaceDir, globalSettingsFile, cleanUp) => { - return createService(workspaceDir, globalSettingsFile).then(service => { - const settingsFile = path.join(workspaceDir, '.vscode', 'settings.json'); - fs.writeFileSync(settingsFile, '{ "testworkbench.editor.icons": true }'); - - const target = sinon.stub(); - service.onDidUpdateConfiguration(event => target()); - - fs.writeFileSync(settingsFile, '{ "testworkbench.editor.icons": false }'); - - service.reloadConfiguration().done(() => { - assert.ok(target.calledOnce); - service.dispose(); - - cleanUp(done); - }); - }); - }); - }); - - test('workspace reload should not trigger event if nothing changed', (done: () => void) => { - createWorkspace((workspaceDir, globalSettingsFile, cleanUp) => { - return createService(workspaceDir, globalSettingsFile).then(service => { - const settingsFile = path.join(workspaceDir, '.vscode', 'settings.json'); - fs.writeFileSync(settingsFile, '{ "testworkbench.editor.icons": true }'); - - service.reloadConfiguration().done(() => { - const target = sinon.stub(); - service.onDidUpdateConfiguration(event => target()); - - service.reloadConfiguration().done(() => { - assert.ok(!target.called); - service.dispose(); - - cleanUp(done); - }); - }); - }); - }); - }); - - test('workspace reload should not trigger event if there is no model', (done: () => void) => { - createWorkspace((workspaceDir, globalSettingsFile, cleanUp) => { - return createService(workspaceDir, globalSettingsFile).then(service => { - const target = sinon.stub(); - service.onDidUpdateConfiguration(event => target()); - service.reloadConfiguration().done(() => { - assert.ok(!target.called); - service.dispose(); - cleanUp(done); - }); - }); - }); - }); - - - test('lookup', (done: () => void) => { - createWorkspace((workspaceDir, globalSettingsFile, cleanUp) => { - return createService(workspaceDir, globalSettingsFile).then(service => { - let res = service.lookup('something.missing'); - assert.ok(!res.default); - assert.ok(!res.user); - assert.ok(!res.workspace); - assert.ok(!res.value); - - res = service.lookup('workspaceLookup.service.testSetting'); - assert.equal(res.default, 'isSet'); - assert.equal(res.value, 'isSet'); - assert.ok(!res.user); - assert.ok(!res.workspace); - - fs.writeFileSync(globalSettingsFile, '{ "workspaceLookup.service.testSetting": true }'); - - return service.reloadConfiguration().then(() => { - res = service.lookup('workspaceLookup.service.testSetting'); - assert.equal(res.default, 'isSet'); - assert.equal(res.user, true); - assert.equal(res.value, true); - assert.ok(!res.workspace); - - const settingsFile = path.join(workspaceDir, '.vscode', 'settings.json'); - fs.writeFileSync(settingsFile, '{ "workspaceLookup.service.testSetting": 55 }'); - - return service.reloadConfiguration().then(() => { - res = service.lookup('workspaceLookup.service.testSetting'); - assert.equal(res.default, 'isSet'); - assert.equal(res.user, true); - assert.equal(res.workspace, 55); - assert.equal(res.value, 55); - - service.dispose(); - - cleanUp(done); - }); - - }); - - }); - }); - }); - - test('keys', (done: () => void) => { - - function contains(array: string[], key: string): boolean { - return array.indexOf(key) >= 0; - } - - createWorkspace((workspaceDir, globalSettingsFile, cleanUp) => { - return createService(workspaceDir, globalSettingsFile).then(service => { - let keys = service.keys(); - - assert.ok(!contains(keys.default, 'something.missing')); - assert.ok(!contains(keys.user, 'something.missing')); - assert.ok(!contains(keys.workspace, 'something.missing')); - - assert.ok(contains(keys.default, 'workspaceLookup.service.testSetting')); - assert.ok(!contains(keys.user, 'workspaceLookup.service.testSetting')); - assert.ok(!contains(keys.workspace, 'workspaceLookup.service.testSetting')); - - fs.writeFileSync(globalSettingsFile, '{ "workspaceLookup.service.testSetting": true }'); - - return service.reloadConfiguration().then(() => { - keys = service.keys(); - - assert.ok(contains(keys.default, 'workspaceLookup.service.testSetting')); - assert.ok(contains(keys.user, 'workspaceLookup.service.testSetting')); - assert.ok(!contains(keys.workspace, 'workspaceLookup.service.testSetting')); - - const settingsFile = path.join(workspaceDir, '.vscode', 'settings.json'); - fs.writeFileSync(settingsFile, '{ "workspaceLookup.service.testSetting": 55 }'); - - return service.reloadConfiguration().then(() => { - keys = service.keys(); - - assert.ok(contains(keys.default, 'workspaceLookup.service.testSetting')); - assert.ok(contains(keys.user, 'workspaceLookup.service.testSetting')); - assert.ok(contains(keys.workspace, 'workspaceLookup.service.testSetting')); - - const settingsFile = path.join(workspaceDir, '.vscode', 'tasks.json'); - fs.writeFileSync(settingsFile, '{ "workspaceLookup.service.taskTestSetting": 55 }'); - - return service.reloadConfiguration().then(() => { - keys = service.keys(); - - assert.ok(!contains(keys.default, 'tasks.workspaceLookup.service.taskTestSetting')); - assert.ok(!contains(keys.user, 'tasks.workspaceLookup.service.taskTestSetting')); - assert.ok(contains(keys.workspace, 'tasks.workspaceLookup.service.taskTestSetting')); - - service.dispose(); - - cleanUp(done); - }); - }); - }); - }); - }); - }); - - test('values', (done: () => void) => { - createWorkspace((workspaceDir, globalSettingsFile, cleanUp) => { - return createService(workspaceDir, globalSettingsFile).then(service => { - let values = service.values(); - let value = values['workspaceLookup.service.testSetting']; - - assert.ok(value); - assert.equal(value.default, 'isSet'); - - fs.writeFileSync(globalSettingsFile, '{ "workspaceLookup.service.testSetting": true }'); - - return service.reloadConfiguration().then(() => { - values = service.values(); - value = values['workspaceLookup.service.testSetting']; - - assert.ok(value); - assert.equal(value.user, true); - - const settingsFile = path.join(workspaceDir, '.vscode', 'settings.json'); - fs.writeFileSync(settingsFile, '{ "workspaceLookup.service.testSetting": 55 }'); - - return service.reloadConfiguration().then(() => { - values = service.values(); - value = values['workspaceLookup.service.testSetting']; - - assert.ok(value); - assert.equal(value.user, true); - assert.equal(value.workspace, 55); - - done(); - }); - }); - }); - }); - }); -}); diff --git a/src/vs/workbench/services/configuration/test/node/configurationEditingService.test.ts b/src/vs/workbench/services/configuration/test/node/configurationEditingService.test.ts index 268e832f7db..8bfb2c6fbe5 100644 --- a/src/vs/workbench/services/configuration/test/node/configurationEditingService.test.ts +++ b/src/vs/workbench/services/configuration/test/node/configurationEditingService.test.ts @@ -18,31 +18,20 @@ import { parseArgs } from 'vs/platform/environment/node/argv'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; import extfs = require('vs/base/node/extfs'); -import { TestTextFileService, TestEditorGroupService, TestLifecycleService, TestBackupFileService, TestTextResourceConfigurationService } from 'vs/workbench/test/workbenchTestServices'; +import { TestTextFileService, TestTextResourceConfigurationService, workbenchInstantiationService } from 'vs/workbench/test/workbenchTestServices'; import uuid = require('vs/base/common/uuid'); import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; -import { WorkspaceService } from 'vs/workbench/services/configuration/node/configuration'; +import { WorkspaceService } from 'vs/workbench/services/configuration/node/configurationService'; import { FileService } from 'vs/workbench/services/files/node/fileService'; -import { ConfigurationEditingService } from 'vs/workbench/services/configuration/node/configurationEditingService'; -import { ConfigurationTarget, ConfigurationEditingError, ConfigurationEditingErrorCode } from 'vs/workbench/services/configuration/common/configurationEditing'; +import { ConfigurationEditingService, ConfigurationEditingError, ConfigurationEditingErrorCode } from 'vs/workbench/services/configuration/node/configurationEditingService'; import { IFileService } from 'vs/platform/files/common/files'; import { WORKSPACE_STANDALONE_CONFIGURATIONS } from 'vs/workbench/services/configuration/common/configuration'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IUntitledEditorService, UntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; -import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; -import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; -import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; +import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { TextModelResolverService } from 'vs/workbench/services/textmodelResolver/common/textModelResolverService'; -import { IModeService } from 'vs/editor/common/services/modeService'; -import { ModeServiceImpl } from 'vs/editor/common/services/modeServiceImpl'; -import { IModelService } from 'vs/editor/common/services/modelService'; -import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; -import { IChoiceService, IMessageService } from 'vs/platform/message/common/message'; +import { IChoiceService } from 'vs/platform/message/common/message'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; @@ -64,7 +53,6 @@ suite('ConfigurationEditingService', () => { let workspaceDir; let globalSettingsFile; let workspaceSettingsDir; - let choiceService; suiteSetup(() => { const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); @@ -114,7 +102,7 @@ suite('ConfigurationEditingService', () => { // Clear services if they are already created clearServices(); - instantiationService = new TestInstantiationService(); + instantiationService = workbenchInstantiationService(); const environmentService = new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, globalSettingsFile); instantiationService.stub(IEnvironmentService, environmentService); const workspacesService = instantiationService.stub(IWorkspacesService, {}); @@ -122,27 +110,10 @@ suite('ConfigurationEditingService', () => { instantiationService.stub(IWorkspaceContextService, workspaceService); return workspaceService.initialize(noWorkspace ? {} : workspaceDir).then(() => { instantiationService.stub(IConfigurationService, workspaceService); - instantiationService.stub(ILifecycleService, new TestLifecycleService()); - instantiationService.stub(IEditorGroupService, new TestEditorGroupService()); - instantiationService.stub(ITelemetryService, NullTelemetryService); - instantiationService.stub(IModeService, ModeServiceImpl); - instantiationService.stub(IModelService, instantiationService.createInstance(ModelServiceImpl)); instantiationService.stub(IFileService, new FileService(workspaceService, new TestTextResourceConfigurationService(), new TestConfigurationService(), { disableWatcher: true })); - instantiationService.stub(IUntitledEditorService, instantiationService.createInstance(UntitledEditorService)); instantiationService.stub(ITextFileService, instantiationService.createInstance(TestTextFileService)); instantiationService.stub(ITextModelService, instantiationService.createInstance(TextModelResolverService)); - instantiationService.stub(IBackupFileService, new TestBackupFileService()); - choiceService = instantiationService.stub(IChoiceService, { - choose: (severity, message, options, cancelId): TPromise => { - return TPromise.as(cancelId); - } - }); - instantiationService.stub(IMessageService, { - show: (severity, message, options, cancelId): void => { } - }); - testObject = instantiationService.createInstance(ConfigurationEditingService); - return workspaceService.initialize(noWorkspace ? {} : workspaceDir); }); } @@ -228,7 +199,6 @@ suite('ConfigurationEditingService', () => { const contents = fs.readFileSync(globalSettingsFile).toString('utf8'); const parsed = json.parse(contents); assert.equal(parsed['configurationEditing.service.testSetting'], 'value'); - assert.equal(instantiationService.get(IConfigurationService).lookup('configurationEditing.service.testSetting').value, 'value'); }); }); @@ -240,10 +210,6 @@ suite('ConfigurationEditingService', () => { const parsed = json.parse(contents); assert.equal(parsed['configurationEditing.service.testSetting'], 'value'); assert.equal(parsed['my.super.setting'], 'my.super.value'); - - const configurationService = instantiationService.get(IConfigurationService); - assert.equal(configurationService.lookup('configurationEditing.service.testSetting').value, 'value'); - assert.equal(configurationService.lookup('my.super.setting').value, 'my.super.value'); }); }); @@ -254,8 +220,6 @@ suite('ConfigurationEditingService', () => { const contents = fs.readFileSync(target).toString('utf8'); const parsed = json.parse(contents); assert.equal(parsed['service.testSetting'], 'value'); - const configurationService = instantiationService.get(IConfigurationService); - assert.equal(configurationService.lookup('tasks.service.testSetting').value, 'value'); }); }); @@ -268,10 +232,6 @@ suite('ConfigurationEditingService', () => { const parsed = json.parse(contents); assert.equal(parsed['service.testSetting'], 'value'); assert.equal(parsed['my.super.setting'], 'my.super.value'); - - const configurationService = instantiationService.get(IConfigurationService); - assert.equal(configurationService.lookup('launch.service.testSetting').value, 'value'); - assert.equal(configurationService.lookup('launch.my.super.setting').value, 'my.super.value'); }); }); diff --git a/src/vs/workbench/services/configuration/test/node/configurationService.test.ts b/src/vs/workbench/services/configuration/test/node/configurationService.test.ts new file mode 100644 index 00000000000..178de894bdd --- /dev/null +++ b/src/vs/workbench/services/configuration/test/node/configurationService.test.ts @@ -0,0 +1,592 @@ +/*--------------------------------------------------------------------------------------------- + * 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 assert from 'assert'; +import * as sinon from 'sinon'; +import * as fs from 'fs'; +import * as path from 'path'; +import * as os from 'os'; +import URI from 'vs/base/common/uri'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { ParsedArgs, IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; +import { parseArgs } from 'vs/platform/environment/node/argv'; +import extfs = require('vs/base/node/extfs'); +import uuid = require('vs/base/common/uuid'); +import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; +import { WorkspaceService } from 'vs/workbench/services/configuration/node/configurationService'; +import { FileChangeType, FileChangesEvent, IFileService } from 'vs/platform/files/common/files'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; +import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { workbenchInstantiationService, TestTextResourceConfigurationService, TestTextFileService } from 'vs/workbench/test/workbenchTestServices'; +import { FileService } from 'vs/workbench/services/files/node/fileService'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { TextModelResolverService } from 'vs/workbench/services/textmodelResolver/common/textModelResolverService'; + +class SettingsTestEnvironmentService extends EnvironmentService { + + constructor(args: ParsedArgs, _execPath: string, private customAppSettingsHome) { + super(args, _execPath); + } + + get appSettingsPath(): string { return this.customAppSettingsHome; } +} + +function setUpFolderWorkspace(folderName: string): TPromise<{ parentDir: string, folderDir: string }> { + const id = uuid.generateUuid(); + const parentDir = path.join(os.tmpdir(), 'vsctests', id); + return setUpFolder(folderName, parentDir).then(folderDir => ({ parentDir, folderDir })); +} + +function setUpFolder(folderName: string, parentDir: string): TPromise { + const folderDir = path.join(parentDir, folderName); + const workspaceSettingsDir = path.join(folderDir, '.vscode'); + return new TPromise((c, e) => { + extfs.mkdirp(workspaceSettingsDir, 493, (error) => { + if (error) { + e(error); + return null; + } + c(folderDir); + }); + }); +} + +function setUpWorkspace(folders: string[]): TPromise<{ parentDir: string, configPath: string }> { + + const id = uuid.generateUuid(); + const parentDir = path.join(os.tmpdir(), 'vsctests', id); + + return createDir(parentDir) + .then(() => { + const configPath = path.join(parentDir, 'vsctests.code-workspace'); + const workspace = { folders: folders.map(path => ({ path })) }; + fs.writeFileSync(configPath, JSON.stringify(workspace, null, '\t')); + + return TPromise.join(folders.map(folder => setUpFolder(folder, parentDir))) + .then(() => ({ parentDir, configPath })); + }); + +} + +function createDir(dir: string): TPromise { + return new TPromise((c, e) => { + extfs.mkdirp(dir, 493, (error) => { + if (error) { + e(error); + return null; + } + c(null); + }); + }); +} + +suite('WorkspaceContextService - Folder', () => { + + let workspaceName = `testWorkspace${uuid.generateUuid()}`, parentResource: string, workspaceResource: string, workspaceContextService: IWorkspaceContextService; + + setup(() => { + return setUpFolderWorkspace(workspaceName) + .then(({ parentDir, folderDir }) => { + parentResource = parentDir; + workspaceResource = folderDir; + const globalSettingsFile = path.join(parentDir, 'settings.json'); + const environmentService = new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, globalSettingsFile); + workspaceContextService = new WorkspaceService(environmentService, null); + return (workspaceContextService).initialize(folderDir); + }); + }); + + teardown(done => { + if (workspaceContextService) { + (workspaceContextService).dispose(); + } + if (parentResource) { + extfs.del(parentResource, os.tmpdir(), () => { }, done); + } + }); + + test('getWorkspace()', () => { + const actual = workspaceContextService.getWorkspace(); + + assert.equal(actual.folders.length, 1); + 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.ok(!actual.configuration); + }); + + test('getWorkbenchState()', () => { + const actual = workspaceContextService.getWorkbenchState(); + + assert.equal(actual, WorkbenchState.FOLDER); + }); + + test('getWorkspaceFolder()', () => { + const actual = workspaceContextService.getWorkspaceFolder(URI.file(path.join(workspaceResource, 'a'))); + + assert.equal(actual, workspaceContextService.getWorkspace().folders[0]); + }); + + test('isCurrentWorkspace() => true', () => { + assert.ok(workspaceContextService.isCurrentWorkspace(workspaceResource)); + }); + + test('isCurrentWorkspace() => false', () => { + assert.ok(!workspaceContextService.isCurrentWorkspace(workspaceResource + 'abc')); + }); +}); + +suite('WorkspaceContextService - Workspace', () => { + + let parentResource: string, testObject: IWorkspaceContextService; + + setup(() => { + return setUpWorkspace(['a', 'b']) + .then(({ parentDir, configPath }) => { + + parentResource = parentDir; + + const environmentService = new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, path.join(parentDir, 'settings.json')); + const workspaceService = new WorkspaceService(environmentService, null); + + const instantiationService = workbenchInstantiationService(); + instantiationService.stub(IWorkspaceContextService, workspaceService); + instantiationService.stub(IConfigurationService, workspaceService); + instantiationService.stub(IEnvironmentService, environmentService); + + return workspaceService.initialize({ id: configPath, configPath }).then(() => { + + instantiationService.stub(IFileService, new FileService(workspaceService, new TestTextResourceConfigurationService(), workspaceService, { disableWatcher: true })); + instantiationService.stub(ITextFileService, instantiationService.createInstance(TestTextFileService)); + instantiationService.stub(ITextModelService, instantiationService.createInstance(TextModelResolverService)); + workspaceService.setInstantiationService(instantiationService); + + testObject = workspaceService; + }); + }); + }); + + teardown(done => { + if (testObject) { + (testObject).dispose(); + } + if (parentResource) { + extfs.del(parentResource, os.tmpdir(), () => { }, done); + } + }); + + test('workspace folders', () => { + const actual = testObject.getWorkspace().folders; + + assert.equal(actual.length, 2); + assert.equal(path.basename(actual[0].uri.fsPath), 'a'); + assert.equal(path.basename(actual[1].uri.fsPath), 'b'); + }); + + test('add folders', () => { + const workspaceDir = path.dirname(testObject.getWorkspace().folders[0].uri.fsPath); + return testObject.addFolders([URI.file(path.join(workspaceDir, 'd')), URI.file(path.join(workspaceDir, 'c'))]) + .then(() => { + const actual = testObject.getWorkspace().folders; + + assert.equal(actual.length, 4); + assert.equal(path.basename(actual[0].uri.fsPath), 'a'); + assert.equal(path.basename(actual[1].uri.fsPath), 'b'); + assert.equal(path.basename(actual[2].uri.fsPath), 'd'); + assert.equal(path.basename(actual[3].uri.fsPath), 'c'); + }); + }); + + test('add folders triggers change event', () => { + const target = sinon.spy(); + testObject.onDidChangeWorkspaceFolders(target); + const workspaceDir = path.dirname(testObject.getWorkspace().folders[0].uri.fsPath); + return testObject.addFolders([URI.file(path.join(workspaceDir, 'd')), URI.file(path.join(workspaceDir, 'c'))]) + .then(() => assert.ok(target.called)); + }); + + test('remove folders', () => { + return testObject.removeFolders([testObject.getWorkspace().folders[0].uri]) + .then(() => { + const actual = testObject.getWorkspace().folders; + assert.equal(actual.length, 1); + assert.equal(path.basename(actual[0].uri.fsPath), 'b'); + }); + }); + + test('remove folders triggers change event', () => { + const target = sinon.spy(); + testObject.onDidChangeWorkspaceFolders(target); + return testObject.removeFolders([testObject.getWorkspace().folders[0].uri]) + .then(() => assert.ok(target.called)); + }); + +}); + +suite('WorkspaceConfigurationService - Folder', () => { + + let workspaceName = `testWorkspace${uuid.generateUuid()}`, parentResource: string, workspaceDir: string, testObject: IConfigurationService, globalSettingsFile: string; + + suiteSetup(() => { + const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); + configurationRegistry.registerConfiguration({ + 'id': '_test', + 'type': 'object', + 'properties': { + 'configurationService.folder.testSetting': { + 'type': 'string', + 'default': 'isSet' + }, + } + }); + }); + + setup(() => { + return setUpFolderWorkspace(workspaceName) + .then(({ parentDir, folderDir }) => { + + parentResource = parentDir; + workspaceDir = folderDir; + globalSettingsFile = path.join(parentDir, 'settings.json'); + + const instantiationService = workbenchInstantiationService(); + const environmentService = new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, globalSettingsFile); + const workspaceService = new WorkspaceService(environmentService, null); + instantiationService.stub(IWorkspaceContextService, workspaceService); + instantiationService.stub(IConfigurationService, workspaceService); + instantiationService.stub(IEnvironmentService, environmentService); + + return workspaceService.initialize(folderDir).then(() => { + instantiationService.stub(IFileService, new FileService(workspaceService, new TestTextResourceConfigurationService(), workspaceService, { disableWatcher: true })); + instantiationService.stub(ITextFileService, instantiationService.createInstance(TestTextFileService)); + instantiationService.stub(ITextModelService, instantiationService.createInstance(TextModelResolverService)); + workspaceService.setInstantiationService(instantiationService); + testObject = workspaceService; + }); + }); + }); + + teardown(done => { + if (testObject) { + (testObject).dispose(); + } + if (parentResource) { + extfs.del(parentResource, os.tmpdir(), () => { }, done); + } + }); + + test('defaults', () => { + assert.deepEqual(testObject.getValue('configurationService'), { 'folder': { 'testSetting': 'isSet' } }); + }); + + test('globals override defaults', () => { + fs.writeFileSync(globalSettingsFile, '{ "configurationService.folder.testSetting": "userValue" }'); + return testObject.reloadConfiguration() + .then(() => assert.equal(testObject.getValue('configurationService.folder.testSetting'), 'userValue')); + }); + + test('globals', () => { + fs.writeFileSync(globalSettingsFile, '{ "testworkbench.editor.tabs": true }'); + return testObject.reloadConfiguration() + .then(() => assert.equal(testObject.getValue('testworkbench.editor.tabs'), true)); + }); + + test('workspace settings', () => { + fs.writeFileSync(path.join(workspaceDir, '.vscode', 'settings.json'), '{ "testworkbench.editor.icons": true }'); + return testObject.reloadConfiguration() + .then(() => assert.equal(testObject.getValue('testworkbench.editor.icons'), true)); + }); + + test('workspace settings override user settings', () => { + fs.writeFileSync(globalSettingsFile, '{ "configurationService.folder.testSetting": "userValue" }'); + fs.writeFileSync(path.join(workspaceDir, '.vscode', 'settings.json'), '{ "configurationService.folder.testSetting": "workspaceValue" }'); + return testObject.reloadConfiguration() + .then(() => assert.equal(testObject.getValue('configurationService.folder.testSetting'), 'workspaceValue')); + }); + + test('workspace change triggers event', () => { + const settingsFile = path.join(workspaceDir, '.vscode', 'settings.json'); + fs.writeFileSync(settingsFile, '{ "configurationService.folder.testSetting": "workspaceValue" }'); + const event = new FileChangesEvent([{ resource: URI.file(settingsFile), type: FileChangeType.ADDED }]); + const target = sinon.spy(); + testObject.onDidChangeConfiguration(target); + return (testObject).handleWorkspaceFileEvents(event) + .then(() => { + assert.equal(testObject.getValue('configurationService.folder.testSetting'), 'workspaceValue'); + assert.ok(target.called); + }); + }); + + test('reload configuration emits events after global configuraiton changes', () => { + fs.writeFileSync(globalSettingsFile, '{ "testworkbench.editor.tabs": true }'); + const target = sinon.spy(); + testObject.onDidChangeConfiguration(target); + return testObject.reloadConfiguration().then(() => assert.ok(target.called)); + }); + + test('reload configuration emits events after workspace configuraiton changes', () => { + fs.writeFileSync(path.join(workspaceDir, '.vscode', 'settings.json'), '{ "configurationService.folder.testSetting": "workspaceValue" }'); + const target = sinon.spy(); + testObject.onDidChangeConfiguration(target); + return testObject.reloadConfiguration().then(() => assert.ok(target.called)); + }); + + test('reload configuration should not emit event if no changes', () => { + fs.writeFileSync(globalSettingsFile, '{ "testworkbench.editor.tabs": true }'); + fs.writeFileSync(path.join(workspaceDir, '.vscode', 'settings.json'), '{ "configurationService.folder.testSetting": "workspaceValue" }'); + return testObject.reloadConfiguration() + .then(() => { + const target = sinon.spy(); + testObject.onDidChangeConfiguration(() => { target(); }); + return testObject.reloadConfiguration() + .then(() => assert.ok(!target.called)); + }); + }); + + test('inspect', () => { + let actual = testObject.inspect('something.missing'); + assert.equal(actual.default, void 0); + assert.equal(actual.user, void 0); + assert.equal(actual.workspace, void 0); + assert.equal(actual.workspaceFolder, void 0); + assert.equal(actual.value, void 0); + + actual = testObject.inspect('configurationService.folder.testSetting'); + assert.equal(actual.default, 'isSet'); + assert.equal(actual.user, void 0); + assert.equal(actual.workspace, void 0); + assert.equal(actual.workspaceFolder, void 0); + assert.equal(actual.value, 'isSet'); + + fs.writeFileSync(globalSettingsFile, '{ "configurationService.folder.testSetting": "userValue" }'); + return testObject.reloadConfiguration() + .then(() => { + actual = testObject.inspect('configurationService.folder.testSetting'); + assert.equal(actual.default, 'isSet'); + assert.equal(actual.user, 'userValue'); + assert.equal(actual.workspace, void 0); + assert.equal(actual.workspaceFolder, void 0); + assert.equal(actual.value, 'userValue'); + + fs.writeFileSync(path.join(workspaceDir, '.vscode', 'settings.json'), '{ "configurationService.folder.testSetting": "workspaceValue" }'); + + return testObject.reloadConfiguration() + .then(() => { + actual = testObject.inspect('configurationService.folder.testSetting'); + assert.equal(actual.default, 'isSet'); + assert.equal(actual.user, 'userValue'); + assert.equal(actual.workspace, 'workspaceValue'); + assert.equal(actual.workspaceFolder, void 0); + assert.equal(actual.value, 'workspaceValue'); + }); + }); + }); + + test('keys', () => { + let actual = testObject.keys(); + assert.ok(actual.default.indexOf('configurationService.folder.testSetting') !== -1); + assert.deepEqual(actual.user, []); + assert.deepEqual(actual.workspace, []); + assert.deepEqual(actual.workspaceFolder, []); + + fs.writeFileSync(globalSettingsFile, '{ "configurationService.folder.testSetting": "userValue" }'); + return testObject.reloadConfiguration() + .then(() => { + actual = testObject.keys(); + assert.ok(actual.default.indexOf('configurationService.folder.testSetting') !== -1); + assert.deepEqual(actual.user, ['configurationService.folder.testSetting']); + assert.deepEqual(actual.workspace, []); + assert.deepEqual(actual.workspaceFolder, []); + + fs.writeFileSync(path.join(workspaceDir, '.vscode', 'settings.json'), '{ "configurationService.folder.testSetting": "workspaceValue" }'); + + return testObject.reloadConfiguration() + .then(() => { + actual = testObject.keys(); + assert.ok(actual.default.indexOf('configurationService.folder.testSetting') !== -1); + assert.deepEqual(actual.user, ['configurationService.folder.testSetting']); + assert.deepEqual(actual.workspace, ['configurationService.folder.testSetting']); + assert.deepEqual(actual.workspaceFolder, []); + }); + }); + }); + + test('update user configuration', () => { + return testObject.updateValue('configurationService.folder.testSetting', 'value', ConfigurationTarget.USER) + .then(() => assert.equal(testObject.getValue('configurationService.folder.testSetting'), 'value')); + }); + + test('update workspace configuration', () => { + return testObject.updateValue('tasks.service.testSetting', 'value', ConfigurationTarget.WORKSPACE) + .then(() => assert.equal(testObject.getValue('tasks.service.testSetting'), 'value')); + }); + + test('update tasks configuration', () => { + return testObject.updateValue('tasks', { 'version': '1.0.0', tasks: [{ 'taskName': 'myTask' }] }, ConfigurationTarget.WORKSPACE) + .then(() => assert.deepEqual(testObject.getValue('tasks'), { 'version': '1.0.0', tasks: [{ 'taskName': 'myTask' }] })); + }); + + test('update user configuration should trigger change event before promise is resolve', () => { + const target = sinon.spy(); + testObject.onDidChangeConfiguration(target); + return testObject.updateValue('configurationService.folder.testSetting', 'value', ConfigurationTarget.USER) + .then(() => assert.ok(target.called)); + }); + + test('update workspace configuration should trigger change event before promise is resolve', () => { + const target = sinon.spy(); + testObject.onDidChangeConfiguration(target); + return testObject.updateValue('configurationService.folder.testSetting', 'value', ConfigurationTarget.WORKSPACE) + .then(() => assert.ok(target.called)); + }); + + test('update task configuration should trigger change event before promise is resolve', () => { + const target = sinon.spy(); + testObject.onDidChangeConfiguration(target); + return testObject.updateValue('tasks', { 'version': '1.0.0', tasks: [{ 'taskName': 'myTask' }] }, ConfigurationTarget.WORKSPACE) + .then(() => assert.ok(target.called)); + }); + + test('initialize with different folder triggers configuration event if there are changes', () => { + return setUpFolderWorkspace(`testWorkspace${uuid.generateUuid()}`) + .then(({ folderDir }) => { + const target = sinon.spy(); + testObject.onDidChangeConfiguration(target); + + fs.writeFileSync(path.join(folderDir, '.vscode', 'settings.json'), '{ "configurationService.folder.testSetting": "workspaceValue2" }'); + return (testObject).initialize(folderDir) + .then(() => { + assert.equal(testObject.getValue('configurationService.folder.testSetting'), 'workspaceValue2'); + assert.ok(target.called); + }); + }); + }); + + test('initialize with different folder triggers configuration event if there are no changes', () => { + fs.writeFileSync(globalSettingsFile, '{ "configurationService.folder.testSetting": "workspaceValue2" }'); + return testObject.reloadConfiguration() + .then(() => setUpFolderWorkspace(`testWorkspace${uuid.generateUuid()}`)) + .then(({ folderDir }) => { + const target = sinon.spy(); + testObject.onDidChangeConfiguration(() => target()); + fs.writeFileSync(path.join(folderDir, '.vscode', 'settings.json'), '{ "configurationService.folder.testSetting": "workspaceValue2" }'); + return (testObject).initialize(folderDir) + .then(() => { + assert.equal(testObject.getValue('configurationService.folder.testSetting'), 'workspaceValue2'); + assert.ok(!target.called); + }); + }); + }); +}); + +suite('WorkspaceConfigurationService - Update (Multiroot)', () => { + + let parentResource: string, workspaceContextService: IWorkspaceContextService, testObject: IConfigurationService; + + suiteSetup(() => { + const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); + configurationRegistry.registerConfiguration({ + 'id': '_test', + 'type': 'object', + 'properties': { + 'configurationService.workspace.testSetting': { + 'type': 'string', + 'default': 'isSet' + }, + 'configurationService.workspace.testResourceSetting': { + 'type': 'string', + 'default': 'isSet', + scope: ConfigurationScope.RESOURCE + } + } + }); + }); + + setup(() => { + return setUpWorkspace(['1', '2']) + .then(({ parentDir, configPath }) => { + + parentResource = parentDir; + + const environmentService = new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, path.join(parentDir, 'settings.json')); + const workspaceService = new WorkspaceService(environmentService, null); + + const instantiationService = workbenchInstantiationService(); + instantiationService.stub(IWorkspaceContextService, workspaceService); + instantiationService.stub(IConfigurationService, workspaceService); + instantiationService.stub(IEnvironmentService, environmentService); + + return workspaceService.initialize({ id: configPath, configPath }).then(() => { + + instantiationService.stub(IFileService, new FileService(workspaceService, new TestTextResourceConfigurationService(), workspaceService, { disableWatcher: true })); + instantiationService.stub(ITextFileService, instantiationService.createInstance(TestTextFileService)); + instantiationService.stub(ITextModelService, instantiationService.createInstance(TextModelResolverService)); + workspaceService.setInstantiationService(instantiationService); + + workspaceContextService = workspaceService; + testObject = workspaceService; + }); + }); + }); + + teardown(done => { + if (testObject) { + (testObject).dispose(); + } + if (parentResource) { + extfs.del(parentResource, os.tmpdir(), () => { }, done); + } + }); + + test('update user configuration', () => { + return testObject.updateValue('configurationService.workspace.testSetting', 'userValue', ConfigurationTarget.USER) + .then(() => assert.equal(testObject.getValue('configurationService.workspace.testSetting'), 'userValue')); + }); + + test('update user configuration should trigger change event before promise is resolve', () => { + const target = sinon.spy(); + testObject.onDidChangeConfiguration(target); + return testObject.updateValue('configurationService.workspace.testSetting', 'userValue', ConfigurationTarget.USER) + .then(() => assert.ok(target.called)); + }); + + test('update workspace configuration', () => { + return testObject.updateValue('configurationService.workspace.testSetting', 'workspaceValue', ConfigurationTarget.WORKSPACE) + .then(() => assert.equal(testObject.getValue('configurationService.workspace.testSetting'), 'workspaceValue')); + }); + + test('update workspace configuration should trigger change event before promise is resolve', () => { + const target = sinon.spy(); + testObject.onDidChangeConfiguration(target); + return testObject.updateValue('configurationService.workspace.testSetting', 'workspaceValue', ConfigurationTarget.WORKSPACE) + .then(() => assert.ok(target.called)); + }); + + test('update workspace folder configuration', () => { + const workspace = workspaceContextService.getWorkspace(); + return testObject.updateValue('configurationService.workspace.testResourceSetting', 'workspaceFolderValue', { resource: workspace.folders[0].uri }, ConfigurationTarget.WORKSPACE_FOLDER) + .then(() => assert.equal(testObject.getValue('configurationService.workspace.testResourceSetting', { resource: workspace.folders[0].uri }), 'workspaceFolderValue')); + }); + + test('update workspace folder configuration should trigger change event before promise is resolve', () => { + const workspace = workspaceContextService.getWorkspace(); + const target = sinon.spy(); + testObject.onDidChangeConfiguration(target); + return testObject.updateValue('configurationService.workspace.testResourceSetting', 'workspaceFolderValue', { resource: workspace.folders[0].uri }, ConfigurationTarget.WORKSPACE_FOLDER) + .then(() => assert.ok(target.called)); + }); + + test('update tasks configuration', () => { + const workspace = workspaceContextService.getWorkspace(); + return testObject.updateValue('tasks', { 'version': '1.0.0', tasks: [{ 'taskName': 'myTask' }] }, { resource: workspace.folders[0].uri }, ConfigurationTarget.WORKSPACE_FOLDER) + .then(() => assert.deepEqual(testObject.getValue('tasks', { resource: workspace.folders[0].uri }), { 'version': '1.0.0', tasks: [{ 'taskName': 'myTask' }] })); + }); +}); 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 7841abf9606..fce774139c1 100644 --- a/src/vs/workbench/services/configurationResolver/test/node/configurationResolverService.test.ts +++ b/src/vs/workbench/services/configurationResolver/test/node/configurationResolverService.test.ts @@ -7,7 +7,7 @@ import assert = require('assert'); import uri from 'vs/base/common/uri'; import platform = require('vs/base/common/platform'); import { TPromise } from 'vs/base/common/winjs.base'; -import { IConfigurationService, getConfigurationValue, IConfigurationOverrides, IConfigurationValue } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationService, getConfigurationValue, IConfigurationOverrides } from 'vs/platform/configuration/common/configuration'; 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'; @@ -348,13 +348,14 @@ class MockConfigurationService implements IConfigurationService { public _serviceBrand: any; public serviceId = IConfigurationService; public constructor(private configuration: any = {}) { } - public reloadConfiguration(section?: string): TPromise { return TPromise.as(this.getConfiguration()); } - public lookup(key: string, overrides?: IConfigurationOverrides): IConfigurationValue { return { value: getConfigurationValue(this.getConfiguration(), key), default: getConfigurationValue(this.getConfiguration(), key), user: getConfigurationValue(this.getConfiguration(), key), workspace: void 0, folder: void 0 }; } - public keys() { return { default: [], user: [], workspace: [], folder: [] }; } - public values() { return {}; } + public inspect(key: string, overrides?: IConfigurationOverrides): any { return { value: getConfigurationValue(this.getConfiguration(), key), default: getConfigurationValue(this.getConfiguration(), key), user: getConfigurationValue(this.getConfiguration(), key), workspaceFolder: void 0, folder: void 0 }; } + public keys() { return { default: [], user: [], workspace: [], workspaceFolder: [] }; } public getConfiguration(): any { return this.configuration; } + public getValue(key: string): any { return getConfigurationValue(this.getConfiguration(), key); } + public updateValue(): TPromise { return null; } public getConfigurationData(): any { return null; } - public onDidUpdateConfiguration() { return { dispose() { } }; } + public onDidChangeConfiguration() { return { dispose() { } }; } + public reloadConfiguration() { return null; } } class MockCommandService implements ICommandService { diff --git a/src/vs/workbench/services/decorations/browser/decorations.ts b/src/vs/workbench/services/decorations/browser/decorations.ts index 72f06102f77..6356737cb2d 100644 --- a/src/vs/workbench/services/decorations/browser/decorations.ts +++ b/src/vs/workbench/services/decorations/browser/decorations.ts @@ -7,24 +7,23 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import URI from 'vs/base/common/uri'; import Event from 'vs/base/common/event'; -import Severity from 'vs/base/common/severity'; import { ColorIdentifier } from 'vs/platform/theme/common/colorRegistry'; import { IDisposable } from 'vs/base/common/lifecycle'; -export const IResourceDecorationsService = createDecorator('IFileDecorationsService'); +export const IDecorationsService = createDecorator('IFileDecorationsService'); -export interface IResourceDecorationData { - readonly severity: Severity; +export interface IDecorationData { + readonly weight?: number; readonly color?: ColorIdentifier; readonly letter?: string; - readonly tooltip?: string; + readonly title?: string; + readonly bubble?: boolean; } -export interface IResourceDecoration { +export interface IDecoration { readonly _decoBrand: undefined; - readonly severity: Severity; - readonly letter?: string; - readonly tooltip?: string; + readonly weight?: number; + readonly title?: string; readonly labelClassName?: string; readonly badgeClassName?: string; } @@ -32,20 +31,20 @@ export interface IResourceDecoration { export interface IDecorationsProvider { readonly label: string; readonly onDidChange: Event; - provideDecorations(uri: URI): IResourceDecorationData | Thenable; + provideDecorations(uri: URI): IDecorationData | Thenable; } export interface IResourceDecorationChangeEvent { affectsResource(uri: URI): boolean; } -export interface IResourceDecorationsService { +export interface IDecorationsService { readonly _serviceBrand: any; readonly onDidChangeDecorations: Event; - registerDecortionsProvider(provider: IDecorationsProvider): IDisposable; + registerDecorationsProvider(provider: IDecorationsProvider): IDisposable; - getTopDecoration(uri: URI, includeChildren: boolean): IResourceDecoration; + getDecoration(uri: URI, includeChildren: boolean): IDecoration; } diff --git a/src/vs/workbench/services/decorations/browser/decorationsService.ts b/src/vs/workbench/services/decorations/browser/decorationsService.ts index 501c6ae6735..cd004e68f20 100644 --- a/src/vs/workbench/services/decorations/browser/decorationsService.ts +++ b/src/vs/workbench/services/decorations/browser/decorationsService.ts @@ -5,25 +5,120 @@ 'use strict'; import URI from 'vs/base/common/uri'; -import Severity from 'vs/base/common/severity'; import Event, { Emitter, debounceEvent, any } from 'vs/base/common/event'; -import { IResourceDecorationsService, IResourceDecoration, IResourceDecorationChangeEvent, IDecorationsProvider, IResourceDecorationData } from './decorations'; +import { IDecorationsService, IDecoration, IResourceDecorationChangeEvent, IDecorationsProvider, IDecorationData } from './decorations'; import { TernarySearchTree } from 'vs/base/common/map'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { isThenable } from 'vs/base/common/async'; import { LinkedList } from 'vs/base/common/linkedList'; import { createStyleSheet, createCSSRule, removeCSSRulesContainingSelector } from 'vs/base/browser/dom'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService'; import { IdGenerator } from 'vs/base/common/idGenerator'; -import { listActiveSelectionForeground, ColorIdentifier } from 'vs/platform/theme/common/colorRegistry'; +import { listActiveSelectionForeground } from 'vs/platform/theme/common/colorRegistry'; +import { IIterator } from 'vs/base/common/iterator'; +class DecorationRule { -class DecorationColors { + static keyOf(data: IDecorationData | IDecorationData[]): string { + if (Array.isArray(data)) { + return data.map(DecorationRule.keyOf).join(','); + } else { + const { color, letter } = data; + return `${color}/${letter}`; + } + } + + private static readonly _classNames = new IdGenerator('monaco-decorations-style-'); + + readonly data: IDecorationData | IDecorationData[]; + readonly labelClassName: string; + readonly badgeClassName: string; + + constructor(data: IDecorationData | IDecorationData[]) { + this.data = data; + this.labelClassName = DecorationRule._classNames.nextId(); + this.badgeClassName = DecorationRule._classNames.nextId(); + } + + appendCSSRules(element: HTMLStyleElement, theme: ITheme): void { + if (!Array.isArray(this.data)) { + this._appendForOne(this.data, element, theme); + } else { + this._appendForMany(this.data, element, theme); + } + } + + private _appendForOne(data: IDecorationData, element: HTMLStyleElement, theme: ITheme): void { + const { color, letter } = data; + // label + createCSSRule(`.${this.labelClassName}`, `color: ${theme.getColor(color) || 'inherit'};`, element); + createCSSRule(`.focused .selected .${this.labelClassName}`, `color: inherit; opacity: inherit;`, element); + // badge + if (letter) { + createCSSRule(`.${this.badgeClassName}`, `background-color: ${theme.getColor(color)}; color: ${theme.getColor(listActiveSelectionForeground)};`, element); + createCSSRule(`.${this.badgeClassName}::before`, `content: "${letter}"`, element); + } + } + + private _appendForMany(data: IDecorationData[], element: HTMLStyleElement, theme: ITheme): void { + // label + const { color } = data[0]; + createCSSRule(`.${this.labelClassName}`, `color: ${theme.getColor(color) || 'inherit'};`, element); + createCSSRule(`.focused .selected .${this.labelClassName}`, `color: inherit; opacity: inherit;`, element); + + // badge + let letters: string[] = []; + let colors: string[] = []; + for (const deco of data) { + letters.push(deco.letter); + colors.push(`${theme.getColor(deco.color).toString()} ${100 / data.length}%`); + } + createCSSRule(`.${this.badgeClassName}::before`, `content: "${letters.join('\u2002')}"`, element); + createCSSRule( + `.${this.badgeClassName}`, + `background: linear-gradient(90deg, ${colors.join()}); color: ${theme.getColor(listActiveSelectionForeground)};`, + element + ); + } + + removeCSSRules(element: HTMLStyleElement): void { + removeCSSRulesContainingSelector(this.labelClassName, element); + removeCSSRulesContainingSelector(this.badgeClassName, element); + } +} + +class ResourceDecoration implements IDecoration { + + static from(data: IDecorationData | IDecorationData[]): ResourceDecoration { + let result = new ResourceDecoration(data); + if (Array.isArray(data)) { + result.weight = data[0].weight; + result.title = data.map(d => d.title).join(', '); + } else { + result.weight = data.weight; + result.title = data.title; + } + return result; + } + + _decoBrand: undefined; + _data: IDecorationData | IDecorationData[]; + + weight?: number; + title?: string; + labelClassName?: string; + badgeClassName?: string; + + private constructor(data: IDecorationData | IDecorationData[]) { + this._data = data; + } +} + +class DecorationStyles { private readonly _disposables: IDisposable[]; private readonly _styleElement = createStyleSheet(); - private readonly _classNames = new IdGenerator('monaco-decoration-styles-'); - private readonly _classNames2ColorIds = new Map(); + private readonly _decorationRules = new Map(); constructor( private _themeService: IThemeService, @@ -35,58 +130,61 @@ class DecorationColors { dispose(): void { dispose(this._disposables); - this._styleElement.innerHTML = ''; + this._styleElement.parentElement.removeChild(this._styleElement); } - makeResourceDecoration(decoration: IResourceDecorationData): IResourceDecoration { - if (!decoration) { - return undefined; + asDecoration(data: IDecorationData | IDecorationData[]): ResourceDecoration { + let key = DecorationRule.keyOf(data); + let rule = this._decorationRules.get(key); + let result = ResourceDecoration.from(data); + + if (!rule) { + // new css rule + rule = new DecorationRule(data); + this._decorationRules.set(key, rule); + rule.appendCSSRules(this._styleElement, this._themeService.getTheme()); } - let { severity, letter, tooltip } = decoration; - let labelClassName, badgeClassName; - - let tuple = this._classNames2ColorIds.get(decoration.color); - - if (tuple) { - // from cache - labelClassName = tuple[0]; - badgeClassName = tuple[1]; - } else { - // new css rules - labelClassName = this._classNames.nextId(); - badgeClassName = this._classNames.nextId(); - this._classNames2ColorIds.set(decoration.color, [labelClassName, badgeClassName]); - this._createCssRules(labelClassName, badgeClassName, decoration.color); - } - - return { - _decoBrand: undefined, - severity, - letter, - tooltip, - labelClassName, - badgeClassName - }; + result.labelClassName = rule.labelClassName; + result.badgeClassName = rule.badgeClassName; + return result; } private _onThemeChange(): void { - this._classNames2ColorIds.forEach((tuple, color) => { - const [labelClassName, badgeClassName] = tuple; - removeCSSRulesContainingSelector(labelClassName, this._styleElement); - removeCSSRulesContainingSelector(badgeClassName, this._styleElement); - this._createCssRules(labelClassName, badgeClassName, color); + this._decorationRules.forEach(rule => { + rule.removeCSSRules(this._styleElement); + rule.appendCSSRules(this._styleElement, this._themeService.getTheme()); }); } - private _createCssRules(labelClassName: string, badgeClassName: string, color: ColorIdentifier): void { - const theme = this._themeService.getTheme(); - // label - createCSSRule(`.${labelClassName}`, `color: ${theme.getColor(color)}`, this._styleElement); - createCSSRule(`.selected .${labelClassName}`, `color: ${theme.getColor(listActiveSelectionForeground)}`, this._styleElement); - - // badge - createCSSRule(`.${badgeClassName}`, `background-color: ${theme.getColor(color)}; color: ${theme.getColor(listActiveSelectionForeground)};`, this._styleElement); + cleanUp(iter: IIterator): void { + // remove every rule for which no more + // decoration (data) is kept. this isn't cheap + let usedDecorations = new Set(); + for (let e = iter.next(); !e.done; e = iter.next()) { + e.value.data.forEach(value => { + if (value instanceof ResourceDecoration) { + if (Array.isArray(value._data)) { + value._data.forEach(data => usedDecorations.add(data)); + } else { + usedDecorations.add(value._data); + } + } + }); + } + this._decorationRules.forEach((value, index) => { + const { data } = value; + let remove: boolean; + if (Array.isArray(data)) { + remove = data.every(data => !usedDecorations.has(data)); + } else if (!usedDecorations.has(data)) { + remove = true; + } + if (remove) { + value.removeCSSRules(this._styleElement); + this._decorationRules.delete(index); + } + }); } } @@ -118,54 +216,63 @@ class FileDecorationChangeEvent implements IResourceDecorationChangeEvent { class DecorationProviderWrapper { - private readonly _data = TernarySearchTree.forPaths | IResourceDecoration>(); + readonly data = TernarySearchTree.forPaths | IDecorationData>(); private readonly _dispoable: IDisposable; constructor( - private readonly _decorationStyles: DecorationColors, private readonly _provider: IDecorationsProvider, - private readonly _emitter: Emitter + private readonly _uriEmitter: Emitter, + private readonly _flushEmitter: Emitter ) { this._dispoable = this._provider.onDidChange(uris => { - for (const uri of uris) { - this._data.delete(uri.toString()); - this._fetchData(uri); + if (!uris) { + // flush event -> drop all data, can affect everything + this.data.clear(); + this._flushEmitter.fire({ affectsResource() { return true; } }); + + } else { + // selective changes -> drop for resource, fetch again, send event + for (const uri of uris) { + this.data.delete(uri.toString()); + this._fetchData(uri); + } } }); } dispose(): void { this._dispoable.dispose(); - this._data.clear(); + this.data.clear(); } knowsAbout(uri: URI): boolean { - return Boolean(this._data.get(uri.toString())) || Boolean(this._data.findSuperstr(uri.toString())); + return Boolean(this.data.get(uri.toString())) || Boolean(this.data.findSuperstr(uri.toString())); } - getOrRetrieve(uri: URI, includeChildren: boolean, callback: (data: IResourceDecoration, isChild: boolean) => void): void { + getOrRetrieve(uri: URI, includeChildren: boolean, callback: (data: IDecorationData, isChild: boolean) => void): void { const key = uri.toString(); - let item = this._data.get(key); + let item = this.data.get(key); if (isThenable(item)) { // pending -> still waiting return; } - if (item === undefined && !includeChildren) { - // unknown, a leaf node -> trigger request + if (item === undefined) { + // unknown -> trigger request item = this._fetchData(uri); } if (item) { - // leaf node + // found something callback(item, false); } + if (includeChildren) { // (resolved) children - const childTree = this._data.findSuperstr(key); + const childTree = this.data.findSuperstr(key); if (childTree) { - childTree.forEach(([, value]) => { + childTree.forEach(value => { if (value && !isThenable(value)) { callback(value, true); } @@ -174,7 +281,7 @@ class DecorationProviderWrapper { } } - private _fetchData(uri: URI): IResourceDecoration { + private _fetchData(uri: URI): IDecorationData { const dataOrThenable = this._provider.provideDecorations(uri); if (!isThenable(dataOrThenable)) { @@ -185,29 +292,30 @@ class DecorationProviderWrapper { // async -> we have a result soon const request = Promise.resolve(dataOrThenable) .then(data => this._keepItem(uri, data)) - .catch(_ => this._data.delete(uri.toString())); + .catch(_ => this.data.delete(uri.toString())); - this._data.set(uri.toString(), request); + this.data.set(uri.toString(), request); return undefined; } } - private _keepItem(uri: URI, data: IResourceDecorationData): IResourceDecoration { - let deco = data ? this._decorationStyles.makeResourceDecoration(data) : null; - this._data.set(uri.toString(), deco); - this._emitter.fire(uri); + private _keepItem(uri: URI, data: IDecorationData): IDecorationData { + let deco = data ? data : null; + this.data.set(uri.toString(), deco); + this._uriEmitter.fire(uri); return deco; } } -export class FileDecorationsService implements IResourceDecorationsService { +export class FileDecorationsService implements IDecorationsService { _serviceBrand: any; private readonly _data = new LinkedList(); private readonly _onDidChangeDecorationsDelayed = new Emitter(); private readonly _onDidChangeDecorations = new Emitter(); - private readonly _decorationStyles: DecorationColors; + private readonly _decorationStyles: DecorationStyles; + private readonly _disposables: IDisposable[]; readonly onDidChangeDecorations: Event = any( this._onDidChangeDecorations.event, @@ -219,22 +327,43 @@ export class FileDecorationsService implements IResourceDecorationsService { constructor( @IThemeService themeService: IThemeService, + cleanUpCount: number = 17 ) { - this._decorationStyles = new DecorationColors(themeService); + this._decorationStyles = new DecorationStyles(themeService); + + // every so many events we check if there are + // css styles that we don't need anymore + let count = 0; + let reg = this.onDidChangeDecorations(() => { + if (++count % cleanUpCount === 0) { + this._decorationStyles.cleanUp(this._data.iterator()); + } + }); + + this._disposables = [ + reg, + this._decorationStyles + ]; } dispose(): void { - this._decorationStyles.dispose(); + dispose(this._disposables); } - registerDecortionsProvider(provider: IDecorationsProvider): IDisposable { + registerDecorationsProvider(provider: IDecorationsProvider): IDisposable { const wrapper = new DecorationProviderWrapper( - this._decorationStyles, provider, - this._onDidChangeDecorationsDelayed + this._onDidChangeDecorationsDelayed, + this._onDidChangeDecorations ); const remove = this._data.push(wrapper); + + this._onDidChangeDecorations.fire({ + // everything might have changed + affectsResource() { return true; } + }); + return { dispose: () => { // fire event that says 'yes' for any resource @@ -246,33 +375,28 @@ export class FileDecorationsService implements IResourceDecorationsService { }; } - getTopDecoration(uri: URI, includeChildren: boolean): IResourceDecoration { - let top: IResourceDecoration; + getDecoration(uri: URI, includeChildren: boolean): IDecoration { + let data: IDecorationData[] = []; + let onlyChildren = true; for (let iter = this._data.iterator(), next = iter.next(); !next.done; next = iter.next()) { - next.value.getOrRetrieve(uri, includeChildren, (candidate, isChild) => { - top = FileDecorationsService._pickBest(top, candidate); - if (isChild && top === candidate) { - // only bubble up color - top = { - _decoBrand: undefined, - severity: top.severity, - labelClassName: top.labelClassName - }; + next.value.getOrRetrieve(uri, includeChildren, (deco, isChild) => { + if (!isChild || deco.bubble) { + data.push(deco); + onlyChildren = onlyChildren && isChild; } }); } - return top; - } - private static _pickBest(a: IResourceDecoration, b: IResourceDecoration): IResourceDecoration { - if (!a) { - return b; - } else if (!b) { - return a; - } else if (Severity.compare(a.severity, b.severity) < 0) { - return a; + if (data.length === 0) { + return undefined; + } else if (onlyChildren) { + let result = this._decorationStyles.asDecoration(data.sort((a, b) => b.weight - a.weight)[0]); + result.badgeClassName = ''; + return result; + } else if (data.length === 1) { + return this._decorationStyles.asDecoration(data[0]); } else { - return b; + return this._decorationStyles.asDecoration(data.sort((a, b) => b.weight - a.weight)); } } } diff --git a/src/vs/workbench/services/decorations/test/browser/decorationsService.test.ts b/src/vs/workbench/services/decorations/test/browser/decorationsService.test.ts index f6a004f6fb0..721051fb31a 100644 --- a/src/vs/workbench/services/decorations/test/browser/decorationsService.test.ts +++ b/src/vs/workbench/services/decorations/test/browser/decorationsService.test.ts @@ -7,10 +7,9 @@ import * as assert from 'assert'; import { FileDecorationsService } from 'vs/workbench/services/decorations/browser/decorationsService'; -import { IDecorationsProvider, IResourceDecorationData } from 'vs/workbench/services/decorations/browser/decorations'; +import { IDecorationsProvider, IDecorationData } from 'vs/workbench/services/decorations/browser/decorations'; import URI from 'vs/base/common/uri'; import Event, { toPromise } from 'vs/base/common/event'; -import Severity from 'vs/base/common/severity'; import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; suite('DecorationsService', function () { @@ -29,23 +28,22 @@ suite('DecorationsService', function () { let uri = URI.parse('foo:bar'); let callCounter = 0; - service.registerDecortionsProvider(new class implements IDecorationsProvider { + service.registerDecorationsProvider(new class implements IDecorationsProvider { readonly label: string = 'Test'; readonly onDidChange: Event = Event.None; provideDecorations(uri: URI) { callCounter += 1; - return new Promise(resolve => { + return new Promise(resolve => { setTimeout(() => resolve({ - severity: Severity.Info, color: 'someBlue', - letter: 'T' + title: 'T' })); }); } }); // trigger -> async - assert.equal(service.getTopDecoration(uri, false), undefined); + assert.equal(service.getDecoration(uri, false), undefined); assert.equal(callCounter, 1); // event when result is computed @@ -53,7 +51,7 @@ suite('DecorationsService', function () { assert.equal(e.affectsResource(uri), true); // sync result - assert.deepEqual(service.getTopDecoration(uri, false).letter, 'T'); + assert.deepEqual(service.getDecoration(uri, false).title, 'T'); assert.equal(callCounter, 1); }); }); @@ -63,17 +61,17 @@ suite('DecorationsService', function () { let uri = URI.parse('foo:bar'); let callCounter = 0; - service.registerDecortionsProvider(new class implements IDecorationsProvider { + service.registerDecorationsProvider(new class implements IDecorationsProvider { readonly label: string = 'Test'; readonly onDidChange: Event = Event.None; provideDecorations(uri: URI) { callCounter += 1; - return { severity: Severity.Info, color: 'someBlue', letter: 'Z' }; + return { color: 'someBlue', title: 'Z' }; } }); // trigger -> sync - assert.deepEqual(service.getTopDecoration(uri, false).letter, 'Z'); + assert.deepEqual(service.getDecoration(uri, false).title, 'Z'); assert.equal(callCounter, 1); }); @@ -81,28 +79,67 @@ suite('DecorationsService', function () { let uri = URI.parse('foo:bar'); let callCounter = 0; - let reg = service.registerDecortionsProvider(new class implements IDecorationsProvider { + let reg = service.registerDecorationsProvider(new class implements IDecorationsProvider { readonly label: string = 'Test'; readonly onDidChange: Event = Event.None; provideDecorations(uri: URI) { callCounter += 1; - return { severity: Severity.Info, color: 'someBlue', letter: 'J' }; + return { color: 'someBlue', title: 'J' }; } }); // trigger -> sync - assert.deepEqual(service.getTopDecoration(uri, false).letter, 'J'); + assert.deepEqual(service.getDecoration(uri, false).title, 'J'); assert.equal(callCounter, 1); // un-register -> ensure good event let didSeeEvent = false; service.onDidChangeDecorations(e => { assert.equal(e.affectsResource(uri), true); - assert.deepEqual(service.getTopDecoration(uri, false), undefined); + assert.deepEqual(service.getDecoration(uri, false), undefined); assert.equal(callCounter, 1); didSeeEvent = true; }); reg.dispose(); assert.equal(didSeeEvent, true); }); + + test('No default bubbling', function () { + + let reg = service.registerDecorationsProvider({ + label: 'Test', + onDidChange: Event.None, + provideDecorations(uri: URI) { + return uri.path.match(/\.txt/) + ? { title: '.txt' } + : undefined; + } + }); + + let childUri = URI.parse('file:///some/path/some/file.txt'); + + let deco = service.getDecoration(childUri, false); + assert.equal(deco.title, '.txt'); + + deco = service.getDecoration(childUri.with({ path: 'some/path/' }), true); + assert.equal(deco, undefined); + reg.dispose(); + + // bubble + reg = service.registerDecorationsProvider({ + label: 'Test', + onDidChange: Event.None, + provideDecorations(uri: URI) { + return uri.path.match(/\.txt/) + ? { title: '.txt.bubble', bubble: true } + : undefined; + } + }); + + deco = service.getDecoration(childUri, false); + assert.equal(deco.title, '.txt.bubble'); + + deco = service.getDecoration(childUri.with({ path: 'some/path/' }), true); + assert.equal(deco.title, '.txt.bubble'); + }); }); diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts deleted file mode 100644 index df0ec9e78cb..00000000000 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ /dev/null @@ -1,366 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * 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 { TPromise } from 'vs/base/common/winjs.base'; -import URI from 'vs/base/common/uri'; -import network = require('vs/base/common/network'); -import { Registry } from 'vs/platform/registry/common/platform'; -import { basename, dirname } from 'vs/base/common/paths'; -import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; -import { EditorInput, EditorOptions, TextEditorOptions, Extensions as EditorExtensions, SideBySideEditorInput, IFileEditorInput, IFileInputFactory, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor'; -import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; -import { IUntitledEditorService, UNTITLED_SCHEMA } from 'vs/workbench/services/untitled/common/untitledEditorService'; -import { IWorkbenchEditorService, IResourceInputType } from 'vs/workbench/services/editor/common/editorService'; -import { IEditorInput, IEditorOptions, ITextEditorOptions, Position, Direction, IEditor, IResourceInput, IResourceDiffInput, IResourceSideBySideInput, IUntitledResourceInput } from 'vs/platform/editor/common/editor'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import nls = require('vs/nls'); -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; - openEditor(input?: IEditorInput, options?: IEditorOptions | ITextEditorOptions, position?: Position): TPromise; - openEditors(editors: { input: IEditorInput, position: Position, options?: IEditorOptions | ITextEditorOptions }[]): TPromise; - replaceEditors(editors: { toReplace: IEditorInput, replaceWith: IEditorInput, options?: IEditorOptions | ITextEditorOptions }[], position?: Position): TPromise; - closeEditor(position: Position, input: IEditorInput): TPromise; - closeEditors(position: Position, filter?: { except?: IEditorInput, direction?: Direction, unmodifiedOnly?: boolean }): TPromise; - closeAllEditors(except?: Position): TPromise; - getActiveEditor(): BaseEditor; - getVisibleEditors(): IEditor[]; - getActiveEditorInput(): IEditorInput; -} - -type ICachedEditorInput = ResourceEditorInput | IFileEditorInput; - -export class WorkbenchEditorService implements IWorkbenchEditorService { - - public _serviceBrand: any; - - private static CACHE: ResourceMap = new ResourceMap(); - - private editorPart: IEditorPart | IWorkbenchEditorService; - private fileInputFactory: IFileInputFactory; - - constructor( - editorPart: IEditorPart | IWorkbenchEditorService, - @IUntitledEditorService private untitledEditorService: IUntitledEditorService, - @IWorkspaceContextService private workspaceContextService: IWorkspaceContextService, - @IInstantiationService private instantiationService: IInstantiationService, - @IEnvironmentService private environmentService: IEnvironmentService, - @IFileService private fileService: IFileService - ) { - this.editorPart = editorPart; - this.fileInputFactory = Registry.as(EditorExtensions.EditorInputFactories).getFileInputFactory(); - } - - public getActiveEditor(): IEditor { - return this.editorPart.getActiveEditor(); - } - - public getActiveEditorInput(): IEditorInput { - return this.editorPart.getActiveEditorInput(); - } - - public getVisibleEditors(): IEditor[] { - return this.editorPart.getVisibleEditors(); - } - - public isVisible(input: IEditorInput, includeSideBySide: boolean): boolean { - if (!input) { - return false; - } - - return this.getVisibleEditors().some(editor => { - if (!editor.input) { - return false; - } - - if (input.matches(editor.input)) { - return true; - } - - if (includeSideBySide && editor.input instanceof SideBySideEditorInput) { - const sideBySideInput = editor.input; - return input.matches(sideBySideInput.master) || input.matches(sideBySideInput.details); - } - - return false; - }); - } - - public openEditor(input: IEditorInput, options?: IEditorOptions, sideBySide?: boolean): TPromise; - public openEditor(input: IEditorInput, options?: IEditorOptions, position?: Position): TPromise; - public openEditor(input: IResourceInputType, position?: Position): TPromise; - public openEditor(input: IResourceInputType, sideBySide?: boolean): TPromise; - public openEditor(input: any, arg2?: any, arg3?: any): TPromise { - if (!input) { - return TPromise.as(null); - } - - // Workbench Input Support - if (input instanceof EditorInput) { - return this.doOpenEditor(input, this.toOptions(arg2), arg3); - } - - // Support opening foreign resources (such as a http link that points outside of the workbench) - const resourceInput = input; - if (resourceInput.resource instanceof URI) { - const schema = resourceInput.resource.scheme; - if (schema === network.Schemas.http || schema === network.Schemas.https) { - window.open(resourceInput.resource.toString(true)); - - return TPromise.as(null); - } - } - - // Untyped Text Editor Support (required for code that uses this service below workbench level) - const textInput = input; - const typedInput = this.createInput(textInput); - if (typedInput) { - return this.doOpenEditor(typedInput, TextEditorOptions.from(textInput), arg2); - } - - return TPromise.as(null); - } - - private toOptions(options?: IEditorOptions | EditorOptions): EditorOptions { - if (!options || options instanceof EditorOptions) { - return options as EditorOptions; - } - - const textOptions: ITextEditorOptions = options; - if (!!textOptions.selection) { - return TextEditorOptions.create(options); - } - - return EditorOptions.create(options); - } - - /** - * Allow subclasses to implement their own behavior for opening editor (see below). - */ - protected doOpenEditor(input: IEditorInput, options?: EditorOptions, sideBySide?: boolean): TPromise; - protected doOpenEditor(input: IEditorInput, options?: EditorOptions, position?: Position): TPromise; - protected doOpenEditor(input: IEditorInput, options?: EditorOptions, arg3?: any): TPromise { - return this.editorPart.openEditor(input, options, arg3); - } - - public openEditors(editors: { input: IResourceInputType, position: Position }[]): TPromise; - public openEditors(editors: { input: IEditorInput, position: Position, options?: IEditorOptions }[]): TPromise; - public openEditors(editors: any[]): TPromise { - const inputs = editors.map(editor => this.createInput(editor.input)); - const typedInputs: { input: IEditorInput, position: Position, options?: EditorOptions }[] = inputs.map((input, index) => { - const options = editors[index].input instanceof EditorInput ? this.toOptions(editors[index].options) : TextEditorOptions.from(editors[index].input); - - return { - input, - options, - position: editors[index].position - }; - }); - - return this.editorPart.openEditors(typedInputs); - } - - public replaceEditors(editors: { toReplace: IResourceInputType, replaceWith: IResourceInputType }[], position?: Position): TPromise; - public replaceEditors(editors: { toReplace: IEditorInput, replaceWith: IEditorInput, options?: IEditorOptions }[], position?: Position): TPromise; - public replaceEditors(editors: any[], position?: Position): TPromise { - const toReplaceInputs = editors.map(editor => this.createInput(editor.toReplace)); - const replaceWithInputs = editors.map(editor => this.createInput(editor.replaceWith)); - const typedReplacements: { toReplace: IEditorInput, replaceWith: IEditorInput, options?: EditorOptions }[] = editors.map((editor, index) => { - const options = editor.toReplace instanceof EditorInput ? this.toOptions(editor.options) : TextEditorOptions.from(editor.replaceWith); - - return { - toReplace: toReplaceInputs[index], - replaceWith: replaceWithInputs[index], - options - }; - }); - - return this.editorPart.replaceEditors(typedReplacements, position); - } - - public closeEditor(position: Position, input: IEditorInput): TPromise { - return this.doCloseEditor(position, input); - } - - protected doCloseEditor(position: Position, input: IEditorInput): TPromise { - return this.editorPart.closeEditor(position, input); - } - - public closeEditors(position: Position, filter?: { except?: IEditorInput, direction?: Direction, unmodifiedOnly?: boolean }): TPromise { - return this.editorPart.closeEditors(position, filter); - } - - public closeAllEditors(except?: Position): TPromise { - return this.editorPart.closeAllEditors(except); - } - - public createInput(input: IEditorInput): EditorInput; - public createInput(input: IResourceInputType): EditorInput; - public createInput(input: any): IEditorInput { - - // Workbench Input Support - if (input instanceof EditorInput) { - return input; - } - - // Side by Side Support - const resourceSideBySideInput = input; - if (resourceSideBySideInput.masterResource && resourceSideBySideInput.detailResource) { - const masterInput = this.createInput({ resource: resourceSideBySideInput.masterResource }); - const detailInput = this.createInput({ resource: resourceSideBySideInput.detailResource }); - - return new SideBySideEditorInput(resourceSideBySideInput.label || masterInput.getName(), typeof resourceSideBySideInput.description === 'string' ? resourceSideBySideInput.description : masterInput.getDescription(), detailInput, masterInput); - } - - // Diff Editor Support - const resourceDiffInput = input; - if (resourceDiffInput.leftResource && resourceDiffInput.rightResource) { - const leftInput = this.createInput({ resource: resourceDiffInput.leftResource }); - const rightInput = this.createInput({ resource: resourceDiffInput.rightResource }); - const label = resourceDiffInput.label || this.toDiffLabel(resourceDiffInput.leftResource, resourceDiffInput.rightResource, this.workspaceContextService, this.environmentService); - - return new DiffEditorInput(label, resourceDiffInput.description, leftInput, rightInput); - } - - // Untitled file support - const untitledInput = input; - if (!untitledInput.resource || typeof untitledInput.filePath === 'string' || (untitledInput.resource instanceof URI && untitledInput.resource.scheme === UNTITLED_SCHEMA)) { - return this.untitledEditorService.createOrGet(untitledInput.filePath ? URI.file(untitledInput.filePath) : untitledInput.resource, untitledInput.language, untitledInput.contents, untitledInput.encoding); - } - - const resourceInput = input; - - // Files support - if (resourceInput.resource instanceof URI && resourceInput.resource.scheme === network.Schemas.file) { - return this.createOrGet(resourceInput.resource, this.instantiationService, resourceInput.label, resourceInput.description, resourceInput.encoding); - } - - // Any other resource - else if (resourceInput.resource instanceof URI) { - const label = resourceInput.label || basename(resourceInput.resource.fsPath); - let description: string; - if (typeof resourceInput.description === 'string') { - description = resourceInput.description; - } else if (resourceInput.resource.scheme === network.Schemas.file) { - description = dirname(resourceInput.resource.fsPath); - } - - return this.createOrGet(resourceInput.resource, this.instantiationService, label, description); - } - - return null; - } - - private createOrGet(resource: URI, instantiationService: IInstantiationService, label: string, description: string, encoding?: string): ICachedEditorInput { - if (WorkbenchEditorService.CACHE.has(resource)) { - const input = WorkbenchEditorService.CACHE.get(resource); - if (input instanceof ResourceEditorInput) { - input.setName(label); - input.setDescription(description); - } else { - input.setPreferredEncoding(encoding); - } - - return input; - } - - let input: ICachedEditorInput; - 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); - } - - WorkbenchEditorService.CACHE.set(resource, input); - once(input.onDispose)(() => { - WorkbenchEditorService.CACHE.delete(resource); - }); - - return input; - } - - private toDiffLabel(res1: URI, res2: URI, context: IWorkspaceContextService, environment: IEnvironmentService): string { - const leftName = getPathLabel(res1.fsPath, context, environment); - const rightName = getPathLabel(res2.fsPath, context, environment); - - return nls.localize('compareLabels', "{0} ↔ {1}", leftName, rightName); - } -} - -export interface IEditorOpenHandler { - (input: IEditorInput, options?: EditorOptions, sideBySide?: boolean): TPromise; - (input: IEditorInput, options?: EditorOptions, position?: Position): TPromise; -} - -export interface IEditorCloseHandler { - (position: Position, input: IEditorInput): TPromise; -} - -/** - * Subclass of workbench editor service that delegates all calls to the provided editor service. Subclasses can choose to override the behavior - * of openEditor() and closeEditor() by providing a handler. - * - * This gives clients a chance to override the behavior of openEditor() and closeEditor(). - */ -export class DelegatingWorkbenchEditorService extends WorkbenchEditorService { - private editorOpenHandler: IEditorOpenHandler; - private editorCloseHandler: IEditorCloseHandler; - - constructor( - @IUntitledEditorService untitledEditorService: IUntitledEditorService, - @IInstantiationService instantiationService: IInstantiationService, - @IWorkspaceContextService workspaceContextService: IWorkspaceContextService, - @IWorkbenchEditorService editorService: IWorkbenchEditorService, - @IEnvironmentService environmentService: IEnvironmentService, - @IFileService fileService: IFileService - ) { - super( - editorService, - untitledEditorService, - workspaceContextService, - instantiationService, - environmentService, - fileService - ); - } - - public setEditorOpenHandler(handler: IEditorOpenHandler): void { - this.editorOpenHandler = handler; - } - - public setEditorCloseHandler(handler: IEditorCloseHandler): void { - this.editorCloseHandler = handler; - } - - protected doOpenEditor(input: IEditorInput, options?: EditorOptions, sideBySide?: boolean): TPromise; - protected doOpenEditor(input: IEditorInput, options?: EditorOptions, position?: Position): TPromise; - protected doOpenEditor(input: IEditorInput, options?: EditorOptions, arg3?: any): TPromise { - const handleOpen = this.editorOpenHandler ? this.editorOpenHandler(input, options, arg3) : TPromise.as(void 0); - - return handleOpen.then(editor => { - if (editor) { - return TPromise.as(editor); - } - - return super.doOpenEditor(input, options, arg3); - }); - } - - protected doCloseEditor(position: Position, input: IEditorInput): TPromise { - const handleClose = this.editorCloseHandler ? this.editorCloseHandler(position, input) : TPromise.as(void 0); - - return handleClose.then(() => { - return super.doCloseEditor(position, input); - }); - } -} diff --git a/src/vs/workbench/services/editor/common/editorService.ts b/src/vs/workbench/services/editor/common/editorService.ts index 05f2ae36cc2..8100a8cd551 100644 --- a/src/vs/workbench/services/editor/common/editorService.ts +++ b/src/vs/workbench/services/editor/common/editorService.ts @@ -6,8 +6,23 @@ 'use strict'; import { TPromise } from 'vs/base/common/winjs.base'; -import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; +import { createDecorator, ServiceIdentifier, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IEditorService, IEditor, IEditorInput, IEditorOptions, ITextEditorOptions, Position, Direction, IResourceInput, IResourceDiffInput, IResourceSideBySideInput, IUntitledResourceInput } from 'vs/platform/editor/common/editor'; +import URI from 'vs/base/common/uri'; +import network = require('vs/base/common/network'); +import { Registry } from 'vs/platform/registry/common/platform'; +import { basename, dirname } from 'vs/base/common/paths'; +import { EditorInput, EditorOptions, TextEditorOptions, Extensions as EditorExtensions, SideBySideEditorInput, IFileEditorInput, IFileInputFactory, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor'; +import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; +import { IUntitledEditorService, UNTITLED_SCHEMA } from 'vs/workbench/services/untitled/common/untitledEditorService'; +import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import nls = require('vs/nls'); +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 const IWorkbenchEditorService = createDecorator('editorService'); @@ -91,4 +106,344 @@ export interface IWorkbenchEditorService extends IEditorService { * Allows to resolve an untyped input to a workbench typed instanceof editor input */ createInput(input: IResourceInputType): IEditorInput; -} \ No newline at end of file +} + +export interface IEditorPart { + openEditor(input?: IEditorInput, options?: IEditorOptions | ITextEditorOptions, sideBySide?: boolean): TPromise; + openEditor(input?: IEditorInput, options?: IEditorOptions | ITextEditorOptions, position?: Position): TPromise; + openEditors(editors: { input: IEditorInput, position: Position, options?: IEditorOptions | ITextEditorOptions }[]): TPromise; + replaceEditors(editors: { toReplace: IEditorInput, replaceWith: IEditorInput, options?: IEditorOptions | ITextEditorOptions }[], position?: Position): TPromise; + closeEditor(position: Position, input: IEditorInput): TPromise; + closeEditors(position: Position, filter?: { except?: IEditorInput, direction?: Direction, unmodifiedOnly?: boolean }): TPromise; + closeAllEditors(except?: Position): TPromise; + getActiveEditor(): IEditor; + getVisibleEditors(): IEditor[]; + getActiveEditorInput(): IEditorInput; +} + +type ICachedEditorInput = ResourceEditorInput | IFileEditorInput; + +export class WorkbenchEditorService implements IWorkbenchEditorService { + + public _serviceBrand: any; + + private static CACHE: ResourceMap = new ResourceMap(); + + private editorPart: IEditorPart | IWorkbenchEditorService; + private fileInputFactory: IFileInputFactory; + + constructor( + editorPart: IEditorPart | IWorkbenchEditorService, + @IUntitledEditorService private untitledEditorService: IUntitledEditorService, + @IWorkspaceContextService private workspaceContextService: IWorkspaceContextService, + @IInstantiationService private instantiationService: IInstantiationService, + @IEnvironmentService private environmentService: IEnvironmentService, + @IFileService private fileService: IFileService + ) { + this.editorPart = editorPart; + this.fileInputFactory = Registry.as(EditorExtensions.EditorInputFactories).getFileInputFactory(); + } + + public getActiveEditor(): IEditor { + return this.editorPart.getActiveEditor(); + } + + public getActiveEditorInput(): IEditorInput { + return this.editorPart.getActiveEditorInput(); + } + + public getVisibleEditors(): IEditor[] { + return this.editorPart.getVisibleEditors(); + } + + public isVisible(input: IEditorInput, includeSideBySide: boolean): boolean { + if (!input) { + return false; + } + + return this.getVisibleEditors().some(editor => { + if (!editor.input) { + return false; + } + + if (input.matches(editor.input)) { + return true; + } + + if (includeSideBySide && editor.input instanceof SideBySideEditorInput) { + const sideBySideInput = editor.input; + return input.matches(sideBySideInput.master) || input.matches(sideBySideInput.details); + } + + return false; + }); + } + + public openEditor(input: IEditorInput, options?: IEditorOptions, sideBySide?: boolean): TPromise; + public openEditor(input: IEditorInput, options?: IEditorOptions, position?: Position): TPromise; + public openEditor(input: IResourceInputType, position?: Position): TPromise; + public openEditor(input: IResourceInputType, sideBySide?: boolean): TPromise; + public openEditor(input: any, arg2?: any, arg3?: any): TPromise { + if (!input) { + return TPromise.as(null); + } + + // Workbench Input Support + if (input instanceof EditorInput) { + return this.doOpenEditor(input, this.toOptions(arg2), arg3); + } + + // Support opening foreign resources (such as a http link that points outside of the workbench) + const resourceInput = input; + if (resourceInput.resource instanceof URI) { + const schema = resourceInput.resource.scheme; + if (schema === network.Schemas.http || schema === network.Schemas.https) { + window.open(resourceInput.resource.toString(true)); + + return TPromise.as(null); + } + } + + // Untyped Text Editor Support (required for code that uses this service below workbench level) + const textInput = input; + const typedInput = this.createInput(textInput); + if (typedInput) { + return this.doOpenEditor(typedInput, TextEditorOptions.from(textInput), arg2); + } + + return TPromise.as(null); + } + + private toOptions(options?: IEditorOptions | EditorOptions): EditorOptions { + if (!options || options instanceof EditorOptions) { + return options as EditorOptions; + } + + const textOptions: ITextEditorOptions = options; + if (!!textOptions.selection) { + return TextEditorOptions.create(options); + } + + return EditorOptions.create(options); + } + + /** + * Allow subclasses to implement their own behavior for opening editor (see below). + */ + protected doOpenEditor(input: IEditorInput, options?: EditorOptions, sideBySide?: boolean): TPromise; + protected doOpenEditor(input: IEditorInput, options?: EditorOptions, position?: Position): TPromise; + protected doOpenEditor(input: IEditorInput, options?: EditorOptions, arg3?: any): TPromise { + return this.editorPart.openEditor(input, options, arg3); + } + + public openEditors(editors: { input: IResourceInputType, position: Position }[]): TPromise; + public openEditors(editors: { input: IEditorInput, position: Position, options?: IEditorOptions }[]): TPromise; + public openEditors(editors: any[]): TPromise { + const inputs = editors.map(editor => this.createInput(editor.input)); + const typedInputs: { input: IEditorInput, position: Position, options?: EditorOptions }[] = inputs.map((input, index) => { + const options = editors[index].input instanceof EditorInput ? this.toOptions(editors[index].options) : TextEditorOptions.from(editors[index].input); + + return { + input, + options, + position: editors[index].position + }; + }); + + return this.editorPart.openEditors(typedInputs); + } + + public replaceEditors(editors: { toReplace: IResourceInputType, replaceWith: IResourceInputType }[], position?: Position): TPromise; + public replaceEditors(editors: { toReplace: IEditorInput, replaceWith: IEditorInput, options?: IEditorOptions }[], position?: Position): TPromise; + public replaceEditors(editors: any[], position?: Position): TPromise { + const toReplaceInputs = editors.map(editor => this.createInput(editor.toReplace)); + const replaceWithInputs = editors.map(editor => this.createInput(editor.replaceWith)); + const typedReplacements: { toReplace: IEditorInput, replaceWith: IEditorInput, options?: EditorOptions }[] = editors.map((editor, index) => { + const options = editor.toReplace instanceof EditorInput ? this.toOptions(editor.options) : TextEditorOptions.from(editor.replaceWith); + + return { + toReplace: toReplaceInputs[index], + replaceWith: replaceWithInputs[index], + options + }; + }); + + return this.editorPart.replaceEditors(typedReplacements, position); + } + + public closeEditor(position: Position, input: IEditorInput): TPromise { + return this.doCloseEditor(position, input); + } + + protected doCloseEditor(position: Position, input: IEditorInput): TPromise { + return this.editorPart.closeEditor(position, input); + } + + public closeEditors(position: Position, filter?: { except?: IEditorInput, direction?: Direction, unmodifiedOnly?: boolean }): TPromise { + return this.editorPart.closeEditors(position, filter); + } + + public closeAllEditors(except?: Position): TPromise { + return this.editorPart.closeAllEditors(except); + } + + public createInput(input: IEditorInput): EditorInput; + public createInput(input: IResourceInputType): EditorInput; + public createInput(input: any): IEditorInput { + + // Workbench Input Support + if (input instanceof EditorInput) { + return input; + } + + // Side by Side Support + const resourceSideBySideInput = input; + if (resourceSideBySideInput.masterResource && resourceSideBySideInput.detailResource) { + const masterInput = this.createInput({ resource: resourceSideBySideInput.masterResource }); + const detailInput = this.createInput({ resource: resourceSideBySideInput.detailResource }); + + return new SideBySideEditorInput(resourceSideBySideInput.label || masterInput.getName(), typeof resourceSideBySideInput.description === 'string' ? resourceSideBySideInput.description : masterInput.getDescription(), detailInput, masterInput); + } + + // Diff Editor Support + const resourceDiffInput = input; + if (resourceDiffInput.leftResource && resourceDiffInput.rightResource) { + const leftInput = this.createInput({ resource: resourceDiffInput.leftResource }); + const rightInput = this.createInput({ resource: resourceDiffInput.rightResource }); + const label = resourceDiffInput.label || this.toDiffLabel(resourceDiffInput.leftResource, resourceDiffInput.rightResource, this.workspaceContextService, this.environmentService); + + return new DiffEditorInput(label, resourceDiffInput.description, leftInput, rightInput); + } + + // Untitled file support + const untitledInput = input; + if (!untitledInput.resource || typeof untitledInput.filePath === 'string' || (untitledInput.resource instanceof URI && untitledInput.resource.scheme === UNTITLED_SCHEMA)) { + return this.untitledEditorService.createOrGet(untitledInput.filePath ? URI.file(untitledInput.filePath) : untitledInput.resource, untitledInput.language, untitledInput.contents, untitledInput.encoding); + } + + const resourceInput = input; + + // Files support + if (resourceInput.resource instanceof URI && resourceInput.resource.scheme === network.Schemas.file) { + return this.createOrGet(resourceInput.resource, this.instantiationService, resourceInput.label, resourceInput.description, resourceInput.encoding); + } + + // Any other resource + else if (resourceInput.resource instanceof URI) { + const label = resourceInput.label || basename(resourceInput.resource.fsPath); + let description: string; + if (typeof resourceInput.description === 'string') { + description = resourceInput.description; + } else if (resourceInput.resource.scheme === network.Schemas.file) { + description = dirname(resourceInput.resource.fsPath); + } + + return this.createOrGet(resourceInput.resource, this.instantiationService, label, description); + } + + return null; + } + + private createOrGet(resource: URI, instantiationService: IInstantiationService, label: string, description: string, encoding?: string): ICachedEditorInput { + if (WorkbenchEditorService.CACHE.has(resource)) { + const input = WorkbenchEditorService.CACHE.get(resource); + if (input instanceof ResourceEditorInput) { + input.setName(label); + input.setDescription(description); + } else { + input.setPreferredEncoding(encoding); + } + + return input; + } + + let input: ICachedEditorInput; + 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); + } + + WorkbenchEditorService.CACHE.set(resource, input); + once(input.onDispose)(() => { + WorkbenchEditorService.CACHE.delete(resource); + }); + + return input; + } + + private toDiffLabel(res1: URI, res2: URI, context: IWorkspaceContextService, environment: IEnvironmentService): string { + const leftName = getPathLabel(res1.fsPath, context, environment); + const rightName = getPathLabel(res2.fsPath, context, environment); + + return nls.localize('compareLabels', "{0} ↔ {1}", leftName, rightName); + } +} + +export interface IEditorOpenHandler { + (input: IEditorInput, options?: EditorOptions, sideBySide?: boolean): TPromise; + (input: IEditorInput, options?: EditorOptions, position?: Position): TPromise; +} + +export interface IEditorCloseHandler { + (position: Position, input: IEditorInput): TPromise; +} + +/** + * Subclass of workbench editor service that delegates all calls to the provided editor service. Subclasses can choose to override the behavior + * of openEditor() and closeEditor() by providing a handler. + * + * This gives clients a chance to override the behavior of openEditor() and closeEditor(). + */ +export class DelegatingWorkbenchEditorService extends WorkbenchEditorService { + private editorOpenHandler: IEditorOpenHandler; + private editorCloseHandler: IEditorCloseHandler; + + constructor( + @IUntitledEditorService untitledEditorService: IUntitledEditorService, + @IInstantiationService instantiationService: IInstantiationService, + @IWorkspaceContextService workspaceContextService: IWorkspaceContextService, + @IWorkbenchEditorService editorService: IWorkbenchEditorService, + @IEnvironmentService environmentService: IEnvironmentService, + @IFileService fileService: IFileService + ) { + super( + editorService, + untitledEditorService, + workspaceContextService, + instantiationService, + environmentService, + fileService + ); + } + + public setEditorOpenHandler(handler: IEditorOpenHandler): void { + this.editorOpenHandler = handler; + } + + public setEditorCloseHandler(handler: IEditorCloseHandler): void { + this.editorCloseHandler = handler; + } + + protected doOpenEditor(input: IEditorInput, options?: EditorOptions, sideBySide?: boolean): TPromise; + protected doOpenEditor(input: IEditorInput, options?: EditorOptions, position?: Position): TPromise; + protected doOpenEditor(input: IEditorInput, options?: EditorOptions, arg3?: any): TPromise { + const handleOpen = this.editorOpenHandler ? this.editorOpenHandler(input, options, arg3) : TPromise.as(void 0); + + return handleOpen.then(editor => { + if (editor) { + return TPromise.as(editor); + } + + return super.doOpenEditor(input, options, arg3); + }); + } + + protected doCloseEditor(position: Position, input: IEditorInput): TPromise { + const handleClose = this.editorCloseHandler ? this.editorCloseHandler(position, input) : TPromise.as(void 0); + + return handleClose.then(() => { + return super.doCloseEditor(position, input); + }); + } +} diff --git a/src/vs/workbench/services/editor/test/browser/editorService.test.ts b/src/vs/workbench/services/editor/test/browser/editorService.test.ts index c6ce1376dc1..dabe18285d6 100644 --- a/src/vs/workbench/services/editor/test/browser/editorService.test.ts +++ b/src/vs/workbench/services/editor/test/browser/editorService.test.ts @@ -14,7 +14,7 @@ import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; import { EditorInput, EditorOptions, TextEditorOptions } from 'vs/workbench/common/editor'; import { FileEditorInput } from 'vs/workbench/parts/files/common/editors/fileEditorInput'; import { workbenchInstantiationService } from 'vs/workbench/test/workbenchTestServices'; -import { DelegatingWorkbenchEditorService, WorkbenchEditorService, IEditorPart } from 'vs/workbench/services/editor/browser/editorService'; +import { DelegatingWorkbenchEditorService, WorkbenchEditorService, IEditorPart } from 'vs/workbench/services/editor/common/editorService'; import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput'; import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; diff --git a/src/vs/workbench/services/files/electron-browser/fileService.ts b/src/vs/workbench/services/files/electron-browser/fileService.ts index d2c61424a26..cf46014fe39 100644 --- a/src/vs/workbench/services/files/electron-browser/fileService.ts +++ b/src/vs/workbench/services/files/electron-browser/fileService.ts @@ -13,7 +13,7 @@ import errors = require('vs/base/common/errors'); import uri from 'vs/base/common/uri'; 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 { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { Action } from 'vs/base/common/actions'; import { IMessageService, IMessageWithAction, Severity, CloseAction } from 'vs/platform/message/common/message'; @@ -119,7 +119,7 @@ export class FileService implements IFileService { this.toUnbind.push(this.raw.onAfterOperation(e => this._onAfterOperation.fire(e))); // Config changes - this.toUnbind.push(this.configurationService.onDidUpdateConfiguration(e => this.onConfigurationChange(this.configurationService.getConfiguration()))); + this.toUnbind.push(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationChange(e))); // Root changes this.toUnbind.push(this.contextService.onDidChangeWorkspaceFolders(() => this.onDidChangeWorkspaceFolders())); @@ -142,8 +142,10 @@ export class FileService implements IFileService { return encodingOverride; } - private onConfigurationChange(configuration: IFilesConfiguration): void { - this.updateOptions(configuration.files); + private onConfigurationChange(event: IConfigurationChangeEvent): void { + if (event.affectsConfiguration('files')) { + this.updateOptions(this.configurationService.getConfiguration('files')); + } } public updateOptions(options: object): void { diff --git a/src/vs/workbench/services/files/node/watcher/nsfw/watcherService.ts b/src/vs/workbench/services/files/node/watcher/nsfw/watcherService.ts index 9e3744b2638..b8c93d184de 100644 --- a/src/vs/workbench/services/files/node/watcher/nsfw/watcherService.ts +++ b/src/vs/workbench/services/files/node/watcher/nsfw/watcherService.ts @@ -83,7 +83,11 @@ export class FileWatcher { // Start watching this.updateFolders(); this.toDispose.push(this.contextService.onDidChangeWorkspaceFolders(() => this.updateFolders())); - this.toDispose.push(this.configurationService.onDidUpdateConfiguration(() => this.updateFolders())); + this.toDispose.push(this.configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('files.watcherExclude')) { + this.updateFolders(); + } + })); return () => this.dispose(); } @@ -95,7 +99,7 @@ export class FileWatcher { this.service.setRoots(this.contextService.getWorkspace().folders.map(folder => { // Fetch the root's watcherExclude setting and return it - const configuration = this.configurationService.getConfiguration(undefined, { + const configuration = this.configurationService.getConfiguration({ resource: folder.uri }); let ignored: string[] = []; 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 6559d99dc1e..428474c05b7 100644 --- a/src/vs/workbench/services/files/test/node/fileService.test.ts +++ b/src/vs/workbench/services/files/test/node/fileService.test.ts @@ -19,13 +19,13 @@ import extfs = require('vs/base/node/extfs'); 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 { TestContextService, TestTextResourceConfigurationService, getRandomTestPath } from 'vs/workbench/test/workbenchTestServices'; import { Workspace, toWorkspaceFolders } from 'vs/platform/workspace/common/workspace'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; suite('FileService', () => { let service: FileService; - const parentDir = path.join(os.tmpdir(), 'vsctests', 'service'); + const parentDir = getRandomTestPath(os.tmpdir(), 'vsctests', 'fileservice'); let testDir: string; setup(function (done) { diff --git a/src/vs/workbench/services/group/common/groupService.ts b/src/vs/workbench/services/group/common/groupService.ts index 12820ea98ea..1cdeceef1ff 100644 --- a/src/vs/workbench/services/group/common/groupService.ts +++ b/src/vs/workbench/services/group/common/groupService.ts @@ -7,7 +7,7 @@ import { createDecorator, ServiceIdentifier, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { Position, IEditorInput } from 'vs/platform/editor/common/editor'; -import { IEditorStacksModel, IEditorGroup } from 'vs/workbench/common/editor'; +import { IEditorStacksModel, IEditorGroup, IEditorOpeningEvent } from 'vs/workbench/common/editor'; import Event from 'vs/base/common/event'; export enum GroupArrangement { @@ -45,6 +45,11 @@ export interface IEditorGroupService { */ onEditorsChanged: Event; + /** + * Emitted when an editor is opening. Allows to prevent/replace the opening via the event method. + */ + onEditorOpening: Event; + /** * Emitted when opening an editor fails. */ diff --git a/src/vs/workbench/services/hash/common/hashService.ts b/src/vs/workbench/services/hash/common/hashService.ts new file mode 100644 index 00000000000..e8e2ee18d9d --- /dev/null +++ b/src/vs/workbench/services/hash/common/hashService.ts @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { createDecorator } from 'vs/platform/instantiation/common/instantiation'; + +export const IHashService = createDecorator('hashService'); + +export interface IHashService { + _serviceBrand: any; + + /** + * Produce a SHA1 hash of the provided content. + */ + createSHA1(content: string): string; +} \ No newline at end of file diff --git a/src/vs/workbench/services/hash/node/hashService.ts b/src/vs/workbench/services/hash/node/hashService.ts new file mode 100644 index 00000000000..fec0cab111f --- /dev/null +++ b/src/vs/workbench/services/hash/node/hashService.ts @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { createHash } from 'crypto'; +import { IHashService } from 'vs/workbench/services/hash/common/hashService'; + +export class HashService implements IHashService { + + _serviceBrand: any; + + public createSHA1(content: string): string { + return createHash('sha1').update(content).digest('hex'); + } +} \ No newline at end of file diff --git a/src/vs/workbench/services/history/browser/history.ts b/src/vs/workbench/services/history/browser/history.ts index ff937e3303c..0c40b27b376 100644 --- a/src/vs/workbench/services/history/browser/history.ts +++ b/src/vs/workbench/services/history/browser/history.ts @@ -13,7 +13,7 @@ import { IEditor as IBaseEditor, IEditorInput, ITextEditorOptions, IResourceInpu import { Extensions as EditorExtensions, EditorInput, IEditorCloseEvent, IEditorGroup, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IHistoryService } from 'vs/workbench/services/history/common/history'; -import { FileChangesEvent, IFileService, FileChangeType } from 'vs/platform/files/common/files'; +import { FileChangesEvent, IFileService, FileChangeType, FILES_EXCLUDE_CONFIG } from 'vs/platform/files/common/files'; import { Selection } from 'vs/editor/common/core/selection'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; @@ -21,12 +21,12 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; import { once, debounceEvent } from 'vs/base/common/event'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; import { IWindowsService } from 'vs/platform/windows/common/windows'; import { getCodeEditor } from 'vs/editor/common/services/codeEditorService'; import { getExcludes, ISearchConfiguration } from 'vs/platform/search/common/search'; -import { parse, IExpression } from 'vs/base/common/glob'; +import { IExpression } from 'vs/base/common/glob'; import { ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ResourceGlobMatcher } from 'vs/workbench/common/resources'; @@ -211,7 +211,11 @@ export class HistoryService extends BaseHistoryService implements IHistoryServic this.recentlyClosedFiles = []; this.loaded = false; this.registry = Registry.as(Extensions.Editors); - this.resourceFilter = instantiationService.createInstance(ResourceGlobMatcher, (root: URI) => this.getExcludes(root), (expression: IExpression) => parse(expression)); + this.resourceFilter = instantiationService.createInstance( + ResourceGlobMatcher, + (root: URI) => this.getExcludes(root), + (event: IConfigurationChangeEvent) => event.affectsConfiguration(FILES_EXCLUDE_CONFIG) || event.affectsConfiguration('search.exclude') + ); this.registerListeners(); } @@ -224,7 +228,7 @@ export class HistoryService extends BaseHistoryService implements IHistoryServic private getExcludes(root?: URI): IExpression { const scope = root ? { resource: root } : void 0; - return getExcludes(this.configurationService.getConfiguration(void 0, scope)); + return getExcludes(this.configurationService.getConfiguration(scope)); } private registerListeners(): void { @@ -572,15 +576,16 @@ export class HistoryService extends BaseHistoryService implements IHistoryServic this.stack = this.stack.slice(0, this.index + 1); } - this.setIndex(this.index + 1); - this.stack.splice(this.index, 0, entry); + this.stack.splice(this.index + 1, 0, entry); // Check for limit if (this.stack.length > HistoryService.MAX_STACK_ITEMS) { this.stack.shift(); // remove first and dispose - if (this.index > 0) { - this.setIndex(this.index - 1); + if (this.lastIndex >= 0) { + this.lastIndex--; } + } else { + this.setIndex(this.index + 1); } } diff --git a/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts b/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts index 9634a27da63..562e8972512 100644 --- a/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts +++ b/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts @@ -268,7 +268,7 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { super(contextKeyService, commandService, telemetryService, messageService, statusBarService); let dispatchConfig = getDispatchConfig(configurationService); - configurationService.onDidUpdateConfiguration((e) => { + configurationService.onDidChangeConfiguration((e) => { let newDispatchConfig = getDispatchConfig(configurationService); if (dispatchConfig === newDispatchConfig) { return; 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 9ee62bfdc44..6f003bde491 100644 --- a/src/vs/workbench/services/keybinding/test/node/keybindingEditing.test.ts +++ b/src/vs/workbench/services/keybinding/test/node/keybindingEditing.test.ts @@ -67,6 +67,7 @@ suite('Keybindings Editing', () => { instantiationService.stub(IConfigurationService, ConfigurationService); instantiationService.stub(IConfigurationService, 'getConfiguration', { 'eol': '\n' }); instantiationService.stub(IConfigurationService, 'onDidUpdateConfiguration', () => { }); + instantiationService.stub(IConfigurationService, 'onDidChangeConfiguration', () => { }); instantiationService.stub(IWorkspaceContextService, new TestContextService()); instantiationService.stub(ILifecycleService, new TestLifecycleService()); instantiationService.stub(IEditorGroupService, new TestEditorGroupService()); diff --git a/src/vs/workbench/services/mode/common/workbenchModeService.ts b/src/vs/workbench/services/mode/common/workbenchModeService.ts index 7b59087af60..62023d035bb 100644 --- a/src/vs/workbench/services/mode/common/workbenchModeService.ts +++ b/src/vs/workbench/services/mode/common/workbenchModeService.ts @@ -9,7 +9,7 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import * as paths from 'vs/base/common/paths'; import { TPromise } from 'vs/base/common/winjs.base'; import mime = require('vs/base/common/mime'); -import { IFilesConfiguration } from 'vs/platform/files/common/files'; +import { IFilesConfiguration, FILES_ASSOCIATIONS_CONFIG } from 'vs/platform/files/common/files'; import { IExtensionService } from 'vs/platform/extensions/common/extensions'; import { IExtensionPointUser, ExtensionMessageCollector, IExtensionPoint, ExtensionsRegistry } from 'vs/platform/extensions/common/extensionsRegistry'; import { ModesRegistry } from 'vs/editor/common/modes/modesRegistry'; @@ -123,7 +123,11 @@ export class WorkbenchModeServiceImpl extends ModeServiceImpl { }); - this._configurationService.onDidUpdateConfiguration(e => this.onConfigurationChange(this._configurationService.getConfiguration())); + this._configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration(FILES_ASSOCIATIONS_CONFIG)) { + this.updateMime(); + } + }); this.onDidCreateMode((mode) => { this._extensionService.activateByEvent(`onLanguage:${mode.getId()}`).done(null, onUnexpectedError); @@ -132,10 +136,8 @@ export class WorkbenchModeServiceImpl extends ModeServiceImpl { protected _onReady(): TPromise { if (!this._onReadyPromise) { - const configuration = this._configurationService.getConfiguration(); this._onReadyPromise = this._extensionService.onReady().then(() => { - this.onConfigurationChange(configuration); - + this.updateMime(); return true; }); } @@ -143,7 +145,8 @@ export class WorkbenchModeServiceImpl extends ModeServiceImpl { return this._onReadyPromise; } - private onConfigurationChange(configuration: IFilesConfiguration): void { + private updateMime(): void { + const configuration = this._configurationService.getConfiguration(); // Clear user configured mime associations mime.clearTextMimes(true /* user configured */); diff --git a/src/vs/workbench/services/panel/common/panelService.ts b/src/vs/workbench/services/panel/common/panelService.ts index 5de4a2d9e18..f490edbda68 100644 --- a/src/vs/workbench/services/panel/common/panelService.ts +++ b/src/vs/workbench/services/panel/common/panelService.ts @@ -14,6 +14,7 @@ export interface IPanelIdentifier { id: string; name: string; commandId: string; + cssClass: string; } export interface IPanelService { diff --git a/src/vs/workbench/services/progress/browser/progressService2.ts b/src/vs/workbench/services/progress/browser/progressService2.ts index a47ab0f2c3a..b6de990d858 100644 --- a/src/vs/workbench/services/progress/browser/progressService2.ts +++ b/src/vs/workbench/services/progress/browser/progressService2.ts @@ -7,7 +7,6 @@ import 'vs/css!./media/progressService2'; import * as dom from 'vs/base/browser/dom'; import { localize } from 'vs/nls'; -import { IActivityBarService } from 'vs/workbench/services/activity/common/activityBarService'; import { IDisposable } from 'vs/base/common/lifecycle'; import { IProgressService2, IProgressOptions, ProgressLocation, IProgress, IProgressStep, Progress, emptyProgress } from 'vs/platform/progress/common/progress'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; @@ -61,7 +60,6 @@ export class ProgressService2 implements IProgressService2 { private _stack: [IProgressOptions, Progress][] = []; constructor( - @IActivityBarService private _activityBar: IActivityBarService, @IViewletService private _viewletService: IViewletService ) { // diff --git a/src/vs/workbench/services/scm/common/scm.ts b/src/vs/workbench/services/scm/common/scm.ts index e5dda8e6e2b..b659bfeedc4 100644 --- a/src/vs/workbench/services/scm/common/scm.ts +++ b/src/vs/workbench/services/scm/common/scm.ts @@ -11,7 +11,6 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' import Event from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; import { Command } from 'vs/editor/common/modes'; -import { ColorIdentifier } from 'vs/platform/theme/common/colorRegistry'; export interface IBaselineResourceProvider { getBaselineResource(resource: URI): TPromise; @@ -25,7 +24,6 @@ export interface ISCMResourceDecorations { tooltip?: string; strikeThrough?: boolean; faded?: boolean; - color?: ColorIdentifier; } export interface ISCMResourceSplice { diff --git a/src/vs/workbench/services/search/node/fileSearch.ts b/src/vs/workbench/services/search/node/fileSearch.ts index ff457516718..02e7c5e570c 100644 --- a/src/vs/workbench/services/search/node/fileSearch.ts +++ b/src/vs/workbench/services/search/node/fileSearch.ts @@ -53,6 +53,7 @@ export class FileWalker { private normalizedFilePatternLowercase: string; private includePattern: glob.ParsedExpression; private maxResults: number; + private exists: boolean; private maxFilesize: number; private isLimitHit: boolean; private resultCount: number; @@ -77,6 +78,7 @@ export class FileWalker { this.filePattern = config.filePattern; this.includePattern = config.includePattern && glob.parse(config.includePattern); this.maxResults = config.maxResults || null; + this.exists = config.exists; this.maxFilesize = config.maxFilesize || null; this.walkedPaths = Object.create(null); this.resultCount = 0; @@ -234,6 +236,7 @@ export class FileWalker { return; } if (this.isLimitHit) { + done(); return; } @@ -392,9 +395,12 @@ export class FileWalker { cmd.on('close', (code: number) => { // ripgrep returns code=1 when no results are found - if (code !== 0 && ((isRipgrep && stderr.length) || !isRipgrep)) { + if (code !== 0 && (!isRipgrep || code !== 1)) { done(new Error(`command failed with error code ${code}: ${this.decodeData(stderr, encoding)}`)); } else { + if (isRipgrep && this.exists && code === 0) { + this.isLimitHit = true; + } done(null, '', true); } }); @@ -657,7 +663,7 @@ export class FileWalker { if (this.isFilePatternMatch(candidate.relativePath) && (!this.includePattern || this.includePattern(candidate.relativePath, candidate.basename))) { this.resultCount++; - if (this.maxResults && this.resultCount > this.maxResults) { + if (this.exists || (this.maxResults && this.resultCount > this.maxResults)) { this.isLimitHit = true; } diff --git a/src/vs/workbench/services/search/node/rawSearchService.ts b/src/vs/workbench/services/search/node/rawSearchService.ts index 12c6c6e0381..55fb059c48f 100644 --- a/src/vs/workbench/services/search/node/rawSearchService.ts +++ b/src/vs/workbench/services/search/node/rawSearchService.ts @@ -23,7 +23,7 @@ import { TextSearchWorkerProvider } from 'vs/workbench/services/search/node/text import { IRawSearchService, IRawSearch, IRawFileMatch, ISerializedFileMatch, ISerializedSearchProgressItem, ISerializedSearchComplete, ISearchEngine, IFileSearchProgressItem, ITelemetryEvent } from './search'; import { ICachedSearchStats, IProgress } from 'vs/platform/search/common/search'; import { fuzzyContains } from 'vs/base/common/strings'; -import { compareItemsByScore, IItemAccessor, ScorerCache } from 'vs/base/parts/quickopen/common/quickOpenScorer'; +import { compareItemsByScore, IItemAccessor, ScorerCache, prepareQuery } from 'vs/base/parts/quickopen/common/quickOpenScorer'; export class SearchService implements IRawSearchService { @@ -254,7 +254,8 @@ export class SearchService implements IRawSearchService { // this is very important because we are also limiting the number of results by config.maxResults // and as such we want the top items to be included in this result set if the number of items // exceeds config.maxResults. - const compare = (matchA: IRawFileMatch, matchB: IRawFileMatch) => compareItemsByScore(matchA, matchB, strings.stripWildcards(config.filePattern), true, FileMatchItemAccessor, scorerCache); + const query = prepareQuery(config.filePattern); + const compare = (matchA: IRawFileMatch, matchB: IRawFileMatch) => compareItemsByScore(matchA, matchB, query, true, FileMatchItemAccessor, scorerCache); return arrays.topAsync(results, compare, config.maxResults, 10000); } diff --git a/src/vs/workbench/services/search/node/ripgrepFileSearch.ts b/src/vs/workbench/services/search/node/ripgrepFileSearch.ts index 7b4b508b4e4..03ea2338f5c 100644 --- a/src/vs/workbench/services/search/node/ripgrepFileSearch.ts +++ b/src/vs/workbench/services/search/node/ripgrepFileSearch.ts @@ -42,7 +42,13 @@ function getRgArgs(config: IRawSearch, folderQuery: IFolderSearch, includePatter } // Follow symlinks - args.push('--follow'); + if (!config.ignoreSymlinks) { + args.push('--follow'); + } + + if (config.exists) { + args.push('--quiet'); + } // Folder to search args.push('--'); diff --git a/src/vs/workbench/services/search/node/ripgrepTextSearch.ts b/src/vs/workbench/services/search/node/ripgrepTextSearch.ts index da61448ee47..df668d90e57 100644 --- a/src/vs/workbench/services/search/node/ripgrepTextSearch.ts +++ b/src/vs/workbench/services/search/node/ripgrepTextSearch.ts @@ -493,7 +493,9 @@ function getRgArgs(config: IRawSearch): IRgGlobResult { } // Follow symlinks - args.push('--follow'); + if (!config.ignoreSymlinks) { + args.push('--follow'); + } // Set default encoding if only one folder is opened if (config.folderQueries.length === 1 && config.folderQueries[0].fileEncoding && config.folderQueries[0].fileEncoding !== 'utf8') { diff --git a/src/vs/workbench/services/search/node/search.ts b/src/vs/workbench/services/search/node/search.ts index 424b3b4f0f7..3775f5d9a6e 100644 --- a/src/vs/workbench/services/search/node/search.ts +++ b/src/vs/workbench/services/search/node/search.ts @@ -19,12 +19,14 @@ export interface IFolderSearch { export interface IRawSearch { folderQueries: IFolderSearch[]; + ignoreSymlinks?: boolean; extraFiles?: string[]; filePattern?: string; excludePattern?: IExpression; includePattern?: IExpression; contentPattern?: IPatternInfo; maxResults?: number; + exists?: boolean; sortByScore?: boolean; cacheKey?: string; maxFilesize?: number; diff --git a/src/vs/workbench/services/search/node/searchService.ts b/src/vs/workbench/services/search/node/searchService.ts index b7adc8e63a9..09f4f9de0e9 100644 --- a/src/vs/workbench/services/search/node/searchService.ts +++ b/src/vs/workbench/services/search/node/searchService.ts @@ -266,10 +266,12 @@ export class DiskSearch implements ISearchResultProvider { excludePattern: query.excludePattern, includePattern: query.includePattern, maxResults: query.maxResults, + exists: query.exists, sortByScore: query.sortByScore, cacheKey: query.cacheKey, useRipgrep: query.useRipgrep, - disregardIgnoreFiles: query.disregardIgnoreFiles + disregardIgnoreFiles: query.disregardIgnoreFiles, + ignoreSymlinks: query.ignoreSymlinks }; if (query.folderQueries) { diff --git a/src/vs/workbench/services/search/test/node/search.test.ts b/src/vs/workbench/services/search/test/node/search.test.ts index abecd589774..acc6ae56808 100644 --- a/src/vs/workbench/services/search/test/node/search.test.ts +++ b/src/vs/workbench/services/search/test/node/search.test.ts @@ -27,9 +27,12 @@ const MULTIROOT_QUERIES: IFolderSearch[] = [ { folder: MORE_FIXTURES } ]; +const testTimeout = 5000; + suite('FileSearchEngine', () => { test('Files: *.js', function (done: () => void) { + this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: ROOT_FOLDER_QUERY, filePattern: '*.js' @@ -48,6 +51,7 @@ suite('FileSearchEngine', () => { }); test('Files: maxResults', function (done: () => void) { + this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: ROOT_FOLDER_QUERY, maxResults: 1 @@ -66,6 +70,7 @@ suite('FileSearchEngine', () => { }); test('Files: maxResults without Ripgrep', function (done: () => void) { + this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: ROOT_FOLDER_QUERY, maxResults: 1, @@ -84,7 +89,94 @@ suite('FileSearchEngine', () => { }); }); + test('Files: exists', function (done: () => void) { + this.timeout(testTimeout); + let engine = new FileSearchEngine({ + folderQueries: ROOT_FOLDER_QUERY, + includePattern: { '**/file.txt': true }, + exists: true + }); + + let count = 0; + engine.search((result) => { + if (result) { + count++; + } + }, () => { }, (error, complete) => { + assert.ok(!error); + assert.equal(count, 0); + assert.ok(complete.limitHit); + done(); + }); + }); + + test('Files: not exists', function (done: () => void) { + this.timeout(testTimeout); + let engine = new FileSearchEngine({ + folderQueries: ROOT_FOLDER_QUERY, + includePattern: { '**/nofile.txt': true }, + exists: true + }); + + let count = 0; + engine.search((result) => { + if (result) { + count++; + } + }, () => { }, (error, complete) => { + assert.ok(!error); + assert.equal(count, 0); + assert.ok(!complete.limitHit); + done(); + }); + }); + + test('Files: exists without Ripgrep', function (done: () => void) { + this.timeout(testTimeout); + let engine = new FileSearchEngine({ + folderQueries: ROOT_FOLDER_QUERY, + includePattern: { '**/file.txt': true }, + exists: true, + useRipgrep: false + }); + + let count = 0; + engine.search((result) => { + if (result) { + count++; + } + }, () => { }, (error, complete) => { + assert.ok(!error); + assert.equal(count, 0); + assert.ok(complete.limitHit); + done(); + }); + }); + + test('Files: not exists without Ripgrep', function (done: () => void) { + this.timeout(testTimeout); + let engine = new FileSearchEngine({ + folderQueries: ROOT_FOLDER_QUERY, + includePattern: { '**/nofile.txt': true }, + exists: true, + useRipgrep: false + }); + + let count = 0; + engine.search((result) => { + if (result) { + count++; + } + }, () => { }, (error, complete) => { + assert.ok(!error); + assert.equal(count, 0); + assert.ok(!complete.limitHit); + done(); + }); + }); + test('Files: examples/com*', function (done: () => void) { + this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: ROOT_FOLDER_QUERY, filePattern: normalize(join('examples', 'com*'), true) @@ -103,6 +195,7 @@ suite('FileSearchEngine', () => { }); test('Files: examples (fuzzy)', function (done: () => void) { + this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: ROOT_FOLDER_QUERY, filePattern: 'xl' @@ -121,6 +214,7 @@ suite('FileSearchEngine', () => { }); test('Files: multiroot', function (done: () => void) { + this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: MULTIROOT_QUERIES, filePattern: 'file' @@ -138,7 +232,57 @@ suite('FileSearchEngine', () => { }); }); + test('Files: multiroot with includePattern and maxResults', function (done: () => void) { + this.timeout(testTimeout); + let engine = new FileSearchEngine({ + folderQueries: MULTIROOT_QUERIES, + maxResults: 1, + includePattern: { + '*.txt': true, + '*.js': true + }, + useRipgrep: true + }); + + let count = 0; + engine.search((result) => { + if (result) { + count++; + } + }, () => { }, (error, complete) => { + assert.ok(!error); + assert.equal(count, 1); + done(); + }); + }); + + test('Files: multiroot with includePattern and exists', function (done: () => void) { + this.timeout(testTimeout); + let engine = new FileSearchEngine({ + folderQueries: MULTIROOT_QUERIES, + exists: true, + includePattern: { + '*.txt': true, + '*.js': true + }, + useRipgrep: true + }); + + let count = 0; + engine.search((result) => { + if (result) { + count++; + } + }, () => { }, (error, complete) => { + assert.ok(!error); + assert.equal(count, 0); + assert.ok(complete.limitHit); + done(); + }); + }); + test('Files: NPE (CamelCase)', function (done: () => void) { + this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: ROOT_FOLDER_QUERY, filePattern: 'NullPE' @@ -157,6 +301,7 @@ suite('FileSearchEngine', () => { }); test('Files: *.*', function (done: () => void) { + this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: ROOT_FOLDER_QUERY, filePattern: '*.*' @@ -175,6 +320,7 @@ suite('FileSearchEngine', () => { }); test('Files: *.as', function (done: () => void) { + this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: ROOT_FOLDER_QUERY, filePattern: '*.as' @@ -193,6 +339,7 @@ suite('FileSearchEngine', () => { }); test('Files: *.* without derived', function (done: () => void) { + this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: ROOT_FOLDER_QUERY, filePattern: 'site.*', @@ -215,6 +362,7 @@ suite('FileSearchEngine', () => { }); test('Files: *.* exclude folder without wildcard', function (done: () => void) { + this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: ROOT_FOLDER_QUERY, filePattern: '*.*', @@ -234,6 +382,7 @@ suite('FileSearchEngine', () => { }); test('Files: *.* exclude folder with leading wildcard', function (done: () => void) { + this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: ROOT_FOLDER_QUERY, filePattern: '*.*', @@ -253,6 +402,7 @@ suite('FileSearchEngine', () => { }); test('Files: *.* exclude folder with trailing wildcard', function (done: () => void) { + this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: ROOT_FOLDER_QUERY, filePattern: '*.*', @@ -272,6 +422,7 @@ suite('FileSearchEngine', () => { }); test('Files: *.* exclude with unicode', function (done: () => void) { + this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: ROOT_FOLDER_QUERY, filePattern: '*.*', @@ -291,6 +442,7 @@ suite('FileSearchEngine', () => { }); test('Files: multiroot with exclude', function (done: () => void) { + this.timeout(testTimeout); const folderQueries: IFolderSearch[] = [ { folder: EXAMPLES_FIXTURES, @@ -324,6 +476,7 @@ suite('FileSearchEngine', () => { }); test('Files: Unicode and Spaces', function (done: () => void) { + this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: ROOT_FOLDER_QUERY, filePattern: '汉语' @@ -345,6 +498,7 @@ suite('FileSearchEngine', () => { }); test('Files: no results', function (done: () => void) { + this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: ROOT_FOLDER_QUERY, filePattern: 'nofilematch' @@ -363,6 +517,7 @@ suite('FileSearchEngine', () => { }); test('Files: absolute path to file ignores excludes', function (done: () => void) { + this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: ROOT_FOLDER_QUERY, filePattern: path.normalize(path.join(require.toUrl('./fixtures'), 'site.css')), @@ -385,6 +540,7 @@ suite('FileSearchEngine', () => { }); test('Files: relative path matched once', function (done: () => void) { + this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: ROOT_FOLDER_QUERY, filePattern: path.normalize(path.join('examples', 'company.js')) @@ -406,6 +562,7 @@ suite('FileSearchEngine', () => { }); test('Files: relative path to file ignores excludes', function (done: () => void) { + this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: ROOT_FOLDER_QUERY, filePattern: path.normalize(path.join('examples', 'company.js')), @@ -428,6 +585,7 @@ suite('FileSearchEngine', () => { }); test('Files: Include pattern, single files', function (done: () => void) { + this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: ROOT_FOLDER_QUERY, includePattern: { @@ -451,6 +609,7 @@ suite('FileSearchEngine', () => { }); test('Files: extraFiles only', function (done: () => void) { + this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: [], extraFiles: [ @@ -477,6 +636,7 @@ suite('FileSearchEngine', () => { }); test('Files: extraFiles only (with include)', function (done: () => void) { + this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: [], extraFiles: [ @@ -504,6 +664,7 @@ suite('FileSearchEngine', () => { }); test('Files: extraFiles only (with exclude)', function (done: () => void) { + this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: [], extraFiles: [ @@ -528,6 +689,7 @@ suite('FileSearchEngine', () => { }); test('Files: no dupes in nested folders', function (done: () => void) { + this.timeout(testTimeout); let engine = new FileSearchEngine({ folderQueries: [ { folder: EXAMPLES_FIXTURES }, @@ -552,6 +714,7 @@ suite('FileSearchEngine', () => { suite('FileWalker', () => { test('Find: exclude subfolder', function (done: () => void) { + this.timeout(testTimeout); if (platform.isWindows) { done(); return; @@ -579,6 +742,7 @@ suite('FileWalker', () => { }); test('Find: folder excludes', function (done: () => void) { + this.timeout(testTimeout); if (platform.isWindows) { done(); return; @@ -605,6 +769,7 @@ suite('FileWalker', () => { }); test('Find: exclude multiple folders', function (done: () => void) { + this.timeout(testTimeout); if (platform.isWindows) { done(); return; @@ -635,6 +800,7 @@ suite('FileWalker', () => { }); test('Find: exclude folder path suffix', function (done: () => void) { + this.timeout(testTimeout); if (platform.isWindows) { done(); return; @@ -662,6 +828,7 @@ suite('FileWalker', () => { }); test('Find: exclude subfolder path suffix', function (done: () => void) { + this.timeout(testTimeout); if (platform.isWindows) { done(); return; @@ -689,6 +856,7 @@ suite('FileWalker', () => { }); test('Find: exclude folder path', function (done: () => void) { + this.timeout(testTimeout); if (platform.isWindows) { done(); return; @@ -716,6 +884,7 @@ suite('FileWalker', () => { }); test('Find: exclude combination of paths', function (done: () => void) { + this.timeout(testTimeout); if (platform.isWindows) { done(); return; diff --git a/src/vs/workbench/services/search/test/node/searchService.test.ts b/src/vs/workbench/services/search/test/node/searchService.test.ts index 36935227c65..7fd40591eef 100644 --- a/src/vs/workbench/services/search/test/node/searchService.test.ts +++ b/src/vs/workbench/services/search/test/node/searchService.test.ts @@ -7,9 +7,10 @@ import * as assert from 'assert'; import { normalize } from 'path'; +import path = require('path'); import { IProgress, IUncachedSearchStats } from 'vs/platform/search/common/search'; -import { ISearchEngine, IRawSearch, IRawFileMatch, ISerializedFileMatch, ISerializedSearchComplete } from 'vs/workbench/services/search/node/search'; +import { ISearchEngine, IRawSearch, IRawFileMatch, ISerializedFileMatch, ISerializedSearchComplete, IFolderSearch } from 'vs/workbench/services/search/node/search'; import { SearchService as RawSearchService } from 'vs/workbench/services/search/node/rawSearchService'; import { DiskSearch } from 'vs/workbench/services/search/node/searchService'; @@ -17,6 +18,12 @@ const TEST_FOLDER_QUERIES = [ { folder: normalize('/some/where') } ]; +const TEST_FIXTURES = path.normalize(require.toUrl('./fixtures')); +const MULTIROOT_QUERIES: IFolderSearch[] = [ + { folder: path.join(TEST_FIXTURES, 'examples') }, + { folder: path.join(TEST_FIXTURES, 'more') } +]; + const stats: IUncachedSearchStats = { fromCache: false, resultCount: 4, @@ -143,6 +150,43 @@ suite('SearchService', () => { }); }); + test('Multi-root with include pattern and maxResults', function () { + const service = new RawSearchService(); + + const query: IRawSearch = { + folderQueries: MULTIROOT_QUERIES, + maxResults: 1, + includePattern: { + '*.txt': true, + '*.js': true + }, + }; + + return DiskSearch.collectResults(service.fileSearch(query)) + .then(result => { + assert.strictEqual(result.results.length, 1, 'Result'); + }); + }); + + test('Multi-root with include pattern and exists', function () { + const service = new RawSearchService(); + + const query: IRawSearch = { + folderQueries: MULTIROOT_QUERIES, + exists: true, + includePattern: { + '*.txt': true, + '*.js': true + }, + }; + + return DiskSearch.collectResults(service.fileSearch(query)) + .then(result => { + assert.strictEqual(result.results.length, 0, 'Result'); + assert.ok(result.limitHit); + }); + }); + test('Sorted results', function () { const paths = ['bab', 'bbc', 'abb']; const matches: IRawFileMatch[] = paths.map(relativePath => ({ diff --git a/src/vs/workbench/services/telemetry/common/workspaceStats.ts b/src/vs/workbench/services/telemetry/node/workspaceStats.ts similarity index 95% rename from src/vs/workbench/services/telemetry/common/workspaceStats.ts rename to src/vs/workbench/services/telemetry/node/workspaceStats.ts index 5bdecfc2d37..e81862ca195 100644 --- a/src/vs/workbench/services/telemetry/common/workspaceStats.ts +++ b/src/vs/workbench/services/telemetry/node/workspaceStats.ts @@ -36,7 +36,7 @@ const SecondLevelDomainWhitelist = [ 'google.com' ]; -type Tags = { [index: string]: boolean | number }; +type Tags = { [index: string]: boolean | number | string }; function stripLowLevelDomains(domain: string): string { let match = domain.match(SecondLevelDomainMatcher); @@ -149,6 +149,7 @@ export class WorkspaceStats { "workbench.filesToOpen" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, "workbench.filesToCreate" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, "workbench.filesToDiff" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "workspace.id" : { "classification": "CustomerContent", "purpose": "FeatureInsight" }, "workspace.roots" : { "classification": "CustomerContent", "purpose": "FeatureInsight" }, "workspace.empty" : { "classification": "CustomerContent", "purpose": "FeatureInsight" }, "workspace.grunt" : { "classification": "CustomerContent", "purpose": "FeatureInsight" }, @@ -175,13 +176,29 @@ export class WorkspaceStats { private getWorkspaceTags(configuration: IWindowConfiguration): TPromise { const tags: Tags = Object.create(null); - const { filesToOpen, filesToCreate, filesToDiff } = configuration; - tags['workbench.filesToOpen'] = filesToOpen && filesToOpen.length || undefined; - tags['workbench.filesToCreate'] = filesToCreate && filesToCreate.length || undefined; - tags['workbench.filesToDiff'] = filesToDiff && filesToDiff.length || undefined; - - const isEmpty = this.contextService.getWorkbenchState() === WorkbenchState.EMPTY; + const state = this.contextService.getWorkbenchState(); const workspace = this.contextService.getWorkspace(); + + let workspaceId: string; + switch (state) { + case WorkbenchState.EMPTY: + workspaceId = void 0; + break; + case WorkbenchState.FOLDER: + workspaceId = crypto.createHash('sha1').update(workspace.folders[0].uri.fsPath).digest('hex'); + break; + case WorkbenchState.WORKSPACE: + workspaceId = crypto.createHash('sha1').update(workspace.configuration.fsPath).digest('hex'); + } + + tags['workspace.id'] = workspaceId; + + const { filesToOpen, filesToCreate, filesToDiff } = configuration; + tags['workbench.filesToOpen'] = filesToOpen && filesToOpen.length || 0; + tags['workbench.filesToCreate'] = filesToCreate && filesToCreate.length || 0; + tags['workbench.filesToDiff'] = filesToDiff && filesToDiff.length || 0; + + const isEmpty = state === WorkbenchState.EMPTY; tags['workspace.roots'] = isEmpty ? 0 : workspace.folders.length; tags['workspace.empty'] = isEmpty; diff --git a/src/vs/workbench/services/telemetry/test/workspaceStats.test.ts b/src/vs/workbench/services/telemetry/test/workspaceStats.test.ts index 5647af87623..7bb6e7d3d8f 100644 --- a/src/vs/workbench/services/telemetry/test/workspaceStats.test.ts +++ b/src/vs/workbench/services/telemetry/test/workspaceStats.test.ts @@ -7,7 +7,7 @@ import * as assert from 'assert'; import * as crypto from 'crypto'; -import { getDomainsOfRemotes, getRemotes, getHashedRemotes } from 'vs/workbench/services/telemetry/common/workspaceStats'; +import { getDomainsOfRemotes, getRemotes, getHashedRemotes } from 'vs/workbench/services/telemetry/node/workspaceStats'; function hash(value: string): string { return crypto.createHash('sha1').update(value.toString()).digest('hex'); diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index 3267637c435..a91a0e12f23 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -31,7 +31,6 @@ import { IMessageService, Severity } from 'vs/platform/message/common/message'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IModelService } from 'vs/editor/common/services/modelService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { anonymize } from 'vs/platform/telemetry/common/telemetryUtils'; import { RunOnceScheduler } from 'vs/base/common/async'; import { IRawTextSource } from 'vs/editor/common/model/textSource'; @@ -364,16 +363,31 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } private loadWithContent(content: IRawTextContent | IContent, backup?: URI): TPromise { - diag('load() - resolved content', this.resource, new Date()); + return this.doLoadWithContent(content, backup).then(model => { - /* __GDPR__ - "fileGet" : { - "mimeType" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "ext": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "path": { "classification": "CustomerContent", "purpose": "FeatureInsight" } + // Telemetry: We log the fileGet telemetry event after the model has been loaded to ensure a good mimetype + if (this.isSettingsFile()) { + /* __GDPR__ + "settingsRead" : {} + */ + this.telemetryService.publicLog('settingsRead'); // Do not log read to user settings.json and .vscode folder as a fileGet event as it ruins our JSON usage data + } else { + /* __GDPR__ + "fileGet" : { + "mimeType" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "ext": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "path": { "classification": "CustomerContent", "purpose": "FeatureInsight" } + } + */ + this.telemetryService.publicLog('fileGet', { mimeType: guessMimeTypes(this.resource.fsPath).join(', '), ext: paths.extname(this.resource.fsPath) }); } - */ - this.telemetryService.publicLog('fileGet', { mimeType: guessMimeTypes(this.resource.fsPath).join(', '), ext: paths.extname(this.resource.fsPath), path: anonymize(this.resource.fsPath) }); + + return model; + }); + } + + private doLoadWithContent(content: IRawTextContent | IContent, backup?: URI): TPromise { + diag('load() - resolved content', this.resource, new Date()); // Update our resolved disk stat model const resolvedStat: IFileStat = { @@ -674,10 +688,15 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // mark the save participant as current pending save operation return this.saveSequentializer.setPending(versionId, saveParticipantPromise.then(newVersionId => { - // the model was not dirty and no save participant changed the contents, so we do not have - // to write the contents to disk, as they are already on disk. we still want to trigger - // a change on the file though so that external file watchers can be notified - if (options.force && !this.dirty && options.reason === SaveReason.EXPLICIT && versionId === newVersionId) { + // Under certain conditions a save to the model will not cause the contents to the flushed on + // disk because we can assume that the contents are already on disk. Instead, we just touch the + // file to still trigger external file watchers for example. + // The conditions are all of: + // - a forced, explicit save (Ctrl+S) + // - the model is not dirty (otherwise we know there are changed which needs to go to the file) + // - the model is not in orphan mode (because in that case we know the file does not exist on disk) + // - the model version did not change due to save participants running + if (options.force && !this.dirty && !this.inOrphanMode && options.reason === SaveReason.EXPLICIT && versionId === newVersionId) { return this.doTouch(); } @@ -765,7 +784,6 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil return this.contextService.getWorkspace().folders.some(folder => { return paths.isEqualOrParent(this.resource.fsPath, path.join(folder.uri.fsPath, '.vscode')); }); - } private doTouch(): TPromise { diff --git a/src/vs/workbench/services/textfile/common/textFileService.ts b/src/vs/workbench/services/textfile/common/textFileService.ts index 5dfbcde38a9..21b9c241b60 100644 --- a/src/vs/workbench/services/textfile/common/textFileService.ts +++ b/src/vs/workbench/services/textfile/common/textFileService.ts @@ -85,7 +85,7 @@ export abstract class TextFileService implements ITextFileService { const configuration = this.configurationService.getConfiguration(); this.currentFilesAssociationConfig = configuration && configuration.files && configuration.files.associations; - this.onConfigurationChange(configuration); + this.onFilesConfigurationChange(configuration); /* __GDPR__ "autoSave" : { @@ -123,8 +123,12 @@ export abstract class TextFileService implements ITextFileService { this.lifecycleService.onWillShutdown(event => event.veto(this.beforeShutdown(event.reason))); this.lifecycleService.onShutdown(this.dispose, this); - // Configuration changes - this.toUnbind.push(this.configurationService.onDidUpdateConfiguration(e => this.onConfigurationChange(this.configurationService.getConfiguration()))); + // Files configuration changes + this.toUnbind.push(this.configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('files')) { + this.onFilesConfigurationChange(this.configurationService.getConfiguration()); + } + })); } private beforeShutdown(reason: ShutdownReason): boolean | TPromise { @@ -316,7 +320,7 @@ export abstract class TextFileService implements ITextFileService { return this.backupFileService.discardAllWorkspaceBackups(); } - protected onConfigurationChange(configuration: IFilesConfiguration): void { + protected onFilesConfigurationChange(configuration: IFilesConfiguration): void { const wasAutoSaveEnabled = (this.getAutoSaveMode() !== AutoSaveMode.OFF); const autoSaveMode = (configuration && configuration.files && configuration.files.autoSave) || AutoSaveConfiguration.OFF; diff --git a/src/vs/workbench/services/textfile/test/textFileService.test.ts b/src/vs/workbench/services/textfile/test/textFileService.test.ts index 777b42a5b71..6416edee36b 100644 --- a/src/vs/workbench/services/textfile/test/textFileService.test.ts +++ b/src/vs/workbench/services/textfile/test/textFileService.test.ts @@ -19,7 +19,7 @@ import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/un import { UntitledEditorModel } from 'vs/workbench/common/editor/untitledEditorModel'; import { HotExitConfiguration } from 'vs/platform/files/common/files'; import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, Workspace } from 'vs/platform/workspace/common/workspace'; class ServiceAccessor { constructor( @@ -104,7 +104,7 @@ suite('Files - TextFileService', () => { const service = accessor.textFileService; service.setConfirmResult(ConfirmResult.DONT_SAVE); - service.onConfigurationChange({ files: { hotExit: 'off' } }); + service.onFilesConfigurationChange({ files: { hotExit: 'off' } }); model.load().done(() => { model.textEditorModel.setValue('foo'); @@ -137,7 +137,7 @@ suite('Files - TextFileService', () => { const service = accessor.textFileService; service.setConfirmResult(ConfirmResult.SAVE); - service.onConfigurationChange({ files: { hotExit: 'off' } }); + service.onFilesConfigurationChange({ files: { hotExit: 'off' } }); model.load().done(() => { model.textEditorModel.setValue('foo'); @@ -377,10 +377,10 @@ suite('Files - TextFileService', () => { const service = accessor.textFileService; // Set hot exit config - service.onConfigurationChange({ files: { hotExit: setting } }); + service.onFilesConfigurationChange({ files: { hotExit: setting } }); // Set empty workspace if required if (!workspace) { - accessor.contextService.setWorkspace(null); + accessor.contextService.setWorkspace(new Workspace('empty:1508317022751')); } // Set multiple windows if required if (multipleWindows) { diff --git a/src/vs/workbench/services/themes/common/colorThemeSchema.ts b/src/vs/workbench/services/themes/common/colorThemeSchema.ts index 5c6b7609512..4e7c779283c 100644 --- a/src/vs/workbench/services/themes/common/colorThemeSchema.ts +++ b/src/vs/workbench/services/themes/common/colorThemeSchema.ts @@ -121,8 +121,12 @@ export const tokenColorizationSettingSchema: IJSONSchema = { foreground: { type: 'string', description: nls.localize('schema.token.foreground', 'Foreground color for the token.'), - format: 'color', - defaultSnippets: [{ body: '${1:#FF0000}' }] + format: 'color-hex', + default: '#ff0000' + }, + background: { + type: 'string', + deprecationMessage: nls.localize('schema.token.background.warning', 'Token background colors are currently not supported.') }, fontStyle: { type: 'string', diff --git a/src/vs/workbench/services/themes/common/fileIconThemeSchema.ts b/src/vs/workbench/services/themes/common/fileIconThemeSchema.ts index f04fc317d19..b26a44c5ab7 100644 --- a/src/vs/workbench/services/themes/common/fileIconThemeSchema.ts +++ b/src/vs/workbench/services/themes/common/fileIconThemeSchema.ts @@ -167,7 +167,7 @@ const schema: IJSONSchema = { }, fontColor: { type: 'string', - format: 'color', + format: 'color-hex', description: nls.localize('schema.fontColor', 'When using a glyph font: The color to use.') }, fontSize: { diff --git a/src/vs/workbench/services/themes/common/workbenchThemeService.ts b/src/vs/workbench/services/themes/common/workbenchThemeService.ts index 2334ac25ae4..f8f97e35a74 100644 --- a/src/vs/workbench/services/themes/common/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/common/workbenchThemeService.ts @@ -7,9 +7,9 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { TPromise } from 'vs/base/common/winjs.base'; import Event from 'vs/base/common/event'; -import { ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; import { Color } from 'vs/base/common/color'; import { ITheme, IThemeService } from 'vs/platform/theme/common/themeService'; +import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; export const IWorkbenchThemeService = createDecorator('themeService'); diff --git a/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts index 9a76bee4429..572299fd514 100644 --- a/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts @@ -13,11 +13,9 @@ import { IWorkbenchThemeService, IColorTheme, ITokenColorCustomizations, IFileIc import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { Registry } from 'vs/platform/registry/common/platform'; -import { IJSONSchema } from 'vs/base/common/jsonSchema'; import errors = require('vs/base/common/errors'); -import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; +import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationRegistry, Extensions as ConfigurationExtensions, IConfigurationPropertySchema, IConfigurationNode } from 'vs/platform/configuration/common/configurationRegistry'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IMessageService } from 'vs/platform/message/common/message'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -151,12 +149,12 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { this.colorThemeStore.onDidChange(themes => { colorThemeSettingSchema.enum = themes.map(t => t.settingsId); colorThemeSettingSchema.enumDescriptions = themes.map(t => themeData.description || ''); - configurationRegistry.notifyConfigurationSchemaUpdated(colorThemeSettingSchema); + configurationRegistry.notifyConfigurationSchemaUpdated(themeSettingsConfiguration); }); this.iconThemeStore.onDidChange(themes => { iconThemeSettingSchema.enum = [null, ...themes.map(t => t.settingsId)]; iconThemeSettingSchema.enumDescriptions = [iconThemeSettingSchema.enumDescriptions[0], ...themes.map(t => themeData.description || '')]; - configurationRegistry.notifyConfigurationSchemaUpdated(iconThemeSettingSchema); + configurationRegistry.notifyConfigurationSchemaUpdated(themeSettingsConfiguration); }); } @@ -223,8 +221,8 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { this.updateColorCustomizations(false); - let colorThemeSetting = this.configurationService.lookup(COLOR_THEME_SETTING).value; - let iconThemeSetting = this.configurationService.lookup(ICON_THEME_SETTING).value || ''; + let colorThemeSetting = this.configurationService.getValue(COLOR_THEME_SETTING); + let iconThemeSetting = this.configurationService.getValue(ICON_THEME_SETTING) || ''; return Promise.join([ this.colorThemeStore.findThemeDataBySettingsId(colorThemeSetting, DEFAULT_THEME_ID).then(theme => { @@ -237,8 +235,8 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { } private installConfigurationListener() { - this.configurationService.onDidUpdateConfiguration(e => { - let colorThemeSetting = this.configurationService.lookup(COLOR_THEME_SETTING).value; + this.configurationService.onDidChangeConfiguration(e => { + let colorThemeSetting = this.configurationService.getValue(COLOR_THEME_SETTING); if (colorThemeSetting !== this.currentColorTheme.settingsId) { this.colorThemeStore.findThemeDataBySettingsId(colorThemeSetting, null).then(theme => { if (theme) { @@ -247,7 +245,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { }); } - let iconThemeSetting = this.configurationService.lookup(ICON_THEME_SETTING).value || ''; + let iconThemeSetting = this.configurationService.getValue(ICON_THEME_SETTING) || ''; if (iconThemeSetting !== this.currentIconTheme.settingsId) { this.iconThemeStore.findThemeBySettingsId(iconThemeSetting).then(theme => { this.setFileIconTheme(theme && theme.id, null); @@ -376,10 +374,10 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { } private updateColorCustomizations(notify = true): void { - let newColorCustomizations = this.configurationService.lookup(CUSTOM_WORKBENCH_COLORS_SETTING).value || {}; + let newColorCustomizations = this.configurationService.getValue(CUSTOM_WORKBENCH_COLORS_SETTING) || {}; let newColorIds = Object.keys(newColorCustomizations); - let newTokenColorCustomizations = this.configurationService.lookup(CUSTOM_EDITOR_COLORS_SETTING).value || {}; + let newTokenColorCustomizations = this.configurationService.getValue(CUSTOM_EDITOR_COLORS_SETTING) || {}; if (this.hasCustomizationChanged(newColorCustomizations, newColorIds, newTokenColorCustomizations)) { this.colorCustomizations = newColorCustomizations; @@ -508,11 +506,11 @@ colorThemeSchema.register(); fileIconThemeSchema.register(); class ConfigurationWriter { - constructor( @IConfigurationService private configurationService: IConfigurationService, @IConfigurationEditingService private configurationEditingService: IConfigurationEditingService) { + constructor( @IConfigurationService private configurationService: IConfigurationService) { } public writeConfiguration(key: string, value: any, settingsTarget: ConfigurationTarget): TPromise { - let settings = this.configurationService.lookup(key); + let settings = this.configurationService.inspect(key); if (settingsTarget === ConfigurationTarget.USER) { if (value === settings.user) { return TPromise.as(null); // nothing to do @@ -527,14 +525,14 @@ class ConfigurationWriter { return TPromise.as(null); // nothing to do } } - return this.configurationEditingService.writeConfiguration(settingsTarget, { key, value }); + return this.configurationService.updateValue(key, value, settingsTarget); } } // Configuration: Themes const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); -const colorThemeSettingSchema: IJSONSchema = { +const colorThemeSettingSchema: IConfigurationPropertySchema = { type: 'string', description: nls.localize('colorTheme', "Specifies the color theme used in the workbench."), default: DEFAULT_THEME_SETTING_VALUE, @@ -543,7 +541,7 @@ const colorThemeSettingSchema: IJSONSchema = { errorMessage: nls.localize('colorThemeError', "Theme is unknown or not installed."), }; -const iconThemeSettingSchema: IJSONSchema = { +const iconThemeSettingSchema: IConfigurationPropertySchema = { type: ['string', 'null'], default: DEFAULT_ICON_THEME_SETTING_VALUE, description: nls.localize('iconTheme', "Specifies the icon theme used in the workbench or 'null' to not show any file icons."), @@ -551,7 +549,7 @@ const iconThemeSettingSchema: IJSONSchema = { enumDescriptions: [nls.localize('noIconThemeDesc', 'No file icons')], errorMessage: nls.localize('iconThemeError', "File icon theme is unknown or not installed.") }; -const colorCustomizationsSchema: IJSONSchema = { +const colorCustomizationsSchema: IConfigurationPropertySchema = { type: ['object'], description: nls.localize('workbenchColors', "Overrides colors from the currently selected color theme."), properties: colorThemeSchema.colorsSchema.properties, @@ -566,7 +564,7 @@ const colorCustomizationsSchema: IJSONSchema = { }] }; -configurationRegistry.registerConfiguration({ +const themeSettingsConfiguration: IConfigurationNode = { id: 'workbench', order: 7.1, type: 'object', @@ -575,7 +573,8 @@ configurationRegistry.registerConfiguration({ [ICON_THEME_SETTING]: iconThemeSettingSchema, [CUSTOM_WORKBENCH_COLORS_SETTING]: colorCustomizationsSchema } -}); +}; +configurationRegistry.registerConfiguration(themeSettingsConfiguration); function tokenGroupSettings(description: string) { return { @@ -584,8 +583,7 @@ function tokenGroupSettings(description: string) { anyOf: [ { type: 'string', - format: 'color', - defaultSnippets: [{ body: '#FF0000' }] + format: 'color-hex' }, colorThemeSchema.tokenColorizationSettingSchema ] diff --git a/src/vs/workbench/services/workspace/common/workspaceEditing.ts b/src/vs/workbench/services/workspace/common/workspaceEditing.ts index 56f6b473db2..e7d86635073 100644 --- a/src/vs/workbench/services/workspace/common/workspaceEditing.ts +++ b/src/vs/workbench/services/workspace/common/workspaceEditing.ts @@ -5,9 +5,9 @@ 'use strict'; import { TPromise } from 'vs/base/common/winjs.base'; -import URI from 'vs/base/common/uri'; import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import URI from 'vs/base/common/uri'; export const IWorkspaceEditingService = createDecorator('workspaceEditingService'); @@ -16,12 +16,12 @@ export interface IWorkspaceEditingService { _serviceBrand: ServiceIdentifier; /** - * add folders to the existing workspace + * Add folders to the existing workspace */ addFolders(folders: URI[]): TPromise; /** - * remove folders from the existing workspace + * Remove folders from the existing workspace */ removeFolders(folders: URI[]): TPromise; diff --git a/src/vs/workbench/services/workspace/node/workspaceEditingService.ts b/src/vs/workbench/services/workspace/node/workspaceEditingService.ts index 0a684c69b28..5fa421760f4 100644 --- a/src/vs/workbench/services/workspace/node/workspaceEditingService.ts +++ b/src/vs/workbench/services/workspace/node/workspaceEditingService.ts @@ -7,17 +7,14 @@ import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing'; import URI from 'vs/base/common/uri'; +import * as nls from 'vs/nls'; import { TPromise } from 'vs/base/common/winjs.base'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/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, isStoredWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces'; -import { dirname } from 'path'; +import { IWindowService, IEnterWorkspaceResult } from 'vs/platform/windows/common/windows'; +import { IJSONEditingService, JSONEditingError, JSONEditingErrorCode } from 'vs/workbench/services/configuration/common/jsonEditing'; +import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; -import { massageFolderPathForWorkspace } from 'vs/platform/workspaces/node/workspaces'; -import { isLinux } from 'vs/base/common/platform'; -import { WorkspaceService } from 'vs/workbench/services/configuration/node/configuration'; +import { WorkspaceService } from 'vs/workbench/services/configuration/node/configurationService'; import { migrateStorageToMultiRootWorkspace } from 'vs/platform/storage/common/migration'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { StorageService } from 'vs/platform/storage/common/storageService'; @@ -26,7 +23,8 @@ 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'; +import { IChoiceService, Severity, IMessageService } from 'vs/platform/message/common/message'; +import { ICommandService } from 'vs/platform/commands/common/commands'; export class WorkspaceEditingService implements IWorkspaceEditingService { @@ -35,98 +33,25 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { constructor( @IJSONEditingService private jsonEditingService: IJSONEditingService, @IWorkspaceContextService private contextService: WorkspaceService, - @IEnvironmentService private environmentService: IEnvironmentService, - @IWindowsService private windowsService: IWindowsService, @IWindowService private windowService: IWindowService, - @IWorkspacesService private workspacesService: IWorkspacesService, @IWorkspaceConfigurationService private workspaceConfigurationService: IWorkspaceConfigurationService, @IStorageService private storageService: IStorageService, @IExtensionService private extensionService: IExtensionService, - @IBackupFileService private backupFileService: IBackupFileService + @IBackupFileService private backupFileService: IBackupFileService, + @IChoiceService private choiceService: IChoiceService, + @IMessageService private messageService: IMessageService, + @ICommandService private commandService: ICommandService ) { } - public addFolders(foldersToAdd: URI[]): TPromise { - if (!this.isSupported()) { - return TPromise.as(void 0); // we need a workspace to begin with - } - - const currentWorkspaceFolders = this.contextService.getWorkspace().folders; - const currentWorkspaceFolderUris = currentWorkspaceFolders.map(folder => folder.uri); - const currentStoredFolders = currentWorkspaceFolders.map(folder => folder.raw); - - const storedFoldersToAdd: IStoredWorkspaceFolder[] = []; - - const workspaceConfigFolder = dirname(this.contextService.getWorkspace().configuration.fsPath); - - foldersToAdd.forEach(folderToAdd => { - if (this.contains(currentWorkspaceFolderUris, folderToAdd)) { - return; // already existing - } - - // File resource: use "path" property - if (folderToAdd.scheme === Schemas.file) { - storedFoldersToAdd.push({ - path: massageFolderPathForWorkspace(folderToAdd.fsPath, workspaceConfigFolder, currentStoredFolders) - }); - } - - // Any other resource: use "uri" property - else { - storedFoldersToAdd.push({ - uri: folderToAdd.toString(true) - }); - } - }); - - if (storedFoldersToAdd.length > 0) { - return this.doSetFolders([...currentStoredFolders, ...storedFoldersToAdd]); - } - - return TPromise.as(void 0); + public addFolders(folders: URI[]): TPromise { + return this.contextService.addFolders(folders) + .then(() => null, error => this.handleWorkspaceConfigurationEditingError(error)); } - public removeFolders(foldersToRemove: URI[]): TPromise { - if (!this.isSupported()) { - return TPromise.as(void 0); // we need a workspace to begin with - } - - const currentWorkspaceFolders = this.contextService.getWorkspace().folders; - const currentStoredFolders = currentWorkspaceFolders.map(folder => folder.raw); - - const newStoredFolders: IStoredWorkspaceFolder[] = currentStoredFolders.filter((folder, index) => { - if (!isStoredWorkspaceFolder(folder)) { - return true; // keep entries which are unrelated - } - - return !this.contains(foldersToRemove, currentWorkspaceFolders[index].uri); // keep entries which are unrelated - }); - - if (newStoredFolders.length !== currentStoredFolders.length) { - return this.doSetFolders(newStoredFolders); - } - - return TPromise.as(void 0); - } - - private doSetFolders(folders: IStoredWorkspaceFolder[]): TPromise { - const workspace = this.contextService.getWorkspace(); - - return this.jsonEditingService.write(workspace.configuration, { key: 'folders', value: folders }, true); - } - - private isSupported(): boolean { - return this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE; // we need a multi folder workspace to begin with; - } - - private contains(resources: URI[], toCheck: URI): boolean { - return resources.some(resource => { - if (isLinux) { - return resource.toString() === toCheck.toString(); - } - - return resource.toString().toLowerCase() === toCheck.toString().toLowerCase(); - }); + public removeFolders(folders: URI[]): TPromise { + return this.contextService.removeFolders(folders) + .then(() => null, error => this.handleWorkspaceConfigurationEditingError(error)); } public createAndEnterWorkspace(folderPaths?: string[], path?: string): TPromise { @@ -137,6 +62,38 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { return this.doEnterWorkspace(() => this.windowService.saveAndEnterWorkspace(path)); } + private handleWorkspaceConfigurationEditingError(error: JSONEditingError): TPromise { + switch (error.code) { + case JSONEditingErrorCode.ERROR_INVALID_FILE: + return this.onInvalidWorkspaceConfigurationFileError(); + case JSONEditingErrorCode.ERROR_FILE_DIRTY: + return this.onWorkspaceConfigurationFileDirtyError(); + } + this.messageService.show(Severity.Error, error.message); + return TPromise.as(void 0); + } + + private onInvalidWorkspaceConfigurationFileError(): TPromise { + const message = nls.localize('errorInvalidTaskConfiguration', "Unable to write into workspace configuration file. Please open the file to correct errors/warnings in it and try again."); + return this.askToOpenWorkspaceConfigurationFile(message); + } + + private onWorkspaceConfigurationFileDirtyError(): TPromise { + const message = nls.localize('errorWorkspaceConfigurationFileDirty', "Unable to write into workspace configuration file because the file is dirty. Please save it and try again."); + return this.askToOpenWorkspaceConfigurationFile(message); + } + + private askToOpenWorkspaceConfigurationFile(message: string): TPromise { + return this.choiceService.choose(Severity.Error, message, [nls.localize('openWorkspaceConfigurationFile', "Open Workspace Configuration File"), nls.localize('close', "Close")], 1) + .then(option => { + switch (option) { + case 0: + this.commandService.executeCommand('workbench.action.openWorkspaceConfigFile'); + break; + } + }); + } + private doEnterWorkspace(mainSidePromise: () => TPromise): TPromise { // Stop the extension host first to give extensions most time to shutdown @@ -190,7 +147,7 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { const targetWorkspaceConfiguration = {}; for (const key of this.workspaceConfigurationService.keys().workspace) { if (configurationProperties[key] && !configurationProperties[key].isFromExtensions && configurationProperties[key].scope === ConfigurationScope.WINDOW) { - targetWorkspaceConfiguration[key] = this.workspaceConfigurationService.lookup(key).workspace; + targetWorkspaceConfiguration[key] = this.workspaceConfigurationService.inspect(key).workspace; } } 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 a935f3c9eed..098ad1ccde8 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts @@ -11,11 +11,11 @@ import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace'; import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration'; import { MainThreadConfigurationShape } from 'vs/workbench/api/node/extHost.protocol'; import { TPromise } from 'vs/base/common/winjs.base'; -import { ConfigurationTarget, ConfigurationEditingErrorCode, ConfigurationEditingError } from 'vs/workbench/services/configuration/common/configurationEditing'; -import { ConfigurationModel } from 'vs/platform/configuration/common/configuration'; +import { ConfigurationModel } from 'vs/platform/configuration/common/configurationModels'; import { TestThreadService } from './testThreadService'; import { mock } from 'vs/workbench/test/electron-browser/api/mock'; import { IWorkspaceFolder, WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; suite('ExtHostConfiguration', function () { @@ -394,7 +394,7 @@ suite('ExtHostConfiguration', function () { const shape = new class extends mock() { $updateConfigurationOption(target: ConfigurationTarget, key: string, value: any): TPromise { - return TPromise.wrapError(new ConfigurationEditingError('Unknown Key', ConfigurationEditingErrorCode.ERROR_UNKNOWN_KEY)); // something !== OK + return TPromise.wrapError(new Error('Unknown Key')); // something !== OK } }; diff --git a/src/vs/workbench/test/electron-browser/api/extHostTextEditor.test.ts b/src/vs/workbench/test/electron-browser/api/extHostTextEditor.test.ts index 402466a620e..48d6e46c0c7 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostTextEditor.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostTextEditor.test.ts @@ -61,6 +61,7 @@ suite('ExtHostTextEditorOptions', () => { $tryShowEditor: undefined, $tryHideEditor: undefined, $trySetDecorations: undefined, + $trySetDecorationsFast: undefined, $tryRevealRange: undefined, $trySetSelections: undefined, $tryApplyEdits: undefined, 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 ee457705998..73399a9bd37 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostWorkspace.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostWorkspace.test.ts @@ -212,6 +212,17 @@ suite('ExtHostWorkspace', function () { sub.dispose(); }); + test('`vscode.workspace.getWorkspaceFolder(file)` don\'t return workspace folder when file open from command line. #36221', function () { + let ws = new ExtHostWorkspace(new TestThreadService(), { + id: 'foo', name: 'Test', folders: [ + aWorkspaceFolderData(URI.file('c:/Users/marek/Desktop/vsc_test/'), 0) + ] + }); + + assert.ok(ws.getWorkspaceFolder(URI.file('c:/Users/marek/Desktop/vsc_test/a.txt'))); + assert.ok(ws.getWorkspaceFolder(URI.file('C:/Users/marek/Desktop/vsc_test/b.txt'))); + }); + function aWorkspaceFolderData(uri: URI, index: number, name: string = ''): IWorkspaceFolderData { return { uri, diff --git a/src/vs/workbench/test/electron-browser/api/mainThreadConfiguration.test.ts b/src/vs/workbench/test/electron-browser/api/mainThreadConfiguration.test.ts index 425dd180872..b91637eeeae 100644 --- a/src/vs/workbench/test/electron-browser/api/mainThreadConfiguration.test.ts +++ b/src/vs/workbench/test/electron-browser/api/mainThreadConfiguration.test.ts @@ -13,13 +13,11 @@ import { Extensions, IConfigurationRegistry, ConfigurationScope } from 'vs/platf import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { MainThreadConfiguration } from 'vs/workbench/api/electron-browser/mainThreadConfiguration'; -import { ConfigurationTarget, IConfigurationEditingService } from 'vs/workbench/services/configuration/common/configurationEditing'; -import { ConfigurationEditingService } from 'vs/workbench/services/configuration/node/configurationEditingService'; import { OneGetThreadService } from './testThreadService'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; +import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; +import { WorkspaceService } from 'vs/workbench/services/configuration/node/configurationService'; -suite('ExtHostConfiguration', function () { +suite('MainThreadConfiguration', function () { let instantiationService: TestInstantiationService; let target: sinon.SinonSpy; @@ -47,12 +45,13 @@ suite('ExtHostConfiguration', function () { }); setup(() => { - instantiationService = new TestInstantiationService(); - instantiationService.stub(IConfigurationService, new TestConfigurationService()); - target = sinon.spy(); - instantiationService.stub(IConfigurationEditingService, ConfigurationEditingService); - instantiationService.stub(IConfigurationEditingService, 'writeConfiguration', target); + + instantiationService = new TestInstantiationService(); + instantiationService.stub(IConfigurationService, WorkspaceService); + instantiationService.stub(IConfigurationService, 'onDidUpdateConfiguration', sinon.mock()); + instantiationService.stub(IConfigurationService, 'onDidChangeConfiguration', sinon.mock()); + instantiationService.stub(IConfigurationService, 'updateValue', target); }); test('update resource configuration without configuration target defaults to workspace in multi root workspace when no resource is provided', function () { @@ -61,7 +60,7 @@ suite('ExtHostConfiguration', function () { testObject.$updateConfigurationOption(null, 'extHostConfiguration.resource', 'value', null); - assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][0]); + assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][3]); }); test('update resource configuration without configuration target defaults to workspace in folder workspace when resource is provider', function () { @@ -70,7 +69,7 @@ suite('ExtHostConfiguration', function () { testObject.$updateConfigurationOption(null, 'extHostConfiguration.resource', 'value', URI.file('abc')); - assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][0]); + assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][3]); }); test('update resource configuration without configuration target defaults to workspace in folder workspace when no resource is provider', function () { @@ -79,7 +78,7 @@ suite('ExtHostConfiguration', function () { testObject.$updateConfigurationOption(null, 'extHostConfiguration.resource', 'value', null); - assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][0]); + assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][3]); }); test('update window configuration without configuration target defaults to workspace in multi root workspace when no resource is provided', function () { @@ -88,7 +87,7 @@ suite('ExtHostConfiguration', function () { testObject.$updateConfigurationOption(null, 'extHostConfiguration.window', 'value', null); - assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][0]); + assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][3]); }); test('update window configuration without configuration target defaults to workspace in multi root workspace when resource is provided', function () { @@ -97,7 +96,7 @@ suite('ExtHostConfiguration', function () { testObject.$updateConfigurationOption(null, 'extHostConfiguration.window', 'value', URI.file('abc')); - assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][0]); + assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][3]); }); test('update window configuration without configuration target defaults to workspace in folder workspace when resource is provider', function () { @@ -106,7 +105,7 @@ suite('ExtHostConfiguration', function () { testObject.$updateConfigurationOption(null, 'extHostConfiguration.window', 'value', URI.file('abc')); - assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][0]); + assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][3]); }); test('update window configuration without configuration target defaults to workspace in folder workspace when no resource is provider', function () { @@ -115,7 +114,7 @@ suite('ExtHostConfiguration', function () { testObject.$updateConfigurationOption(null, 'extHostConfiguration.window', 'value', null); - assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][0]); + assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][3]); }); test('update resource configuration without configuration target defaults to folder', function () { @@ -124,7 +123,7 @@ suite('ExtHostConfiguration', function () { testObject.$updateConfigurationOption(null, 'extHostConfiguration.resource', 'value', URI.file('abc')); - assert.equal(ConfigurationTarget.FOLDER, target.args[0][0]); + assert.equal(ConfigurationTarget.WORKSPACE_FOLDER, target.args[0][3]); }); test('update configuration with user configuration target', function () { @@ -133,7 +132,7 @@ suite('ExtHostConfiguration', function () { testObject.$updateConfigurationOption(ConfigurationTarget.USER, 'extHostConfiguration.window', 'value', URI.file('abc')); - assert.equal(ConfigurationTarget.USER, target.args[0][0]); + assert.equal(ConfigurationTarget.USER, target.args[0][3]); }); test('update configuration with workspace configuration target', function () { @@ -142,16 +141,16 @@ suite('ExtHostConfiguration', function () { testObject.$updateConfigurationOption(ConfigurationTarget.WORKSPACE, 'extHostConfiguration.window', 'value', URI.file('abc')); - assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][0]); + assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][3]); }); test('update configuration with folder configuration target', function () { instantiationService.stub(IWorkspaceContextService, { getWorkbenchState: () => WorkbenchState.FOLDER }); const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null)); - testObject.$updateConfigurationOption(ConfigurationTarget.FOLDER, 'extHostConfiguration.window', 'value', URI.file('abc')); + testObject.$updateConfigurationOption(ConfigurationTarget.WORKSPACE_FOLDER, 'extHostConfiguration.window', 'value', URI.file('abc')); - assert.equal(ConfigurationTarget.FOLDER, target.args[0][0]); + assert.equal(ConfigurationTarget.WORKSPACE_FOLDER, target.args[0][3]); }); test('remove resource configuration without configuration target defaults to workspace in multi root workspace when no resource is provided', function () { @@ -160,7 +159,7 @@ suite('ExtHostConfiguration', function () { testObject.$removeConfigurationOption(null, 'extHostConfiguration.resource', null); - assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][0]); + assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][3]); }); test('remove resource configuration without configuration target defaults to workspace in folder workspace when resource is provider', function () { @@ -169,7 +168,7 @@ suite('ExtHostConfiguration', function () { testObject.$removeConfigurationOption(null, 'extHostConfiguration.resource', URI.file('abc')); - assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][0]); + assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][3]); }); test('remove resource configuration without configuration target defaults to workspace in folder workspace when no resource is provider', function () { @@ -178,7 +177,7 @@ suite('ExtHostConfiguration', function () { testObject.$removeConfigurationOption(null, 'extHostConfiguration.resource', null); - assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][0]); + assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][3]); }); test('remove window configuration without configuration target defaults to workspace in multi root workspace when no resource is provided', function () { @@ -187,7 +186,7 @@ suite('ExtHostConfiguration', function () { testObject.$removeConfigurationOption(null, 'extHostConfiguration.window', null); - assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][0]); + assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][3]); }); test('remove window configuration without configuration target defaults to workspace in multi root workspace when resource is provided', function () { @@ -196,7 +195,7 @@ suite('ExtHostConfiguration', function () { testObject.$removeConfigurationOption(null, 'extHostConfiguration.window', URI.file('abc')); - assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][0]); + assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][3]); }); test('remove window configuration without configuration target defaults to workspace in folder workspace when resource is provider', function () { @@ -205,7 +204,7 @@ suite('ExtHostConfiguration', function () { testObject.$removeConfigurationOption(null, 'extHostConfiguration.window', URI.file('abc')); - assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][0]); + assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][3]); }); test('remove window configuration without configuration target defaults to workspace in folder workspace when no resource is provider', function () { @@ -214,7 +213,7 @@ suite('ExtHostConfiguration', function () { testObject.$removeConfigurationOption(null, 'extHostConfiguration.window', null); - assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][0]); + assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][3]); }); test('remove configuration without configuration target defaults to folder', function () { @@ -223,6 +222,6 @@ suite('ExtHostConfiguration', function () { testObject.$removeConfigurationOption(null, 'extHostConfiguration.resource', URI.file('abc')); - assert.equal(ConfigurationTarget.FOLDER, target.args[0][0]); + assert.equal(ConfigurationTarget.WORKSPACE_FOLDER, target.args[0][3]); }); }); diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index 18d72e4d754..1fcdca0f09c 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -15,7 +15,7 @@ import URI from 'vs/base/common/uri'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; import { StorageService, InMemoryLocalStorage } from 'vs/platform/storage/common/storageService'; -import { IEditorGroup, ConfirmResult } from 'vs/workbench/common/editor'; +import { IEditorGroup, ConfirmResult, IEditorOpeningEvent } from 'vs/workbench/common/editor'; import Event, { Emitter } from 'vs/base/common/event'; import Severity from 'vs/base/common/severity'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; @@ -26,7 +26,7 @@ import { TextModelResolverService } from 'vs/workbench/services/textmodelResolve import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { IEditorInput, IEditorOptions, Position, Direction, IEditor, IResourceInput } from 'vs/platform/editor/common/editor'; import { IUntitledEditorService, UntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; -import { IMessageService, IConfirmation, IConfirmationResult } from 'vs/platform/message/common/message'; +import { IMessageService, IConfirmation, IConfirmationResult, IChoiceService } from 'vs/platform/message/common/message'; 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'; @@ -102,12 +102,14 @@ export class TestContextService implements IWorkspaceContextService { } public getWorkbenchState(): WorkbenchState { - if (this.workspace) { - if (this.workspace.configuration) { - return WorkbenchState.WORKSPACE; - } + if (this.workspace.configuration) { + return WorkbenchState.WORKSPACE; + } + + if (this.workspace.folders.length) { return WorkbenchState.FOLDER; } + return WorkbenchState.EMPTY; } @@ -115,6 +117,14 @@ export class TestContextService implements IWorkspaceContextService { return this.workspace; } + public addFolders(foldersToAdd: URI[]): TPromise { + return TPromise.as(void 0); + } + + public removeFolders(foldersToRemove: URI[]): TPromise { + return TPromise.as(void 0); + } + public getWorkspaceFolder(resource: URI): IWorkspaceFolder { return this.isInsideWorkspace(resource) ? this.workspace.folders[0] : null; } @@ -223,8 +233,8 @@ export class TestTextFileService extends TextFileService { return this.confirmResult; } - public onConfigurationChange(configuration: any): void { - super.onConfigurationChange(configuration); + public onFilesConfigurationChange(configuration: any): void { + super.onFilesConfigurationChange(configuration); } protected cleanupBackupsBeforeShutdown(): TPromise { @@ -257,6 +267,11 @@ export function workbenchInstantiationService(): IInstantiationService { instantiationService.stub(ITextModelService, instantiationService.createInstance(TextModelResolverService)); instantiationService.stub(IEnvironmentService, TestEnvironmentService); instantiationService.stub(IThemeService, new TestThemeService()); + instantiationService.stub(IChoiceService, { + choose: (severity, message, options, cancelId): TPromise => { + return TPromise.as(cancelId); + } + }); return instantiationService; } @@ -453,6 +468,7 @@ export class TestEditorGroupService implements IEditorGroupService { private stacksModel: EditorStacksModel; private _onEditorsChanged: Emitter; + private _onEditorOpening: Emitter; private _onEditorOpenFail: Emitter; private _onEditorsMoved: Emitter; private _onGroupOrientationChanged: Emitter; @@ -461,6 +477,7 @@ export class TestEditorGroupService implements IEditorGroupService { constructor(callback?: (method: string) => void) { this._onEditorsMoved = new Emitter(); this._onEditorsChanged = new Emitter(); + this._onEditorOpening = new Emitter(); this._onGroupOrientationChanged = new Emitter(); this._onEditorOpenFail = new Emitter(); this._onTabOptionsChanged = new Emitter(); @@ -487,6 +504,10 @@ export class TestEditorGroupService implements IEditorGroupService { return this._onEditorsChanged.event; } + public get onEditorOpening(): Event { + return this._onEditorOpening.event; + } + public get onEditorOpenFail(): Event { return this._onEditorOpenFail.event; } @@ -1205,7 +1226,7 @@ export class TestTextResourceConfigurationService implements ITextResourceConfig constructor(private configurationService = new TestConfigurationService()) { } - public onDidUpdateConfiguration() { + public onDidChangeConfiguration() { return { dispose() { } }; } @@ -1215,4 +1236,8 @@ export class TestTextResourceConfigurationService implements ITextResourceConfig public getConfiguration(resource: any, position?: any, section?: any): any { return this.configurationService.getConfiguration(section, { resource }); } +} + +export function getRandomTestPath(tmpdir: string, ...segments: string[]): string { + return paths.join(tmpdir, ...segments, generateUuid()); } \ No newline at end of file diff --git a/src/vs/workbench/workbench.main.ts b/src/vs/workbench/workbench.main.ts index b162fb6a023..4844c81d0aa 100644 --- a/src/vs/workbench/workbench.main.ts +++ b/src/vs/workbench/workbench.main.ts @@ -9,6 +9,9 @@ import 'vs/base/common/strings'; import 'vs/base/common/errors'; +// Configuration +import 'vs/workbench/services/configuration/common/configurationExtensionPoint'; + // Editor import 'vs/editor/editor.all'; diff --git a/test/smoke/README.md b/test/smoke/README.md index 995fc730a4c..48e4cd60acd 100644 --- a/test/smoke/README.md +++ b/test/smoke/README.md @@ -40,3 +40,7 @@ Here's an example: when opening quick open, focus goes from its list to its inpu ### I type in a Monaco editor instance, but the text doesn't appear to be there This is a **waiting** issue. When you type in a Monaco editor instance, you're really typing in a `textarea`. The `textarea` is then polled for its contents, then the editor model gets updated and finally the editor view gets updated. It's a good idea to always wait for the text to appear rendered in the editor after you type in it. + +### I type in a Monaco editor instance, but the text appears scrambled + +This is an issue which is **not yet fixed**. Unfortunately this seems to happen whenever the CPU load of the system is high. Rerunning the test will often result in a successful outcome. \ No newline at end of file diff --git a/test/smoke/package.json b/test/smoke/package.json index 1908b5ebca7..ebcf1a12e04 100644 --- a/test/smoke/package.json +++ b/test/smoke/package.json @@ -8,7 +8,6 @@ "mocha": "mocha" }, "devDependencies": { - "@types/electron": "~1.4.37", "@types/htmlparser2": "3.7.29", "@types/mkdirp": "0.5.1", "@types/mocha": "2.2.41", @@ -16,14 +15,16 @@ "@types/node": "8.0.33", "@types/rimraf": "2.0.2", "@types/webdriverio": "4.6.1", + "electron": "1.7.7", "htmlparser2": "^3.9.2", + "mkdirp": "^0.5.1", "mocha": "^3.2.0", "ncp": "^2.0.0", "portastic": "^1.0.1", "rimraf": "^2.6.1", - "spectron": "~3.6.4", + "spectron": "^3.7.2", "strip-json-comments": "^2.0.1", "tmp": "0.0.33", "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 e49b90b9551..d0b4c01b201 100644 --- a/test/smoke/src/areas/css/css.test.ts +++ b/test/smoke/src/areas/css/css.test.ts @@ -11,7 +11,6 @@ describe('CSS', () => { let app: SpectronApplication; before(function () { app = new SpectronApplication(); return app.start('CSS'); }); after(() => app.stop()); - beforeEach(function () { app.screenCapturer.testName = this.currentTest.title; }); it('verifies quick outline', async () => { await app.workbench.quickopen.openFile('style.css'); @@ -22,7 +21,7 @@ describe('CSS', () => { it('verifies warnings for the empty rule', async () => { await app.workbench.quickopen.openFile('style.css'); - await app.client.type('.foo{}'); + await app.workbench.editor.waitForTypeInEditor('style.css', '.foo{}'); let warning = await app.client.waitForElement(Problems.getSelectorInEditor(ProblemSeverity.WARNING)); await app.screenCapturer.capture('CSS Warning in editor'); @@ -38,7 +37,7 @@ describe('CSS', () => { it('verifies that warning becomes an error once setting changed', async () => { await app.workbench.settingsEditor.addUserSetting('css.lint.emptyRules', '"error"'); await app.workbench.quickopen.openFile('style.css'); - await app.client.type('.foo{}'); + await app.workbench.editor.waitForTypeInEditor('style.css', '.foo{}'); let error = await app.client.waitForElement(Problems.getSelectorInEditor(ProblemSeverity.ERROR)); await app.screenCapturer.capture('CSS Error in editor'); diff --git a/test/smoke/src/areas/debug/debug.test.ts b/test/smoke/src/areas/debug/debug.test.ts index 379bb46df85..5c7a759eb40 100644 --- a/test/smoke/src/areas/debug/debug.test.ts +++ b/test/smoke/src/areas/debug/debug.test.ts @@ -42,7 +42,6 @@ describe('Debug', () => { // otherwise concurrent test runs will clash on those ports before(async () => await app.start('Debug', [], { PORT: String(await findFreePort()), ...process.env })); after(() => app.stop()); - beforeEach(function () { app.screenCapturer.testName = this.currentTest.title; }); it('configure launch json', async function () { await app.workbench.debug.openDebugViewlet(); diff --git a/test/smoke/src/areas/debug/debug.ts b/test/smoke/src/areas/debug/debug.ts index a47299176d2..7537ca1e69a 100644 --- a/test/smoke/src/areas/debug/debug.ts +++ b/test/smoke/src/areas/debug/debug.ts @@ -41,7 +41,7 @@ export class Debug extends Viewlet { } async openDebugViewlet(): Promise { - await this.spectron.command('workbench.view.debug'); + await this.spectron.runCommand('workbench.view.debug'); await this.spectron.client.waitForElement(DEBUG_VIEW); } @@ -114,8 +114,8 @@ export class Debug extends Viewlet { async waitForReplCommand(text: string, accept: (result: string) => boolean): Promise { await this.spectron.workbench.quickopen.runCommand('Debug: Focus Debug Console'); await this.spectron.client.waitForActiveElement(REPL_FOCUSED); + await this.spectron.client.setValue(REPL_FOCUSED, text); - await this.spectron.client.keys(text); // Wait for the keys to be picked up by the editor model such that repl evalutes what just got typed await this.spectron.workbench.editor.waitForEditorContents('debug:input', s => s.indexOf(text) >= 0); await this.spectron.client.keys(['Enter', 'NULL']); diff --git a/test/smoke/src/areas/editor/editor.test.ts b/test/smoke/src/areas/editor/editor.test.ts index 896884c044d..72a537c4e23 100644 --- a/test/smoke/src/areas/editor/editor.test.ts +++ b/test/smoke/src/areas/editor/editor.test.ts @@ -3,15 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as assert from 'assert'; - import { SpectronApplication } from '../../spectron/application'; describe('Editor', () => { let app: SpectronApplication; before(() => { app = new SpectronApplication(); return app.start('Editor'); }); after(() => app.stop()); - beforeEach(function () { app.screenCapturer.testName = this.currentTest.title; }); it('shows correct quick outline', async function () { await app.workbench.quickopen.openFile('www'); @@ -32,31 +29,26 @@ describe('Editor', () => { it(`renames local 'app' variable`, async function () { await app.workbench.quickopen.openFile('www'); - - const selector = await app.workbench.editor.getSelector('app', 7); - const rename = await app.workbench.editor.rename('app', 7); - await rename.rename('newApp'); - - const actual = await app.client.waitForText(selector, 'newApp'); + await app.workbench.editor.rename('www', 7, 'app', 'newApp'); + await app.workbench.editor.waitForEditorContents('www', contents => contents.indexOf('newApp') > -1); await app.screenCapturer.capture('Rename result'); - assert.equal(actual, 'newApp'); }); - it('folds/unfolds the code correctly', async function () { - await app.workbench.quickopen.openFile('www'); + // it('folds/unfolds the code correctly', async function () { + // await app.workbench.quickopen.openFile('www'); - // Fold - await app.workbench.editor.foldAtLine(3); - await app.workbench.editor.waitUntilShown(3); - await app.workbench.editor.waitUntilHidden(4); - await app.workbench.editor.waitUntilHidden(5); + // // Fold + // await app.workbench.editor.foldAtLine(3); + // await app.workbench.editor.waitUntilShown(3); + // await app.workbench.editor.waitUntilHidden(4); + // await app.workbench.editor.waitUntilHidden(5); - // Unfold - await app.workbench.editor.unfoldAtLine(3); - await app.workbench.editor.waitUntilShown(3); - await app.workbench.editor.waitUntilShown(4); - await app.workbench.editor.waitUntilShown(5); - }); + // // Unfold + // await app.workbench.editor.unfoldAtLine(3); + // await app.workbench.editor.waitUntilShown(3); + // await app.workbench.editor.waitUntilShown(4); + // await app.workbench.editor.waitUntilShown(5); + // }); it(`verifies that 'Go To Definition' works`, async function () { await app.workbench.quickopen.openFile('app.js'); diff --git a/test/smoke/src/areas/editor/editor.ts b/test/smoke/src/areas/editor/editor.ts index 5c81f8f47bd..499f0fe6196 100644 --- a/test/smoke/src/areas/editor/editor.ts +++ b/test/smoke/src/areas/editor/editor.ts @@ -6,7 +6,9 @@ import { SpectronApplication } from '../../spectron/application'; import { QuickOutline } from './quickoutline'; import { References } from './peek'; -import { Rename } from './rename'; + +const RENAME_BOX = '.monaco-editor .monaco-editor.rename-box'; +const RENAME_INPUT = `${RENAME_BOX} .rename-input`; export class Editor { @@ -18,13 +20,13 @@ export class Editor { constructor(private spectron: SpectronApplication) { } - public async openOutline(): Promise { + async openOutline(): Promise { const outline = new QuickOutline(this.spectron); await outline.open(); return outline; } - public async findReferences(term: string, line: number): Promise { + async findReferences(term: string, line: number): Promise { await this.clickOnTerm(term, line); await this.spectron.workbench.quickopen.runCommand('Find All References'); const references = new References(this.spectron); @@ -32,20 +34,22 @@ export class Editor { return references; } - public async rename(term: string, line: number): Promise { - await this.clickOnTerm(term, line); + async rename(filename: string, line: number, from: string, to: string): Promise { + await this.clickOnTerm(from, line); await this.spectron.workbench.quickopen.runCommand('Rename Symbol'); - const rename = new Rename(term, this.spectron); - await rename.waitUntilOpen(); - return rename; + + await this.spectron.client.waitForActiveElement(RENAME_INPUT); + await this.spectron.client.setValue(RENAME_INPUT, to); + + await this.spectron.client.keys(['Enter', 'NULL']); } - public async gotoDefinition(term: string, line: number): Promise { + async gotoDefinition(term: string, line: number): Promise { await this.clickOnTerm(term, line); await this.spectron.workbench.quickopen.runCommand('Go to Definition'); } - public async peekDefinition(term: string, line: number): Promise { + async peekDefinition(term: string, line: number): Promise { await this.clickOnTerm(term, line); await this.spectron.workbench.quickopen.runCommand('Peek Definition'); const peek = new References(this.spectron); @@ -53,7 +57,7 @@ export class Editor { return peek; } - public async waitForHighlightingLine(line: number): Promise { + async waitForHighlightingLine(line: number): Promise { const currentLineIndex = await this.getViewLineIndex(line); if (currentLineIndex) { await this.spectron.client.waitForElement(`.monaco-editor .view-overlays>:nth-child(${currentLineIndex}) .current-line`); @@ -62,60 +66,81 @@ export class Editor { throw new Error('Cannot find line ' + line); } - public async getSelector(term: string, line: number): Promise { + async getSelector(term: string, line: number): Promise { const lineIndex = await this.getViewLineIndex(line); const classNames = await this.spectron.client.waitFor(() => this.getClassSelectors(term, lineIndex), classNames => classNames && !!classNames.length, 'Getting class names for editor lines'); return `${Editor.VIEW_LINES}>:nth-child(${lineIndex}) span span.${classNames[0]}`; } - public async foldAtLine(line: number): Promise { + async foldAtLine(line: number): Promise { const lineIndex = await this.getViewLineIndex(line); await this.spectron.client.waitAndClick(Editor.FOLDING_EXPANDED.replace('${INDEX}', '' + lineIndex)); await this.spectron.client.waitForElement(Editor.FOLDING_COLLAPSED.replace('${INDEX}', '' + lineIndex)); } - public async unfoldAtLine(line: number): Promise { + async unfoldAtLine(line: number): Promise { const lineIndex = await this.getViewLineIndex(line); await this.spectron.client.waitAndClick(Editor.FOLDING_COLLAPSED.replace('${INDEX}', '' + lineIndex)); await this.spectron.client.waitForElement(Editor.FOLDING_EXPANDED.replace('${INDEX}', '' + lineIndex)); } - public async waitUntilHidden(line: number): Promise { + async waitUntilHidden(line: number): Promise { await this.spectron.client.waitFor(() => this.getViewLineIndexWithoutWait(line), lineNumber => lineNumber === undefined, 'Waiting until line number is hidden'); } - public async waitUntilShown(line: number): Promise { + async waitUntilShown(line: number): Promise { await this.getViewLineIndex(line); } - public async clickOnTerm(term: string, line: number): Promise { + async clickOnTerm(term: string, line: number): Promise { const selector = await this.getSelector(term, line); await this.spectron.client.waitAndClick(selector); } - public async waitForTypeInEditor(filename: string, text: string): Promise { - const editor = `.monaco-editor[data-uri$="${filename}"]`; - await this.spectron.client.waitAndClick(editor); + async waitForTypeInEditor(filename: string, text: string, selectorPrefix = ''): Promise { + const editor = [ + selectorPrefix || '', + `.monaco-editor[data-uri$="${filename}"]` + ].join(' '); + + await this.spectron.client.element(editor); const textarea = `${editor} textarea`; await this.spectron.client.waitForActiveElement(textarea); - await this.spectron.client.type(text); + // https://github.com/Microsoft/vscode/issues/34203#issuecomment-334441786 + await this.spectron.client.spectron.client.selectorExecute(textarea, (elements, text) => { + const textarea = (Array.isArray(elements) ? elements : [elements])[0] as HTMLTextAreaElement; + const start = textarea.selectionStart; + const newStart = start + text.length; + const value = textarea.value; + const newValue = value.substr(0, start) + text + value.substr(start); - await this.waitForEditorContents(filename, c => c.indexOf(text) > -1); + textarea.value = newValue; + textarea.setSelectionRange(newStart, newStart); + + const event = new Event('input', { 'bubbles': true, 'cancelable': true }); + textarea.dispatchEvent(event); + }, text); + + await this.waitForEditorContents(filename, c => c.indexOf(text) > -1, selectorPrefix); } - public async waitForEditorContents(filename: string, accept: (contents: string) => boolean): Promise { - const selector = `.monaco-editor[data-uri$="${filename}"] .view-lines`; + async waitForEditorContents(filename: string, accept: (contents: string) => boolean, selectorPrefix = ''): Promise { + const selector = [ + selectorPrefix || '', + `.monaco-editor[data-uri$="${filename}"] .view-lines` + ].join(' '); + return this.spectron.client.waitForTextContent(selector, undefined, c => accept(c.replace(/\u00a0/g, ' '))); } - public async waitForActiveEditor(filename: string): Promise { + 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 { + // 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 => { diff --git a/test/smoke/src/areas/editor/quickoutline.ts b/test/smoke/src/areas/editor/quickoutline.ts index 35fab316ee0..852bd8b9145 100644 --- a/test/smoke/src/areas/editor/quickoutline.ts +++ b/test/smoke/src/areas/editor/quickoutline.ts @@ -14,7 +14,7 @@ export class QuickOutline extends QuickOpen { public async open(): Promise { await this.spectron.client.waitFor(async () => { - await this.spectron.command('workbench.action.gotoSymbol'); + await this.spectron.runCommand('workbench.action.gotoSymbol'); const entry = await this.spectron.client.element('div[aria-label="Quick Picker"] .monaco-tree-rows.show-twisties div.monaco-tree-row .quick-open-entry'); if (entry) { const text = await this.spectron.client.getText('div[aria-label="Quick Picker"] .monaco-tree-rows.show-twisties div.monaco-tree-row .quick-open-entry .monaco-icon-label .label-name .monaco-highlighted-label span'); diff --git a/test/smoke/src/areas/editor/rename.ts b/test/smoke/src/areas/editor/rename.ts deleted file mode 100644 index 521798c9ba3..00000000000 --- a/test/smoke/src/areas/editor/rename.ts +++ /dev/null @@ -1,26 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { SpectronApplication } from '../../spectron/application'; - -export class Rename { - - private static RENAME_BOX = '.monaco-editor .monaco-editor.rename-box'; - private static RENAME_INPUT = `${Rename.RENAME_BOX} .rename-input`; - - constructor(private term: string, private spectron: SpectronApplication) { - } - - public async waitUntilOpen(): Promise { - await this.spectron.client.waitForVisibility(Rename.RENAME_BOX); - await this.spectron.client.waitForValue(Rename.RENAME_INPUT, this.term); - } - - public async rename(newTerm: string): Promise { - await this.spectron.client.type(newTerm); - await this.spectron.client.keys(['Enter', 'NULL']); - await this.spectron.client.waitForVisibility(Rename.RENAME_BOX, result => !result); - } -} \ No newline at end of file diff --git a/test/smoke/src/areas/explorer/explorer.test.ts b/test/smoke/src/areas/explorer/explorer.test.ts index 665b525a9f2..581851e0ab8 100644 --- a/test/smoke/src/areas/explorer/explorer.test.ts +++ b/test/smoke/src/areas/explorer/explorer.test.ts @@ -9,7 +9,6 @@ describe('Explorer', () => { let app: SpectronApplication; before(() => { app = new SpectronApplication(); return app.start('Explorer'); }); after(() => app.stop()); - beforeEach(function () { app.screenCapturer.testName = this.currentTest.title; }); it('quick open search produces correct result', async function () { const expectedNames = [ @@ -22,8 +21,7 @@ describe('Explorer', () => { 'jsconfig.json' ]; - await app.workbench.quickopen.openQuickOpen(); - await app.client.type('.js'); + await app.workbench.quickopen.openQuickOpen('.js'); await app.workbench.quickopen.waitForQuickOpenElements(names => expectedNames.every(n => names.some(m => n === m))); await app.client.keys(['Escape', 'NULL']); }); @@ -35,8 +33,7 @@ describe('Explorer', () => { 'package.json' ]; - await app.workbench.quickopen.openQuickOpen(); - await app.client.type('a.s'); + await app.workbench.quickopen.openQuickOpen('a.s'); await app.workbench.quickopen.waitForQuickOpenElements(names => expectedNames.every(n => names.some(m => n === m))); await app.client.keys(['Escape', 'NULL']); }); diff --git a/test/smoke/src/areas/explorer/explorer.ts b/test/smoke/src/areas/explorer/explorer.ts index e799bc0f225..1dd454462ac 100644 --- a/test/smoke/src/areas/explorer/explorer.ts +++ b/test/smoke/src/areas/explorer/explorer.ts @@ -17,7 +17,7 @@ export class Explorer extends Viewlet { } public openExplorerView(): Promise { - return this.spectron.command('workbench.view.explorer'); + return this.spectron.runCommand('workbench.view.explorer'); } public getOpenEditorsViewTitle(): Promise { diff --git a/test/smoke/src/areas/extensions/extensions.test.ts b/test/smoke/src/areas/extensions/extensions.test.ts index 11b11538fff..316d0a2a72c 100644 --- a/test/smoke/src/areas/extensions/extensions.test.ts +++ b/test/smoke/src/areas/extensions/extensions.test.ts @@ -10,7 +10,6 @@ describe('Extensions', () => { let app: SpectronApplication = new SpectronApplication(); before(() => app.start('Extensions')); after(() => app.stop()); - beforeEach(function () { app.screenCapturer.testName = this.currentTest.title; }); if (app.build !== VSCODE_BUILD.DEV) { it(`install and activate vscode-smoketest-check extension`, async function () { diff --git a/test/smoke/src/areas/extensions/extensions.ts b/test/smoke/src/areas/extensions/extensions.ts index 62e0bb936db..d0c3f97320d 100644 --- a/test/smoke/src/areas/extensions/extensions.ts +++ b/test/smoke/src/areas/extensions/extensions.ts @@ -6,6 +6,8 @@ import { SpectronApplication } from '../../spectron/application'; import { Viewlet } from '../workbench/viewlet'; +const SEARCH_BOX = 'div.extensions-viewlet[id="workbench.view.extensions"] input.search-box'; + export class Extensions extends Viewlet { constructor(spectron: SpectronApplication) { @@ -13,21 +15,18 @@ export class Extensions extends Viewlet { } async openExtensionsViewlet(): Promise { - await this.spectron.command('workbench.view.extensions'); + await this.spectron.runCommand('workbench.view.extensions'); await this.waitForExtensionsViewlet(); } async waitForExtensionsViewlet(): Promise { - await this.spectron.client.waitForActiveElement('div.extensions-viewlet[id="workbench.view.extensions"] input.search-box'); + await this.spectron.client.waitForActiveElement(SEARCH_BOX); } 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.waitForActiveElement('div.extensions-viewlet[id="workbench.view.extensions"] input.search-box'); - await this.spectron.client.keys(name); + await this.spectron.client.click(SEARCH_BOX); + await this.spectron.client.waitForActiveElement(SEARCH_BOX); + await this.spectron.client.setValue(SEARCH_BOX, name); } async installExtension(name: string): Promise { diff --git a/test/smoke/src/areas/git/git.test.ts b/test/smoke/src/areas/git/git.test.ts index 1bf9f78b609..57c0240b1d4 100644 --- a/test/smoke/src/areas/git/git.test.ts +++ b/test/smoke/src/areas/git/git.test.ts @@ -14,17 +14,16 @@ describe('Git', () => { let app: SpectronApplication; before(() => { app = new SpectronApplication(); return app.start('Git'); }); after(() => app.stop()); - beforeEach(function () { app.screenCapturer.testName = this.currentTest.title; }); it('reflects working tree changes', async function () { await app.workbench.scm.openSCMViewlet(); await app.workbench.quickopen.openFile('app.js'); - await app.client.type('.foo{}'); + await app.workbench.editor.waitForTypeInEditor('app.js', '.foo{}'); await app.workbench.saveOpenedFile(); await app.workbench.quickopen.openFile('index.jade'); - await app.client.type('hello world'); + await app.workbench.editor.waitForTypeInEditor('index.jade', 'hello world'); await app.workbench.saveOpenedFile(); await app.workbench.scm.refreshSCMViewlet(); diff --git a/test/smoke/src/areas/git/scm.ts b/test/smoke/src/areas/git/scm.ts index 90642445171..94576417744 100644 --- a/test/smoke/src/areas/git/scm.ts +++ b/test/smoke/src/areas/git/scm.ts @@ -30,7 +30,7 @@ export class SCM extends Viewlet { } async openSCMViewlet(): Promise { - await this.spectron.command('workbench.view.scm'); + await this.spectron.runCommand('workbench.view.scm'); await this.spectron.client.waitForElement(SCM_INPUT); } @@ -95,8 +95,9 @@ export class SCM extends Viewlet { } async commit(message: string): Promise { - await this.spectron.client.click(SCM_INPUT); - await this.spectron.client.type(message); - await this.spectron.client.click(COMMIT_COMMAND); + await this.spectron.client.waitAndClick(SCM_INPUT); + await this.spectron.client.waitForActiveElement(SCM_INPUT); + await this.spectron.client.setValue(SCM_INPUT, message); + await this.spectron.client.waitAndClick(COMMIT_COMMAND); } } \ No newline at end of file diff --git a/test/smoke/src/areas/multiroot/multiroot.test.ts b/test/smoke/src/areas/multiroot/multiroot.test.ts index 839e2848d42..f341f3da137 100644 --- a/test/smoke/src/areas/multiroot/multiroot.test.ts +++ b/test/smoke/src/areas/multiroot/multiroot.test.ts @@ -5,7 +5,6 @@ import * as assert from 'assert'; import { SpectronApplication, CODE_WORKSPACE_PATH, VSCODE_BUILD } from '../../spectron/application'; -import { Window } from '../window'; describe('Multiroot', () => { let app: SpectronApplication = new SpectronApplication(void 0, CODE_WORKSPACE_PATH); @@ -15,18 +14,16 @@ describe('Multiroot', () => { before(() => app.start('Multi Root')); after(() => app.stop()); - beforeEach(function () { app.screenCapturer.testName = this.currentTest.title; }); it('shows results from all folders', async function () { - await app.workbench.quickopen.openQuickOpen(); - await app.workbench.quickopen.type('*.*'); + await app.workbench.quickopen.openQuickOpen('*.*'); await app.workbench.quickopen.waitForQuickOpenElements(names => names.length >= 6); await app.workbench.quickopen.closeQuickOpen(); }); it('shows workspace name in title', async function () { - const title = await new Window(app).getTitle(); + const title = await app.client.getTitle(); await app.screenCapturer.capture('window title'); assert.ok(title.indexOf('smoketest (Workspace)') >= 0); }); diff --git a/test/smoke/src/areas/preferences/keybindings.ts b/test/smoke/src/areas/preferences/keybindings.ts index c6c7431ff19..1de2eba3470 100644 --- a/test/smoke/src/areas/preferences/keybindings.ts +++ b/test/smoke/src/areas/preferences/keybindings.ts @@ -5,36 +5,24 @@ import { SpectronApplication } from '../../spectron/application'; +const SEARCH_INPUT = '.settings-search-input input'; + export class KeybindingsEditor { - constructor(private spectron: SpectronApplication) { - // noop - } + constructor(private spectron: SpectronApplication) { } - public async openKeybindings(): Promise { - await this.spectron.command('workbench.action.openGlobalKeybindings'); - await this.spectron.client.waitForElement('.settings-search-input .synthetic-focus'); - } + async updateKeybinding(command: string, keys: string[], ariaLabel: string): Promise { + await this.spectron.runCommand('workbench.action.openGlobalKeybindings'); + await this.spectron.client.waitForActiveElement(SEARCH_INPUT); + await this.spectron.client.setValue(SEARCH_INPUT, command); - public async search(text: string, select: boolean = false): Promise { - await this.spectron.client.type(text); - if (select) { - await this.spectron.client.waitAndClick('div[aria-label="Keybindings"] .monaco-list-row.keybinding-item'); - await this.spectron.client.waitForElement('div[aria-label="Keybindings"] .monaco-list-row.keybinding-item.focused.selected'); - } - } + await this.spectron.client.waitAndClick('div[aria-label="Keybindings"] .monaco-list-row.keybinding-item'); + await this.spectron.client.waitForElement('div[aria-label="Keybindings"] .monaco-list-row.keybinding-item.focused.selected'); - public async openDefineKeybindingDialog(): Promise { await this.spectron.client.waitAndClick('div[aria-label="Keybindings"] .monaco-list-row.keybinding-item .action-item .icon.add'); - return this.spectron.client.waitForElement('.defineKeybindingWidget .monaco-inputbox.synthetic-focus'); - } + await this.spectron.client.waitForElement('.defineKeybindingWidget .monaco-inputbox.synthetic-focus'); - public async updateKeybinding(command: string, keys: string[], ariaLabel: string): Promise { - await this.search(command, true); - await this.openDefineKeybindingDialog(); - await this.spectron.client.keys(keys); - await this.spectron.client.keys(['Enter', 'NULL']); + await this.spectron.client.keys([...keys, 'NULL', 'Enter', 'NULL']); await this.spectron.client.waitForElement(`div[aria-label="Keybindings"] div[aria-label="Keybinding is ${ariaLabel}."]`); } - } \ No newline at end of file diff --git a/test/smoke/src/areas/preferences/preferences.test.ts b/test/smoke/src/areas/preferences/preferences.test.ts index 795c3aba1d2..4a9939bf2b3 100644 --- a/test/smoke/src/areas/preferences/preferences.test.ts +++ b/test/smoke/src/areas/preferences/preferences.test.ts @@ -12,18 +12,17 @@ describe('Preferences', () => { let app: SpectronApplication; before(() => { app = new SpectronApplication(); return app.start('Preferences'); }); after(() => app.stop()); - beforeEach(function () { app.screenCapturer.testName = this.currentTest.title; }); it('turns off editor line numbers and verifies the live change', async function () { await app.workbench.explorer.openFile('app.js'); let lineNumbers = await app.client.waitForElements('.line-numbers'); - await app.screenCapturer.capture('line numbers'); + await app.screenCapturer.capture('app.js has line numbers'); assert.ok(!!lineNumbers.length, 'Line numbers are not present in the editor before disabling them.'); await app.workbench.settingsEditor.addUserSetting('editor.lineNumbers', '"off"'); - await app.workbench.selectTab('app.js'); lineNumbers = await app.client.waitForElements('.line-numbers', result => !result || result.length === 0); + await app.screenCapturer.capture('line numbers hidden'); assert.ok(!lineNumbers.length, 'Line numbers are still present in the editor after disabling them.'); }); @@ -31,8 +30,7 @@ describe('Preferences', () => { it(`changes 'workbench.action.toggleSidebarPosition' command key binding and verifies it`, async function () { assert.ok(await app.workbench.activitybar.getActivityBar(ActivityBarPosition.LEFT), 'Activity bar should be positioned on the left.'); - await app.workbench.keybindingsEditor.openKeybindings(); - await app.workbench.keybindingsEditor.updateKeybinding('workbench.action.toggleSidebarPosition', ['Control', 'u', 'NULL'], 'Control+U'); + await app.workbench.keybindingsEditor.updateKeybinding('workbench.action.toggleSidebarPosition', ['Control', 'u'], 'Control+U'); await app.client.keys(['Control', 'u', 'NULL']); assert.ok(await app.workbench.activitybar.getActivityBar(ActivityBarPosition.RIGHT), 'Activity bar was not moved to right after toggling its position.'); diff --git a/test/smoke/src/areas/preferences/settings.ts b/test/smoke/src/areas/preferences/settings.ts index b771b3702cc..ce28c1b2c9e 100644 --- a/test/smoke/src/areas/preferences/settings.ts +++ b/test/smoke/src/areas/preferences/settings.ts @@ -10,28 +10,26 @@ export enum ActivityBarPosition { RIGHT = 1 }; +const SEARCH_INPUT = '.settings-search-input input'; +const EDITOR = '.editable-preferences-editor-container .monaco-editor textarea'; + export class SettingsEditor { - constructor(private spectron: SpectronApplication) { - // noop - } - - async openUserSettings(): Promise { - await this.spectron.command('workbench.action.openGlobalSettings'); - await this.spectron.client.waitForActiveElement('.settings-search-input input'); - } - - async focusEditableSettings(): Promise { - await this.spectron.client.keys(['ArrowDown', 'NULL'], false); - await this.spectron.client.waitForActiveElement('.editable-preferences-editor-container .monaco-editor textarea'); - await this.spectron.client.keys(['ArrowRight', 'NULL'], false); - } + constructor(private spectron: SpectronApplication) { } async addUserSetting(setting: string, value: string): Promise { - await this.openUserSettings(); + await this.spectron.runCommand('workbench.action.openGlobalSettings'); + await this.spectron.client.waitForActiveElement(SEARCH_INPUT); - await this.focusEditableSettings(); - await this.spectron.client.keys(`"${setting}": ${value},`); + await this.spectron.client.keys(['ArrowDown', 'NULL']); + await this.spectron.client.waitForActiveElement(EDITOR); + + await this.spectron.client.keys(['ArrowRight', 'NULL']); + await this.spectron.screenCapturer.capture('user settings is open and focused'); + + await this.spectron.workbench.editor.waitForTypeInEditor('settings.json', `"${setting}": ${value}`, '.editable-preferences-editor-container'); await this.spectron.workbench.saveOpenedFile(); + + await this.spectron.screenCapturer.capture('user settings has changed'); } } \ No newline at end of file diff --git a/test/smoke/src/areas/problems/problems.ts b/test/smoke/src/areas/problems/problems.ts index 2e6f7bc1e5a..ed1ea406c1a 100644 --- a/test/smoke/src/areas/problems/problems.ts +++ b/test/smoke/src/areas/problems/problems.ts @@ -20,14 +20,14 @@ export class Problems { public async showProblemsView(): Promise { if (!await this.isVisible()) { - await this.spectron.command('workbench.actions.view.problems'); + await this.spectron.runCommand('workbench.actions.view.problems'); await this.waitForProblemsView(); } } public async hideProblemsView(): Promise { if (await this.isVisible()) { - await this.spectron.command('workbench.actions.view.problems'); + await this.spectron.runCommand('workbench.actions.view.problems'); await this.spectron.client.waitForElement(Problems.PROBLEMS_VIEW_SELECTOR, el => !el); } } diff --git a/test/smoke/src/areas/quickopen/quickopen.ts b/test/smoke/src/areas/quickopen/quickopen.ts index 5260874be38..4f70d280c11 100644 --- a/test/smoke/src/areas/quickopen/quickopen.ts +++ b/test/smoke/src/areas/quickopen/quickopen.ts @@ -15,28 +15,22 @@ export class QuickOpen { constructor(readonly spectron: SpectronApplication) { } - async openQuickOpen(): Promise { - await this.spectron.command('workbench.action.quickOpen'); + async openQuickOpen(value: string): Promise { + await this.spectron.runCommand('workbench.action.quickOpen'); await this.waitForQuickOpenOpened(); - } - async openCommandPallette(): Promise { - await this.spectron.command('workbench.action.showCommands'); - await this.waitForQuickOpenOpened(); + if (value) { + await this.spectron.client.setValue(QuickOpen.QUICK_OPEN_INPUT, value); + } } async closeQuickOpen(): Promise { - await this.spectron.command('workbench.action.closeQuickOpen'); + await this.spectron.runCommand('workbench.action.closeQuickOpen'); await this.waitForQuickOpenClosed(); } - async type(text: string): Promise { - await this.spectron.client.type(text); - } - async openFile(fileName: string): Promise { - await this.openQuickOpen(); - await this.type(fileName); + await this.openQuickOpen(fileName); await this.waitForQuickOpenElements(names => names.some(n => n === fileName)); await this.spectron.client.keys(['Enter', 'NULL']); @@ -45,10 +39,7 @@ export class QuickOpen { } async runCommand(commandText: string): Promise { - await this.openCommandPallette(); - - // type the text - await this.type(commandText); + await this.openQuickOpen(`> ${commandText}`); // wait for best choice to be focused await this.spectron.client.waitForTextContent(QuickOpen.QUICK_OPEN_FOCUSED_ELEMENT, commandText); @@ -69,7 +60,7 @@ export class QuickOpen { } async submit(text: string): Promise { - await this.spectron.client.type(text); + await this.spectron.client.setValue(QuickOpen.QUICK_OPEN_INPUT, text); await this.spectron.client.keys(['Enter', 'NULL']); await this.waitForQuickOpenClosed(); } diff --git a/test/smoke/src/areas/search/search.test.ts b/test/smoke/src/areas/search/search.test.ts index 83adb28fd08..60972beb081 100644 --- a/test/smoke/src/areas/search/search.test.ts +++ b/test/smoke/src/areas/search/search.test.ts @@ -9,7 +9,6 @@ describe('Search', () => { let app: SpectronApplication; before(() => { app = new SpectronApplication(); return app.start('Search'); }); after(() => app.stop()); - beforeEach(function () { app.screenCapturer.testName = this.currentTest.title; }); it('searches for body & checks for correct result number', async function () { await app.workbench.search.openSearchViewlet(); @@ -19,29 +18,23 @@ describe('Search', () => { }); it('searches only for *.js files & checks for correct result number', async function () { - await app.workbench.search.openSearchViewlet(); await app.workbench.search.searchFor('body'); await app.workbench.search.showQueryDetails(); - await app.workbench.search.setFilesToIncludeTextAndSearch('*.js'); - + await app.workbench.search.setFilesToIncludeText('*.js'); await app.workbench.search.submitSearch(); await app.workbench.search.waitForResultText('4 results in 1 file'); - await app.workbench.search.setFilesToIncludeTextAndSearch(''); + await app.workbench.search.setFilesToIncludeText(''); await app.workbench.search.hideQueryDetails(); }); it('dismisses result & checks for correct result number', async function () { - await app.workbench.search.openSearchViewlet(); await app.workbench.search.searchFor('body'); - await app.workbench.search.removeFileMatch(1); - await app.workbench.search.waitForResultText('3 results in 3 files'); }); it('replaces first search result with a replace term', async function () { - await app.workbench.search.openSearchViewlet(); await app.workbench.search.searchFor('body'); await app.workbench.search.setReplaceText('ydob'); diff --git a/test/smoke/src/areas/search/search.ts b/test/smoke/src/areas/search/search.ts index 3d3642ea37a..4267da79f47 100644 --- a/test/smoke/src/areas/search/search.ts +++ b/test/smoke/src/areas/search/search.ts @@ -6,92 +6,79 @@ import { SpectronApplication } from '../../spectron/application'; import { Viewlet } from '../workbench/viewlet'; -export class Search extends Viewlet { +const VIEWLET = 'div[id="workbench.view.search"] .search-viewlet'; +const INPUT = `${VIEWLET} .search-widget .search-container .monaco-inputbox input`; +const INCLUDE_INPUT = `${VIEWLET} .query-details .monaco-inputbox input[aria-label="Search Include Patterns"]`; - static SEARCH_VIEWLET_XPATH = 'div[id="workbench.view.search"] .search-viewlet'; +export class Search extends Viewlet { constructor(spectron: SpectronApplication) { super(spectron); } - public async openSearchViewlet(): Promise { - if (!await this.isSearchViewletFocused()) { - await this.spectron.command('workbench.view.search'); - await this.spectron.client.waitForElement(`${Search.SEARCH_VIEWLET_XPATH} .search-widget .search-container .monaco-inputbox.synthetic-focus input`); - } + async openSearchViewlet(): Promise { + await this.spectron.runCommand('workbench.view.search'); + await this.spectron.client.waitForActiveElement(INPUT); } - public async isSearchViewletFocused(): Promise { - const element = await this.spectron.client.element(`${Search.SEARCH_VIEWLET_XPATH} .search-widget .search-container .monaco-inputbox.synthetic-focus input`); - return !!element; - } - - public async searchFor(text: string): Promise { - const searchBoxSelector = `${Search.SEARCH_VIEWLET_XPATH} .search-widget .search-container .monaco-inputbox input`; - - await this.spectron.client.clearElement(searchBoxSelector); - await this.spectron.client.click(searchBoxSelector); - await this.spectron.client.element(`${Search.SEARCH_VIEWLET_XPATH} .search-widget .search-container .monaco-inputbox.synthetic-focus input`); - - await this.spectron.client.keys(text); - + async searchFor(text: string): Promise { + await this.spectron.client.click(INPUT); + await this.spectron.client.waitForActiveElement(INPUT); + await this.spectron.client.setValue(INPUT, text); await this.submitSearch(); } - public async submitSearch(): Promise { - await this.spectron.client.click(`${Search.SEARCH_VIEWLET_XPATH} .search-widget .search-container .monaco-inputbox input`); - await this.spectron.client.element(`${Search.SEARCH_VIEWLET_XPATH} .search-widget .search-container .monaco-inputbox.synthetic-focus input`); - await this.spectron.client.keys(['NULL', 'Enter', 'NULL'], false); - await this.spectron.client.element(`${Search.SEARCH_VIEWLET_XPATH} .messages[aria-hidden="false"]`); + async submitSearch(): Promise { + await this.spectron.client.click(INPUT); + await this.spectron.client.waitForActiveElement(INPUT); + + await this.spectron.client.keys(['Enter', 'NULL']); + await this.spectron.client.element(`${VIEWLET} .messages[aria-hidden="false"]`); } - public async setFilesToIncludeTextAndSearch(text: string): Promise { - await this.spectron.client.click(`${Search.SEARCH_VIEWLET_XPATH} .query-details .monaco-inputbox input[aria-label="Search Include Patterns"]`); - await this.spectron.client.element(`${Search.SEARCH_VIEWLET_XPATH} .query-details .monaco-inputbox.synthetic-focus input[aria-label="Search Include Patterns"]`); - await this.spectron.client.clearElement(`${Search.SEARCH_VIEWLET_XPATH} .query-details .monaco-inputbox.synthetic-focus input[aria-label="Search Include Patterns"]`); - - if (text) { - await this.spectron.client.keys(text); - } + async setFilesToIncludeText(text: string): Promise { + await this.spectron.client.click(INCLUDE_INPUT); + await this.spectron.client.waitForActiveElement(INCLUDE_INPUT); + await this.spectron.client.setValue(INCLUDE_INPUT, text || ''); } - public async showQueryDetails(): Promise { + async showQueryDetails(): Promise { if (!await this.areDetailsVisible()) { - await this.spectron.client.waitAndClick(`${Search.SEARCH_VIEWLET_XPATH} .query-details .more`); + await this.spectron.client.waitAndClick(`${VIEWLET} .query-details .more`); } } - public async hideQueryDetails(): Promise { + async hideQueryDetails(): Promise { if (await this.areDetailsVisible()) { - await this.spectron.client.waitAndClick(`${Search.SEARCH_VIEWLET_XPATH} .query-details.more .more`); + await this.spectron.client.waitAndClick(`${VIEWLET} .query-details.more .more`); } } - public async areDetailsVisible(): Promise { - const element = await this.spectron.client.element(`${Search.SEARCH_VIEWLET_XPATH} .query-details.more`); + async areDetailsVisible(): Promise { + const element = await this.spectron.client.element(`${VIEWLET} .query-details.more`); return !!element; } - public async removeFileMatch(index: number): Promise { - await this.spectron.client.waitAndmoveToObject(`${Search.SEARCH_VIEWLET_XPATH} .results .monaco-tree-rows>:nth-child(${index}) .filematch`); - const file = await this.spectron.client.waitForText(`${Search.SEARCH_VIEWLET_XPATH} .results .monaco-tree-rows>:nth-child(${index}) .filematch a.label-name`); - await this.spectron.client.click(`${Search.SEARCH_VIEWLET_XPATH} .results .monaco-tree-rows>:nth-child(${index}) .filematch .action-label.icon.action-remove`); - await this.spectron.client.waitForText(`${Search.SEARCH_VIEWLET_XPATH} .results .monaco-tree-rows>:nth-child(${index}) .filematch a.label-name`, void 0, result => result !== file); + async removeFileMatch(index: number): Promise { + await this.spectron.client.waitAndMoveToObject(`${VIEWLET} .results .monaco-tree-rows>:nth-child(${index}) .filematch`); + const file = await this.spectron.client.waitForText(`${VIEWLET} .results .monaco-tree-rows>:nth-child(${index}) .filematch a.label-name`); + await this.spectron.client.waitAndClick(`${VIEWLET} .results .monaco-tree-rows>:nth-child(${index}) .filematch .action-label.icon.action-remove`); + await this.spectron.client.waitForText(`${VIEWLET} .results .monaco-tree-rows>:nth-child(${index}) .filematch a.label-name`, void 0, result => result !== file); } - public async setReplaceText(text: string): Promise { - await this.spectron.client.waitAndClick(`${Search.SEARCH_VIEWLET_XPATH} .search-widget .monaco-button.toggle-replace-button.collapse`); - await this.spectron.client.waitAndClick(`${Search.SEARCH_VIEWLET_XPATH} .search-widget .replace-container .monaco-inputbox input[title="Replace"]`); - await this.spectron.client.element(`${Search.SEARCH_VIEWLET_XPATH} .search-widget .replace-container .monaco-inputbox.synthetic-focus input[title="Replace"]`); - await this.spectron.client.setValue(`${Search.SEARCH_VIEWLET_XPATH} .search-widget .replace-container .monaco-inputbox.synthetic-focus input[title="Replace"]`, text); + async setReplaceText(text: string): Promise { + await this.spectron.client.waitAndClick(`${VIEWLET} .search-widget .monaco-button.toggle-replace-button.collapse`); + await this.spectron.client.waitAndClick(`${VIEWLET} .search-widget .replace-container .monaco-inputbox input[title="Replace"]`); + await this.spectron.client.element(`${VIEWLET} .search-widget .replace-container .monaco-inputbox.synthetic-focus input[title="Replace"]`); + await this.spectron.client.setValue(`${VIEWLET} .search-widget .replace-container .monaco-inputbox.synthetic-focus input[title="Replace"]`, text); } - public async replaceFileMatch(index: number): Promise { - await this.spectron.client.waitAndmoveToObject(`${Search.SEARCH_VIEWLET_XPATH} .results .monaco-tree-rows>:nth-child(${index}) .filematch`); - await this.spectron.client.click(`${Search.SEARCH_VIEWLET_XPATH} .results .monaco-tree-rows>:nth-child(${index}) .filematch .action-label.icon.action-replace-all`); + async replaceFileMatch(index: number): Promise { + await this.spectron.client.waitAndMoveToObject(`${VIEWLET} .results .monaco-tree-rows>:nth-child(${index}) .filematch`); + await this.spectron.client.click(`${VIEWLET} .results .monaco-tree-rows>:nth-child(${index}) .filematch .action-label.icon.action-replace-all`); } - public async waitForResultText(text: string): Promise { - await this.spectron.client.waitForText(`${Search.SEARCH_VIEWLET_XPATH} .messages[aria-hidden="false"] .message>p`, text); + async waitForResultText(text: string): Promise { + await this.spectron.client.waitForText(`${VIEWLET} .messages[aria-hidden="false"] .message>p`, text); } } \ No newline at end of file diff --git a/test/smoke/src/areas/statusbar/statusbar.test.ts b/test/smoke/src/areas/statusbar/statusbar.test.ts index 0d141465514..9ea1798e130 100644 --- a/test/smoke/src/areas/statusbar/statusbar.test.ts +++ b/test/smoke/src/areas/statusbar/statusbar.test.ts @@ -13,7 +13,6 @@ describe('Statusbar', () => { let app: SpectronApplication = new SpectronApplication(); before(() => app.start('Statusbar')); after(() => app.stop()); - beforeEach(function () { app.screenCapturer.testName = this.currentTest.title; }); it('verifies presence of all default status bar elements', async function () { await app.workbench.statusbar.waitForStatusbarElement(StatusBarElement.BRANCH_STATUS); @@ -69,7 +68,7 @@ describe('Statusbar', () => { await app.workbench.quickopen.waitForQuickOpenOpened(); - await app.workbench.quickopen.submit('15'); + await app.workbench.quickopen.submit(':15'); await app.workbench.editor.waitForHighlightingLine(15); }); diff --git a/test/smoke/src/areas/terminal/terminal.test.ts b/test/smoke/src/areas/terminal/terminal.test.ts index 70fa2f3f4b3..de81c0dbed2 100644 --- a/test/smoke/src/areas/terminal/terminal.test.ts +++ b/test/smoke/src/areas/terminal/terminal.test.ts @@ -9,7 +9,6 @@ describe('Terminal', () => { let app: SpectronApplication; before(() => { app = new SpectronApplication(); return app.start('Terminal'); }); after(() => app.stop()); - beforeEach(function () { app.screenCapturer.testName = this.currentTest.title; }); it(`opens terminal, runs 'echo' and verifies the output`, async function () { const expected = new Date().getTime().toString(); diff --git a/test/smoke/src/areas/terminal/terminal.ts b/test/smoke/src/areas/terminal/terminal.ts index eff9e3a2594..951497edccf 100644 --- a/test/smoke/src/areas/terminal/terminal.ts +++ b/test/smoke/src/areas/terminal/terminal.ts @@ -9,10 +9,10 @@ const PANEL_SELECTOR = 'div[id="workbench.panel.terminal"]'; const XTERM_SELECTOR = `${PANEL_SELECTOR} .terminal-wrapper`; export class Terminal { - constructor(private spectron: SpectronApplication) { - } - public async showTerminal(): Promise { + constructor(private spectron: SpectronApplication) { } + + async showTerminal(): Promise { if (!await this.isVisible()) { await this.spectron.workbench.quickopen.runCommand('View: Toggle Integrated Terminal'); await this.spectron.client.waitForElement(XTERM_SELECTOR); @@ -20,17 +20,18 @@ export class Terminal { } } - public async isVisible(): Promise { + async isVisible(): Promise { const element = await this.spectron.client.element(PANEL_SELECTOR); return !!element; } - public async runCommand(commandText: string): Promise { - await this.spectron.client.type(commandText); + async runCommand(commandText: string): Promise { + // TODO@Tyriar fix this. we should not use type but setValue + // await this.spectron.client.type(commandText); await this.spectron.client.keys(['Enter', 'NULL']); } - public async waitForTerminalText(fn: (text: string[]) => boolean, timeOutDescription: string = 'Getting Terminal Text'): Promise { + async waitForTerminalText(fn: (text: string[]) => boolean, timeOutDescription: string = 'Getting Terminal Text'): Promise { return this.spectron.client.waitFor(async () => { const terminalText = await this.getTerminalText(); if (fn(terminalText)) { @@ -40,7 +41,7 @@ export class Terminal { }, void 0, timeOutDescription); } - public getCurrentLineNumber(): Promise { + getCurrentLineNumber(): Promise { return this.getTerminalText().then(text => text.length); } diff --git a/test/smoke/src/areas/workbench/data-loss.test.ts b/test/smoke/src/areas/workbench/data-loss.test.ts index d270deaeef7..68d7dd1b377 100644 --- a/test/smoke/src/areas/workbench/data-loss.test.ts +++ b/test/smoke/src/areas/workbench/data-loss.test.ts @@ -9,7 +9,6 @@ describe('Dataloss', () => { let app: SpectronApplication; before(() => { app = new SpectronApplication(); return app.start('Dataloss'); }); after(() => app.stop()); - beforeEach(function () { app.screenCapturer.testName = this.currentTest.title; }); it(`verifies that 'hot exit' works for dirty files`, async function () { await app.workbench.newUntitledFile(); diff --git a/test/smoke/src/areas/workbench/data-migration.test.ts b/test/smoke/src/areas/workbench/data-migration.test.ts index 8bb19bbd53d..d52859bc1ae 100644 --- a/test/smoke/src/areas/workbench/data-migration.test.ts +++ b/test/smoke/src/areas/workbench/data-migration.test.ts @@ -23,7 +23,6 @@ describe('Data Migration', () => { // Setting up stable version let app = new SpectronApplication(STABLE_PATH); await app.start('Data Migration'); - app.screenCapturer.testName = 'Untitled is restorted'; await app.workbench.newUntitledFile(); await app.workbench.editor.waitForTypeInEditor('Untitled-1', textToType); @@ -34,7 +33,6 @@ describe('Data Migration', () => { app = new SpectronApplication(LATEST_PATH); await app.start('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.`); @@ -50,12 +48,10 @@ describe('Data Migration', () => { let app = new SpectronApplication(STABLE_PATH, fileName); await Util.removeFile(`${fileName}`); await app.start('Data Migration'); - app.screenCapturer.testName = 'Newly created dirty file is restorted'; - await app.workbench.waitForActiveTab(fileName); - await app.client.type(firstTextPart); + await app.workbench.editor.waitForTypeInEditor('plainFile', firstTextPart); await app.workbench.saveOpenedFile(); - await app.client.type(secondTextPart); + await app.workbench.editor.waitForTypeInEditor('plainFile', secondTextPart); await app.stop(); await new Promise(c => setTimeout(c, 1000)); // wait until all resources are released (e.g. locked local storage) @@ -63,7 +59,6 @@ describe('Data Migration', () => { // Checking latest version for the restored state app = new SpectronApplication(LATEST_PATH); await app.start('Data Migration'); - app.screenCapturer.testName = 'Newly created dirty file is restorted'; const filename = fileName.split('/')[1]; assert.ok(await app.workbench.waitForActiveTab(filename), `Untitled-1 tab is not present after migration.`); @@ -76,7 +71,6 @@ describe('Data Migration', () => { const fileName1 = 'app.js', fileName2 = 'jsconfig.json', fileName3 = 'readme.md'; let app = new SpectronApplication(STABLE_PATH); await app.start('Data Migration'); - app.screenCapturer.testName = 'Opened tabs are restored'; await app.workbench.quickopen.openFile(fileName1); await app.workbench.quickopen.openFile(fileName2); @@ -85,7 +79,6 @@ describe('Data Migration', () => { app = new SpectronApplication(LATEST_PATH); await app.start('Data Migration'); - app.screenCapturer.testName = 'Opened tabs are restored'; assert.ok(await app.workbench.waitForTab(fileName1), `${fileName1} tab was not restored after migration.`); assert.ok(await app.workbench.waitForTab(fileName2), `${fileName2} tab was not restored after migration.`); diff --git a/test/smoke/src/areas/workbench/localization.test.ts b/test/smoke/src/areas/workbench/localization.test.ts index ebed884c5ed..059eb5415de 100644 --- a/test/smoke/src/areas/workbench/localization.test.ts +++ b/test/smoke/src/areas/workbench/localization.test.ts @@ -17,7 +17,6 @@ describe('Localization', () => { it(`starts with 'DE' locale and verifies title and viewlets text is in German`, async function () { await app.start('Localization', ['--locale=DE']); - app.screenCapturer.testName = 'DE locale test'; let text = await app.workbench.explorer.getOpenEditorsViewTitle(); await app.screenCapturer.capture('Open editors title'); diff --git a/test/smoke/src/areas/workbench/workbench.ts b/test/smoke/src/areas/workbench/workbench.ts index 9bf860c3584..08a3204d1eb 100644 --- a/test/smoke/src/areas/workbench/workbench.ts +++ b/test/smoke/src/areas/workbench/workbench.ts @@ -57,7 +57,7 @@ export class Workbench { // ignore if there is no dirty file return Promise.resolve(); } - await this.spectron.command('workbench.action.files.save'); + await this.spectron.runCommand('workbench.action.files.save'); return this.spectron.client.waitForElement('.tabs-container div.tab.active.dirty', element => !element); } @@ -71,8 +71,8 @@ export class Workbench { await this.editor.waitForActiveEditor(fileName); } - public async waitForActiveTab(fileName: string, isDirty: boolean = false): Promise { - return this.spectron.client.waitForElement(`.tabs-container div.tab.active${isDirty ? '.dirty' : ''}[aria-selected="true"][aria-label="${fileName}, tab"]`).then(() => true); + public async waitForActiveTab(fileName: string, isDirty: boolean = false): Promise { + return this.spectron.client.waitForElement(`.tabs-container div.tab.active${isDirty ? '.dirty' : ''}[aria-selected="true"][aria-label="${fileName}, tab"]`); } public async waitForTab(fileName: string, isDirty: boolean = false): Promise { @@ -80,7 +80,7 @@ export class Workbench { } public async newUntitledFile(): Promise { - await this.spectron.command('workbench.action.files.newUntitledFile'); + await this.spectron.runCommand('workbench.action.files.newUntitledFile'); await this.waitForEditorFocus('Untitled-1', true); } } diff --git a/test/smoke/src/helpers/screenshot.ts b/test/smoke/src/helpers/screenshot.ts index 289e600b728..4a398321c82 100644 --- a/test/smoke/src/helpers/screenshot.ts +++ b/test/smoke/src/helpers/screenshot.ts @@ -5,35 +5,29 @@ import * as path from 'path'; import * as fs from 'fs'; -import * as mkdirp from 'mkdirp'; import { Application } from 'spectron'; -import { SCREENSHOTS_DIR } from '../spectron/application'; - -function sanitize(name: string): string { - return name.replace(/[&*:\/]/g, ''); -} +import { sanitize } from './utilities'; export class ScreenCapturer { private static counter = 0; - testName: string = 'default'; - constructor(private application: Application, private suiteName: string) { } + constructor( + private application: Application, + private screenshotsDirPath: string | undefined + ) { } async capture(name: string): Promise { - if (!SCREENSHOTS_DIR) { + if (!this.screenshotsDirPath) { return; } const screenshotPath = path.join( - SCREENSHOTS_DIR, - sanitize(this.suiteName), - sanitize(this.testName), + this.screenshotsDirPath, `${ScreenCapturer.counter++}-${sanitize(name)}.png` ); const image = await this.application.browserWindow.capturePage(); - await new Promise((c, e) => mkdirp(path.dirname(screenshotPath), err => err ? e(err) : c())); await new Promise((c, e) => fs.writeFile(screenshotPath, image, err => err ? e(err) : c())); } } diff --git a/test/smoke/src/helpers/utilities.ts b/test/smoke/src/helpers/utilities.ts index 2ba4141ee3e..bde4dde38b7 100644 --- a/test/smoke/src/helpers/utilities.ts +++ b/test/smoke/src/helpers/utilities.ts @@ -75,4 +75,8 @@ export async function mkdirp(path: string, mode?: number): Promise { } return true; +} + +export function sanitize(name: string): string { + return name.replace(/[&*:\/]/g, ''); } \ No newline at end of file diff --git a/test/smoke/src/main.ts b/test/smoke/src/main.ts index 31dbaf71149..d78cc255a2c 100644 --- a/test/smoke/src/main.ts +++ b/test/smoke/src/main.ts @@ -17,9 +17,16 @@ const testDataPath = tmpDir.name; process.once('exit', () => rimraf.sync(testDataPath)); const [, , ...args] = process.argv; -const opts = minimist(args, { string: ['build', 'stable-build', 'screenshots', 'wait-time'] }); +const opts = minimist(args, { + string: [ + 'build', + 'stable-build', + 'log', + 'wait-time' + ] +}); -opts.screenshots = opts.screenshots === '' ? path.join(testDataPath, 'screenshots') : opts.screenshots; +process.env.ARTIFACTS_DIR = opts.log || ''; const workspacePath = path.join(testDataPath, 'smoketest.code-workspace'); const testRepoUrl = 'https://github.com/Microsoft/vscode-smoketest-express'; @@ -98,7 +105,6 @@ process.env.VSCODE_EXTENSIONS_DIR = extensionsPath; process.env.SMOKETEST_REPO = testRepoLocalDir; process.env.VSCODE_WORKSPACE_PATH = workspacePath; process.env.VSCODE_KEYBINDINGS_PATH = keybindingsPath; -process.env.SCREENSHOTS_DIR = opts.screenshots || ''; process.env.WAIT_TIME = opts['wait-time'] || '20'; if (process.env.VSCODE_DEV === '1') { diff --git a/test/smoke/src/spectron/application.ts b/test/smoke/src/spectron/application.ts index 77ef6fd4207..6b88aed84e8 100644 --- a/test/smoke/src/spectron/application.ts +++ b/test/smoke/src/spectron/application.ts @@ -11,6 +11,8 @@ import { Workbench } from '../areas/workbench/workbench'; import * as fs from 'fs'; import * as cp from 'child_process'; import * as path from 'path'; +import * as mkdirp from 'mkdirp'; +import { sanitize } from '../helpers/utilities'; export const LATEST_PATH = process.env.VSCODE_PATH as string; export const STABLE_PATH = process.env.VSCODE_STABLE_PATH || ''; @@ -19,7 +21,7 @@ export const CODE_WORKSPACE_PATH = process.env.VSCODE_WORKSPACE_PATH as string; export const USER_DIR = process.env.VSCODE_USER_DIR as string; export const EXTENSIONS_DIR = process.env.VSCODE_EXTENSIONS_DIR as string; export const VSCODE_EDITION = process.env.VSCODE_EDITION as string; -export const SCREENSHOTS_DIR = process.env.SCREENSHOTS_DIR as string; +export const ARTIFACTS_DIR = process.env.ARTIFACTS_DIR as string; export const WAIT_TIME = parseInt(process.env.WAIT_TIME as string); export enum VSCODE_BUILD { @@ -52,12 +54,15 @@ export class SpectronApplication { private _workbench: Workbench; private _screenCapturer: ScreenCapturer; private spectron: Application; - private keybindings: any[]; + private keybindings: any[]; private stopLogCollection: (() => Promise) | undefined; - constructor(private _electronPath: string = LATEST_PATH, private _workspace: string = WORKSPACE_PATH, private _userDir: string = USER_DIR) { - } + constructor( + private _electronPath: string = LATEST_PATH, + private _workspace: string = WORKSPACE_PATH, + private _userDir: string = USER_DIR + ) { } - public get build(): VSCODE_BUILD { + get build(): VSCODE_BUILD { switch (VSCODE_EDITION) { case 'dev': return VSCODE_BUILD.DEV; @@ -67,27 +72,27 @@ export class SpectronApplication { return VSCODE_BUILD.STABLE; } - public get app(): Application { + get app(): Application { return this.spectron; } - public get client(): SpectronClient { + get client(): SpectronClient { return this._client; } - public get webclient(): WebClient { + get webclient(): WebClient { return this.spectron.client; } - public get screenCapturer(): ScreenCapturer { + get screenCapturer(): ScreenCapturer { return this._screenCapturer; } - public get workbench(): Workbench { + get workbench(): Workbench { return this._workbench; } - public async start(testSuiteName: string, codeArgs: string[] = [], env = process.env): Promise { + async start(testSuiteName: string, codeArgs: string[] = [], env = process.env): Promise { await this.retrieveKeybindings(); cp.execSync('git checkout .', { cwd: WORKSPACE_PATH }); await this.startApplication(testSuiteName, codeArgs, env); @@ -96,17 +101,22 @@ export class SpectronApplication { await this.screenCapturer.capture('Application started'); } - public async reload(): Promise { + async reload(): Promise { await this.workbench.quickopen.runCommand('Reload Window'); // TODO @sandy: Find a proper condition to wait for reload await new Promise(c => setTimeout(c, 500)); await this.checkWindowReady(); } - public async stop(): Promise { + async stop(): Promise { + if (this.stopLogCollection) { + await this.stopLogCollection(); + this.stopLogCollection = undefined; + } + if (this.spectron && this.spectron.isRunning()) { await this.screenCapturer.capture('Stopping application'); - return await this.spectron.stop(); + await this.spectron.stop(); } } @@ -127,23 +137,26 @@ export class SpectronApplication { // Prevent Quick Open from closing when focus is stolen, this allows concurrent smoketest suite running args.push('--sticky-quickopen'); - // Disable telemetry for smoke tests + // Disable telemetry args.push('--disable-telemetry'); + // Disable updates + args.push('--disable-updates'); + // Ensure that running over custom extensions directory, rather than picking up the one that was used by a tester previously args.push(`--extensions-dir=${EXTENSIONS_DIR}`); args.push(...codeArgs); - chromeDriverArgs.push(`--user-data-dir=${path.join(this._userDir, String(SpectronApplication.count++))}`); + const id = String(SpectronApplication.count++); + chromeDriverArgs.push(`--user-data-dir=${path.join(this._userDir, id)}`); // Spectron always uses the same port number for the chrome driver // and it handles gracefully when two instances use the same port number // This works, but when one of the instances quits, it takes down // chrome driver with it, leaving the other instance in DISPAIR!!! :( const port = await findFreePort(); - - this.spectron = new Application({ + const opts: any = { path: this._electronPath, port, args, @@ -151,10 +164,66 @@ export class SpectronApplication { chromeDriverArgs, startTimeout: 10000, requireName: 'nodeRequire' - }); + }; + + let testsuiteRootPath: string | undefined = undefined; + let screenshotsDirPath: string | undefined = undefined; + + if (ARTIFACTS_DIR) { + testsuiteRootPath = path.join(ARTIFACTS_DIR, sanitize(testSuiteName)); + mkdirp.sync(testsuiteRootPath); + + // Collect screenshots + screenshotsDirPath = path.join(testsuiteRootPath, 'screenshots'); + mkdirp.sync(screenshotsDirPath); + + // Collect chromedriver logs + const chromedriverLogPath = path.join(testsuiteRootPath, 'chromedriver.log'); + opts.chromeDriverLogPath = chromedriverLogPath; + + // Collect webdriver logs + const webdriverLogsPath = path.join(testsuiteRootPath, 'webdriver'); + mkdirp.sync(webdriverLogsPath); + opts.webdriverLogPath = webdriverLogsPath; + } + + this.spectron = new Application(opts); await this.spectron.start(); - this._screenCapturer = new ScreenCapturer(this.spectron, testSuiteName); + if (testsuiteRootPath) { + // Collect logs + const mainProcessLogPath = path.join(testsuiteRootPath, 'main.log'); + const rendererProcessLogPath = path.join(testsuiteRootPath, 'renderer.log'); + + const flush = async () => { + const mainLogs = await this.spectron.client.getMainProcessLogs(); + await new Promise((c, e) => fs.appendFile(mainProcessLogPath, mainLogs.join('\n'), { encoding: 'utf8' }, err => err ? e(err) : c())); + + const rendererLogs = (await this.spectron.client.getRenderProcessLogs()).map(m => `${m.timestamp} - ${m.level} - ${m.message}`); + await new Promise((c, e) => fs.appendFile(rendererProcessLogPath, rendererLogs.join('\n'), { encoding: 'utf8' }, err => err ? e(err) : c())); + }; + + let running = true; + const loopFlush = async () => { + while (true) { + await flush(); + + if (!running) { + return; + } + + await new Promise(c => setTimeout(c, 1000)); + } + }; + + const loopPromise = loopFlush(); + this.stopLogCollection = () => { + running = false; + return loopPromise; + }; + } + + this._screenCapturer = new ScreenCapturer(this.spectron, screenshotsDirPath); this._client = new SpectronClient(this.spectron, this); this._workbench = new Workbench(this); } @@ -193,7 +262,7 @@ export class SpectronApplication { * Retrieves the command from keybindings file and executes it with WebdriverIO client API * @param command command (e.g. 'workbench.action.files.newUntitledFile') */ - public command(command: string, capture?: boolean): Promise { + runCommand(command: string): Promise { const binding = this.keybindings.find(x => x['command'] === command); if (!binding) { return this.workbench.quickopen.runCommand(command); @@ -209,7 +278,7 @@ export class SpectronApplication { keysToPress.push('NULL'); }); - return this.client.keys(keysToPress, capture); + return this.client.keys(keysToPress); } /** diff --git a/test/smoke/src/spectron/client.ts b/test/smoke/src/spectron/client.ts index 20c4f463e2b..24857764b63 100644 --- a/test/smoke/src/spectron/client.ts +++ b/test/smoke/src/spectron/client.ts @@ -17,94 +17,95 @@ export class SpectronClient { private retryCount: number; private readonly retryDuration = 100; // in milliseconds - constructor(public spectron: Application, private application: SpectronApplication) { + constructor(readonly spectron: Application, private application: SpectronApplication) { this.retryCount = (WAIT_TIME * 1000) / this.retryDuration; } - public windowByIndex(index: number): Promise { + windowByIndex(index: number): Promise { return this.spectron.client.windowByIndex(index); } - public async keys(keys: string[] | string, capture: boolean = true): Promise { - return Promise.resolve(this.spectron.client.keys(keys)); + keys(keys: string[]): Promise { + this.spectron.client.keys(keys); + return Promise.resolve(); } - public async getText(selector: string, capture: boolean = true): Promise { + async getText(selector: string, capture: boolean = true): Promise { return this.spectron.client.getText(selector); } - public async waitForText(selector: string, text?: string, accept?: (result: string) => boolean): Promise { + async waitForText(selector: string, text?: string, accept?: (result: string) => boolean): Promise { accept = accept ? accept : result => text !== void 0 ? text === result : !!result; return this.waitFor(() => this.spectron.client.getText(selector), accept, `getText with selector ${selector}`); } - public async waitForTextContent(selector: string, textContent?: string, accept?: (result: string) => boolean): Promise { + async waitForTextContent(selector: string, textContent?: string, accept?: (result: string) => boolean): Promise { accept = accept ? accept : (result => textContent !== void 0 ? textContent === result : !!result); const fn = async () => await this.spectron.client.selectorExecute(selector, div => Array.isArray(div) ? div[0].textContent : div.textContent); return this.waitFor(fn, s => accept!(typeof s === 'string' ? s : ''), `getTextContent with selector ${selector}`); } - public async waitForValue(selector: string, value?: string, accept?: (result: string) => boolean): Promise { + async waitForValue(selector: string, value?: string, accept?: (result: string) => boolean): Promise { accept = accept ? accept : result => value !== void 0 ? value === result : !!result; return this.waitFor(() => this.spectron.client.getValue(selector), accept, `getValue with selector ${selector}`); } - public async waitForHTML(selector: string, accept: (result: string) => boolean = (result: string) => !!result): Promise { + async waitForHTML(selector: string, accept: (result: string) => boolean = (result: string) => !!result): Promise { return this.waitFor(() => this.spectron.client.getHTML(selector), accept, `getHTML with selector ${selector}`); } - public async waitAndClick(selector: string): Promise { + async waitAndClick(selector: string): Promise { return this.waitFor(() => this.spectron.client.click(selector), void 0, `click with selector ${selector}`); } - public async click(selector: string): Promise { + async click(selector: string): Promise { return this.spectron.client.click(selector); } - public async doubleClickAndWait(selector: string, capture: boolean = true): Promise { + async doubleClickAndWait(selector: string, capture: boolean = true): Promise { return this.waitFor(() => this.spectron.client.doubleClick(selector), void 0, `doubleClick with selector ${selector}`); } - public async leftClick(selector: string, xoffset: number, yoffset: number, capture: boolean = true): Promise { + async leftClick(selector: string, xoffset: number, yoffset: number, capture: boolean = true): Promise { return this.spectron.client.leftClick(selector, xoffset, yoffset); } - public async rightClick(selector: string, capture: boolean = true): Promise { + async rightClick(selector: string, capture: boolean = true): Promise { return this.spectron.client.rightClick(selector); } - public async moveToObject(selector: string, capture: boolean = true): Promise { + async moveToObject(selector: string, capture: boolean = true): Promise { return this.spectron.client.moveToObject(selector); } - public async waitAndmoveToObject(selector: string): Promise { + async waitAndMoveToObject(selector: string): Promise { return this.waitFor(() => this.spectron.client.moveToObject(selector), void 0, `move to object with selector ${selector}`); } - public async setValue(selector: string, text: string, capture: boolean = true): Promise { + async setValue(selector: string, text: string, capture: boolean = true): Promise { return this.spectron.client.setValue(selector, text); } - public async waitForElements(selector: string, accept: (result: Element[]) => boolean = result => result.length > 0): Promise { + async waitForElements(selector: string, accept: (result: Element[]) => boolean = result => result.length > 0): Promise { return this.waitFor>(() => this.spectron.client.elements(selector), result => accept(result.value), `elements with selector ${selector}`) .then(result => result.value); } - public async waitForElement(selector: string, accept: (result: Element | undefined) => boolean = result => !!result): Promise { + async waitForElement(selector: string, accept: (result: Element | undefined) => boolean = result => !!result): Promise { return this.waitFor>(() => this.spectron.client.element(selector), result => accept(result ? result.value : void 0), `element with selector ${selector}`) .then(result => result.value); } - public async waitForVisibility(selector: string, accept: (result: boolean) => boolean = result => result): Promise { + async waitForVisibility(selector: string, accept: (result: boolean) => boolean = result => result): Promise { return this.waitFor(() => this.spectron.client.isVisible(selector), accept, `isVisible with selector ${selector}`); } - public async element(selector: string): Promise { + async element(selector: string): Promise { return this.spectron.client.element(selector) .then(result => result.value); } - public async waitForActiveElement(selector: string): Promise { + async waitForActiveElement(selector: string): Promise { return this.waitFor( () => this.spectron.client.execute(s => document.activeElement.matches(s), selector), r => r.value, @@ -112,49 +113,45 @@ export class SpectronClient { ); } - public async waitForAttribute(selector: string, attribute: string, accept: (result: string) => boolean = result => !!result): Promise { + async waitForAttribute(selector: string, attribute: string, accept: (result: string) => boolean = result => !!result): Promise { return this.waitFor(() => this.spectron.client.getAttribute(selector), accept, `attribute with selector ${selector}`); } - public async dragAndDrop(sourceElem: string, destinationElem: string, capture: boolean = true): Promise { + async dragAndDrop(sourceElem: string, destinationElem: string, capture: boolean = true): Promise { return this.spectron.client.dragAndDrop(sourceElem, destinationElem); } - public async selectByValue(selector: string, value: string, capture: boolean = true): Promise { + async selectByValue(selector: string, value: string, capture: boolean = true): Promise { return this.spectron.client.selectByValue(selector, value); } - public async getValue(selector: string, capture: boolean = true): Promise { + async getValue(selector: string, capture: boolean = true): Promise { return this.spectron.client.getValue(selector); } - public async getAttribute(selector: string, attribute: string, capture: boolean = true): Promise { + async getAttribute(selector: string, attribute: string, capture: boolean = true): Promise { return Promise.resolve(this.spectron.client.getAttribute(selector, attribute)); } - public clearElement(selector: string): any { - return this.spectron.client.clearElement(selector); - } - - public buttonDown(): any { + buttonDown(): any { return this.spectron.client.buttonDown(); } - public buttonUp(): any { + buttonUp(): any { return this.spectron.client.buttonUp(); } - public async isVisible(selector: string, capture: boolean = true): Promise { + async isVisible(selector: string, capture: boolean = true): Promise { return this.spectron.client.isVisible(selector); } - public async getTitle(): Promise { + async getTitle(): Promise { return this.spectron.client.getTitle(); } private running = false; - public async waitFor(func: () => T | Promise, accept?: (result: T) => boolean | Promise, timeoutMessage?: string, retryCount?: number): Promise; - public async waitFor(func: () => T | Promise, accept: (result: T) => boolean | Promise = result => !!result, timeoutMessage?: string, retryCount?: number): Promise { + async waitFor(func: () => T | Promise, accept?: (result: T) => boolean | Promise, timeoutMessage?: string, retryCount?: number): Promise; + async waitFor(func: () => T | Promise, accept: (result: T) => boolean | Promise = result => !!result, timeoutMessage?: string, retryCount?: number): Promise { if (this.running) { throw new Error('Not allowed to run nested waitFor calls!'); } @@ -190,22 +187,22 @@ export class SpectronClient { } } - public type(text: string): Promise { - return new Promise((res) => { - let textSplit = text.split(' '); + // type(text: string): Promise { + // return new Promise((res) => { + // let textSplit = text.split(' '); - const type = async (i: number) => { - if (!textSplit[i] || textSplit[i].length <= 0) { - return res(); - } + // const type = async (i: number) => { + // if (!textSplit[i] || textSplit[i].length <= 0) { + // return res(); + // } - const toType = textSplit[i + 1] ? `${textSplit[i]} ` : textSplit[i]; - await this.keys(toType, false); - await this.keys(['NULL']); - await type(i + 1); - }; + // const toType = textSplit[i + 1] ? `${textSplit[i]} ` : textSplit[i]; + // await this.keys(toType); + // await this.keys(['NULL']); + // await type(i + 1); + // }; - return type(0); - }); - } + // return type(0); + // }); + // } } \ No newline at end of file diff --git a/tslint.json b/tslint.json index d4df6618424..1264f2b5cf0 100644 --- a/tslint.json +++ b/tslint.json @@ -348,15 +348,52 @@ ] }, { - "target": "**/vs/workbench/services/**", + "target": "**/vs/workbench/services/**/common/**", "restrictions": [ "vs/nls", "vs/css!./**/*", - "**/vs/base/**", - "**/vs/platform/**", - "**/vs/editor/**", + "**/vs/base/**/common/**", + "**/vs/platform/**/common/**", + "**/vs/editor/common/**", + "**/vs/workbench/common/**", + "**/vs/workbench/services/**/common/**" + ] + }, + { + "target": "**/vs/workbench/services/**/browser/**", + "restrictions": [ + "vs/nls", + "vs/css!./**/*", + "**/vs/base/**/{common,browser}/**", + "**/vs/platform/**/{common,browser}/**", + "**/vs/editor/{common,browser}/**", + "**/vs/workbench/{common,browser}/**", + "**/vs/workbench/services/**/{common,browser}/**" + ] + }, + { + "target": "**/vs/workbench/services/**/node/**", + "restrictions": [ + "vs/nls", + "vs/css!./**/*", + "**/vs/base/**/{common,node}/**", + "**/vs/platform/**/{common,node}/**", + "**/vs/editor/{common,node}/**", + "**/vs/workbench/{common,node}/**", + "**/vs/workbench/services/**/{common,node}/**", + "*" // node modules + ] + }, + { + "target": "**/vs/workbench/services/**/electron-browser/**", + "restrictions": [ + "vs/nls", + "vs/css!./**/*", + "**/vs/base/**/{common,browser,node,electron-browser}/**", + "**/vs/platform/**/{common,browser,node,electron-browser}/**", + "**/vs/editor/**/{common,browser,node,electron-browser}/**", "**/vs/workbench/{common,browser,node,electron-browser,api}/**", - "**/vs/workbench/services/**", + "**/vs/workbench/services/**/{common,browser,node,electron-browser}/**", "*" // node modules ] },