From d9fb87d002d4745865ac1aa3d66ba3d7973aee50 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Tue, 12 Nov 2019 15:55:21 -0800 Subject: [PATCH 001/200] CI for Monaco Editor compile and bundling. --- .github/workflows/ci.yml | 17 ++++ .gitignore | 1 + build/gulpfile.editor.js | 22 +++++ build/lib/compilation.js | 2 +- build/lib/standalone.js | 24 +++++ build/lib/standalone.ts | 31 +++++++ build/monaco/esm.core.js | 21 +++++ build/monaco/monaco.webpack.config.js | 55 +++++++++++ package.json | 3 + yarn.lock | 129 +++++++++++++++++++++++++- 10 files changed, 302 insertions(+), 3 deletions(-) create mode 100644 build/monaco/esm.core.js create mode 100644 build/monaco/monaco.webpack.config.js diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 55415410f7c..eafe38df5a1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -110,3 +110,20 @@ jobs: name: Run Unit Tests - run: ./scripts/test-integration.sh --tfs "Integration Tests" name: Run Integration Tests + + monaco: + runs-on: macos-latest + env: + CHILD_CONCURRENCY: "1" + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: actions/checkout@v1 + - uses: actions/setup-node@v1 + with: + node-version: 10 + - run: yarn --frozen-lockfile + name: Install Dependencies + - run: yarn monaco-compile-check + name: Run Monaco Editor Checks + - run: yarn editor-esm-bundle + name: Editor Distro & ESM Bundle diff --git a/.gitignore b/.gitignore index 160c42ed74b..e73dd4d9e8c 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ out-editor/ out-editor-src/ out-editor-build/ out-editor-esm/ +out-editor-esm-bundle/ out-editor-min/ out-monaco-editor-core/ out-vscode/ diff --git a/build/gulpfile.editor.js b/build/gulpfile.editor.js index 6734a820cf1..17cee5ffb9c 100644 --- a/build/gulpfile.editor.js +++ b/build/gulpfile.editor.js @@ -310,6 +310,28 @@ gulp.task('editor-distro', ) ); +const bundleEditorESMTask = task.define('editor-esm-bundle-webpack', () => { + return standalone.bundleESM(); +}); + +gulp.task('editor-esm-bundle', + task.series( + task.parallel( + util.rimraf('out-editor-src'), + util.rimraf('out-editor-build'), + util.rimraf('out-editor-esm'), + util.rimraf('out-monaco-editor-core'), + util.rimraf('out-editor'), + util.rimraf('out-editor-min') + ), + extractEditorSrcTask, + createESMSourcesAndResourcesTask, + compileEditorESMTask, + bundleEditorESMTask, + finalEditorResourcesTask + ) +); + //#region monaco type checking function createTscCompileTask(watch) { diff --git a/build/lib/compilation.js b/build/lib/compilation.js index 170e086b35b..59bf1a250f6 100644 --- a/build/lib/compilation.js +++ b/build/lib/compilation.js @@ -44,7 +44,7 @@ function createCompile(src, build, emitError) { const input = es.through(); const output = input .pipe(utf8Filter) - .pipe(bom()) + .pipe(bom()) // this is required to preserve BOM in test files that loose it otherwise .pipe(utf8Filter.restore) .pipe(tsFilter) .pipe(util.loadSourcemaps()) diff --git a/build/lib/standalone.js b/build/lib/standalone.js index 56f89544dac..0bba289427f 100644 --- a/build/lib/standalone.js +++ b/build/lib/standalone.js @@ -5,9 +5,13 @@ *--------------------------------------------------------------------------------------------*/ Object.defineProperty(exports, "__esModule", { value: true }); const ts = require("typescript"); +const es = require("event-stream"); const fs = require("fs"); const path = require("path"); const tss = require("./treeshaking"); +const gulp = require("gulp"); +const webpack = require("webpack"); +const webpackGulp = require('webpack-stream'); const REPO_ROOT = path.join(__dirname, '../../'); const SRC_DIR = path.join(REPO_ROOT, 'src'); let dirCache = {}; @@ -309,3 +313,23 @@ function transportCSS(module, enqueue, write) { return haystack.length >= needle.length && haystack.substr(0, needle.length) === needle; } } +function bundleESM() { + const result = es.through(); + const webpackConfigPath = path.join(__dirname, '../../build/monaco/monaco.webpack.config.js'); + const webpackConfig = Object.assign(Object.assign({}, require(webpackConfigPath)), { mode: 'production' }); + const webpackDone = (err, stats) => { + if (err) { + result.emit('error', err); + } + const { compilation } = stats; + if (compilation.errors.length > 0) { + result.emit('error', compilation.errors.join('\n')); + } + if (compilation.warnings.length > 0) { + result.emit('data', compilation.warnings.join('\n')); + } + }; + return webpackGulp(webpackConfig, webpack, webpackDone) + .pipe(gulp.dest('out-editor-esm-bundle')); +} +exports.bundleESM = bundleESM; diff --git a/build/lib/standalone.ts b/build/lib/standalone.ts index 07000331ad2..f84afd1033a 100644 --- a/build/lib/standalone.ts +++ b/build/lib/standalone.ts @@ -4,9 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import * as ts from 'typescript'; +import * as es from 'event-stream'; import * as fs from 'fs'; import * as path from 'path'; import * as tss from './treeshaking'; +import * as gulp from 'gulp'; +import * as webpack from 'webpack'; +const webpackGulp = require('webpack-stream'); const REPO_ROOT = path.join(__dirname, '../../'); const SRC_DIR = path.join(REPO_ROOT, 'src'); @@ -357,3 +361,30 @@ function transportCSS(module: string, enqueue: (module: string) => void, write: return haystack.length >= needle.length && haystack.substr(0, needle.length) === needle; } } + +export function bundleESM() { + const result = es.through(); + + const webpackConfigPath = path.join(__dirname, '../../build/monaco/monaco.webpack.config.js'); + + const webpackConfig = { + ...require(webpackConfigPath), + ...{ mode: 'production' } + }; + + const webpackDone = (err: any, stats: any) => { + if (err) { + result.emit('error', err); + } + const { compilation } = stats; + if (compilation.errors.length > 0) { + result.emit('error', compilation.errors.join('\n')); + } + if (compilation.warnings.length > 0) { + result.emit('data', compilation.warnings.join('\n')); + } + }; + + return webpackGulp(webpackConfig, webpack, webpackDone) + .pipe(gulp.dest('out-editor-esm-bundle')); +} diff --git a/build/monaco/esm.core.js b/build/monaco/esm.core.js new file mode 100644 index 00000000000..d0cc7e9290a --- /dev/null +++ b/build/monaco/esm.core.js @@ -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. + *--------------------------------------------------------------------------------------------*/ + +// Entry file for webpack bunlding. + +import * as monaco from 'monaco-editor-core'; + +self.MonacoEnvironment = { + getWorkerUrl: function (moduleId, label) { + return './editor.worker.bundle.js'; + } +} + +monaco.editor.create(document.getElementById('container'), { + value: [ + 'var hello = "hello world";' + ].join('\n'), + language: 'javascript' +}); diff --git a/build/monaco/monaco.webpack.config.js b/build/monaco/monaco.webpack.config.js new file mode 100644 index 00000000000..fe2067648d4 --- /dev/null +++ b/build/monaco/monaco.webpack.config.js @@ -0,0 +1,55 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +const path = require('path'); + +module.exports = { + mode: 'production', + entry: { + "core": './build/monaco/esm.core.js', + "editor.worker": './out-monaco-editor-core/esm/vs/editor/editor.worker.js' + }, + output: { + globalObject: 'self', + filename: '[name].bundle.js', + path: path.resolve(__dirname, 'dist') + }, + module: { + rules: [ + { + test: /\.css$/, + use: ['style-loader', 'css-loader'], + }, + { + test: /\.(woff(2)?|ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/, + use: [ + { + loader: 'file-loader', + options: { + name: '[name].[ext]', + outputPath: 'fonts/' + } + } + ] + } + ] + }, + resolve: { + alias: { + 'monaco-editor-core': path.resolve(__dirname, '../../out-monaco-editor-core/esm/vs/editor/editor.main.js'), + } + }, + stats: { + all: false, + modules: true, + maxModules: 0, + errors: true, + warnings: true, + // our additional options + moduleTrace: true, + errorDetails: true, + chunks: true + } +}; diff --git a/package.json b/package.json index 51c1dc12a7a..b4b00f46176 100644 --- a/package.json +++ b/package.json @@ -83,11 +83,13 @@ "copy-webpack-plugin": "^4.5.2", "coveralls": "^2.11.11", "cson-parser": "^1.3.3", + "css-loader": "^3.2.0", "debounce": "^1.0.0", "event-stream": "3.3.4", "express": "^4.13.1", "fancy-log": "^1.3.3", "fast-plist": "0.1.2", + "file-loader": "^4.2.0", "glob": "^5.0.13", "gulp": "^4.0.0", "gulp-atom-electron": "^1.22.0", @@ -134,6 +136,7 @@ "rimraf": "^2.2.8", "sinon": "^1.17.2", "source-map": "^0.4.4", + "style-loader": "^1.0.0", "ts-loader": "^4.4.2", "tslint": "^5.16.0", "typescript": "3.7.2", diff --git a/yarn.lock b/yarn.lock index 3f00734e764..e6832d476cf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -443,6 +443,11 @@ ajv-keywords@^3.1.0: resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.2.0.tgz#e86b819c602cf8821ad637413698f1dec021847a" integrity sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo= +ajv-keywords@^3.4.1: + version "3.4.1" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.4.1.tgz#ef916e271c64ac12171fd8384eaae6b2345854da" + integrity sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ== + ajv@^5.1.0: version "5.3.0" resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.3.0.tgz#4414ff74a50879c208ee5fdc826e32c303549eda" @@ -473,6 +478,16 @@ ajv@^6.1.0: json-schema-traverse "^0.4.1" uri-js "^4.2.1" +ajv@^6.10.2: + version "6.10.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52" + integrity sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw== + dependencies: + fast-deep-equal "^2.0.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + ajv@^6.5.3, ajv@^6.6.1: version "6.8.1" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.8.1.tgz#0890b93742985ebf8973cd365c5b23920ce3cb20" @@ -1330,7 +1345,7 @@ camelcase@^3.0.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" integrity sha1-MvxLn82vhF/N9+c7uXysImHwqwo= -camelcase@^5.0.0: +camelcase@^5.0.0, camelcase@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== @@ -2017,6 +2032,24 @@ css-color-names@0.0.4: resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0" integrity sha1-gIrcLnnPhHOAabZGyyDsJ762KeA= +css-loader@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-3.2.0.tgz#bb570d89c194f763627fcf1f80059c6832d009b2" + integrity sha512-QTF3Ud5H7DaZotgdcJjGMvyDj5F3Pn1j/sC6VBEOVp94cbwqyIBdcs/quzj4MC1BKQSrTpQznegH/5giYbhnCQ== + dependencies: + camelcase "^5.3.1" + cssesc "^3.0.0" + icss-utils "^4.1.1" + loader-utils "^1.2.3" + normalize-path "^3.0.0" + postcss "^7.0.17" + postcss-modules-extract-imports "^2.0.0" + postcss-modules-local-by-default "^3.0.2" + postcss-modules-scope "^2.1.0" + postcss-modules-values "^3.0.0" + postcss-value-parser "^4.0.0" + schema-utils "^2.0.0" + css-select@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" @@ -2032,6 +2065,11 @@ css-what@2.1: resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.0.tgz#9467d032c38cfaefb9f2d79501253062f87fa1bd" integrity sha1-lGfQMsOM+u+58teVASUwYvh/ob0= +cssesc@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" + integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== + cssnano@^3.0.0: version "3.10.0" resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-3.10.0.tgz#4f38f6cea2b9b17fa01490f23f1dc68ea65c1c38" @@ -2995,6 +3033,14 @@ file-entry-cache@^2.0.0: flat-cache "^1.2.1" object-assign "^4.0.1" +file-loader@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-4.2.0.tgz#5fb124d2369d7075d70a9a5abecd12e60a95215e" + integrity sha512-+xZnaK5R8kBJrHK0/6HRlrKNamvVS5rjyuju+rnyxRGuwUJwpAMsVzUl5dz6rK8brkzjV6JpcFNjp6NqV0g1OQ== + dependencies: + loader-utils "^1.2.3" + schema-utils "^2.0.0" + file-uri-to-path@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" @@ -4173,6 +4219,13 @@ iconv-lite@^0.4.4: dependencies: safer-buffer ">= 2.1.2 < 3" +icss-utils@^4.0.0, icss-utils@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-4.1.1.tgz#21170b53789ee27447c2f47dd683081403f9a467" + integrity sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA== + dependencies: + postcss "^7.0.14" + ieee754@^1.1.11, ieee754@^1.1.4: version "1.1.12" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.12.tgz#50bf24e5b9c8bb98af4964c941cdb0918da7b60b" @@ -5085,7 +5138,7 @@ loader-runner@^2.3.0: resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.3.0.tgz#f482aea82d543e07921700d5a46ef26fdac6b8a2" integrity sha1-9IKuqC1UPgeSFwDVpG7yb9rGuKI= -loader-utils@1.2.3: +loader-utils@1.2.3, loader-utils@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7" integrity sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA== @@ -6668,6 +6721,39 @@ postcss-minify-selectors@^2.0.4: postcss "^5.0.14" postcss-selector-parser "^2.0.0" +postcss-modules-extract-imports@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz#818719a1ae1da325f9832446b01136eeb493cd7e" + integrity sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ== + dependencies: + postcss "^7.0.5" + +postcss-modules-local-by-default@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.2.tgz#e8a6561be914aaf3c052876377524ca90dbb7915" + integrity sha512-jM/V8eqM4oJ/22j0gx4jrp63GSvDH6v86OqyTHHUvk4/k1vceipZsaymiZ5PvocqZOl5SFHiFJqjs3la0wnfIQ== + dependencies: + icss-utils "^4.1.1" + postcss "^7.0.16" + postcss-selector-parser "^6.0.2" + postcss-value-parser "^4.0.0" + +postcss-modules-scope@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-2.1.0.tgz#ad3f5bf7856114f6fcab901b0502e2a2bc39d4eb" + integrity sha512-91Rjps0JnmtUB0cujlc8KIKCsJXWjzuxGeT/+Q2i2HXKZ7nBUeF9YQTZZTNvHVoNYj1AthsjnGLtqDUE0Op79A== + dependencies: + postcss "^7.0.6" + postcss-selector-parser "^6.0.0" + +postcss-modules-values@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz#5b5000d6ebae29b4255301b4a3a54574423e7f10" + integrity sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg== + dependencies: + icss-utils "^4.0.0" + postcss "^7.0.6" + postcss-normalize-charset@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz#ef9ee71212d7fe759c78ed162f61ed62b5cb93f1" @@ -6726,6 +6812,15 @@ postcss-selector-parser@^2.0.0, postcss-selector-parser@^2.2.2: indexes-of "^1.0.1" uniq "^1.0.1" +postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz#934cf799d016c83411859e09dcecade01286ec5c" + integrity sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg== + dependencies: + cssesc "^3.0.0" + indexes-of "^1.0.1" + uniq "^1.0.1" + postcss-svgo@^2.1.1: version "2.1.6" resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-2.1.6.tgz#b6df18aa613b666e133f08adb5219c2684ac108d" @@ -6750,6 +6845,11 @@ postcss-value-parser@^3.0.1, postcss-value-parser@^3.0.2, postcss-value-parser@^ resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz#87f38f9f18f774a4ab4c8a232f5c5ce8872a9d15" integrity sha1-h/OPnxj3dKSrTIojL1xc6IcqnRU= +postcss-value-parser@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.0.2.tgz#482282c09a42706d1fc9a069b73f44ec08391dc9" + integrity sha512-LmeoohTpp/K4UiyQCwuGWlONxXamGzCMtFxLq4W1nZVGIQLYvMCJx3yAF9qyyuFpflABI9yVdtJAqbihOsCsJQ== + postcss-zindex@^2.0.1: version "2.2.0" resolved "https://registry.yarnpkg.com/postcss-zindex/-/postcss-zindex-2.2.0.tgz#d2109ddc055b91af67fc4cb3b025946639d2af22" @@ -6769,6 +6869,15 @@ postcss@^5.0.10, postcss@^5.0.11, postcss@^5.0.12, postcss@^5.0.13, postcss@^5.0 source-map "^0.5.6" supports-color "^3.2.3" +postcss@^7.0.14, postcss@^7.0.16, postcss@^7.0.17, postcss@^7.0.5, postcss@^7.0.6: + version "7.0.21" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.21.tgz#06bb07824c19c2021c5d056d5b10c35b989f7e17" + integrity sha512-uIFtJElxJo29QC753JzhidoAhvp/e/Exezkdhfmt8AymWT6/5B7W1WmponYWkHk2eg6sONyTch0A3nkMPun3SQ== + dependencies: + chalk "^2.4.2" + source-map "^0.6.1" + supports-color "^6.1.0" + prebuild-install@5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-5.3.0.tgz#58b4d8344e03590990931ee088dd5401b03004c8" @@ -7560,6 +7669,14 @@ schema-utils@^0.4.4, schema-utils@^0.4.5: ajv "^6.1.0" ajv-keywords "^3.1.0" +schema-utils@^2.0.0, schema-utils@^2.0.1: + version "2.5.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.5.0.tgz#8f254f618d402cc80257486213c8970edfd7c22f" + integrity sha512-32ISrwW2scPXHUSusP8qMg5dLUawKkyV+/qIEV9JdXKx+rsM6mi8vZY8khg2M69Qom16rtroWXD3Ybtiws38gQ== + dependencies: + ajv "^6.10.2" + ajv-keywords "^3.4.1" + semver-greatest-satisfied-range@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.1.0.tgz#13e8c2658ab9691cb0cd71093240280d36f77a5b" @@ -8145,6 +8262,14 @@ strip-json-comments@^2.0.1, strip-json-comments@~2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= +style-loader@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-1.0.0.tgz#1d5296f9165e8e2c85d24eee0b7caf9ec8ca1f82" + integrity sha512-B0dOCFwv7/eY31a5PCieNwMgMhVGFe9w+rh7s/Bx8kfFkrth9zfTZquoYvdw8URgiqxObQKcpW51Ugz1HjfdZw== + dependencies: + loader-utils "^1.2.3" + schema-utils "^2.0.1" + sudo-prompt@9.0.0: version "9.0.0" resolved "https://registry.yarnpkg.com/sudo-prompt/-/sudo-prompt-9.0.0.tgz#eebedeee9fcd6f661324e6bb46335e3288e8dc8a" From 25d4fa506be6567defebc5b64d7b13ea2b596391 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Tue, 12 Nov 2019 16:03:32 -0800 Subject: [PATCH 002/200] update yarn gulp esm bundle script --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index eafe38df5a1..a6b96ee9ae3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -125,5 +125,5 @@ jobs: name: Install Dependencies - run: yarn monaco-compile-check name: Run Monaco Editor Checks - - run: yarn editor-esm-bundle + - run: yarn gulp editor-esm-bundle name: Editor Distro & ESM Bundle From 2c1ad8e921b36d3b6e9f80a9b75814535514a616 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Fri, 21 Feb 2020 10:58:11 +0100 Subject: [PATCH 003/200] Fix task user task exeuction through AP. Fixes #90820 --- src/vs/workbench/api/browser/mainThreadTask.ts | 2 +- src/vs/workbench/api/common/extHostTask.ts | 5 ++++- src/vs/workbench/api/common/shared/tasks.ts | 2 +- .../contrib/tasks/browser/abstractTaskService.ts | 8 +++----- src/vs/workbench/contrib/tasks/common/taskService.ts | 2 ++ 5 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadTask.ts b/src/vs/workbench/api/browser/mainThreadTask.ts index f78dc6ca963..ede1ae5c393 100644 --- a/src/vs/workbench/api/browser/mainThreadTask.ts +++ b/src/vs/workbench/api/browser/mainThreadTask.ts @@ -512,7 +512,7 @@ export class MainThreadTask implements MainThreadTaskShape { public $executeTask(value: TaskHandleDTO | TaskDTO): Promise { return new Promise((resolve, reject) => { if (TaskHandleDTO.is(value)) { - const workspaceFolder = this._workspaceContextServer.getWorkspaceFolder(URI.revive(value.workspaceFolder)); + const workspaceFolder = typeof value.workspaceFolder === 'string' ? value.workspaceFolder : this._workspaceContextServer.getWorkspaceFolder(URI.revive(value.workspaceFolder)); if (workspaceFolder) { this._taskService.getTask(workspaceFolder, value.id, true).then((task: Task | undefined) => { if (!task) { diff --git a/src/vs/workbench/api/common/extHostTask.ts b/src/vs/workbench/api/common/extHostTask.ts index 8ae923128bc..71430153a3d 100644 --- a/src/vs/workbench/api/common/extHostTask.ts +++ b/src/vs/workbench/api/common/extHostTask.ts @@ -26,6 +26,7 @@ import { Schemas } from 'vs/base/common/network'; import * as Platform from 'vs/base/common/platform'; import { ILogService } from 'vs/platform/log/common/log'; import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService'; +import { USER_TASKS_GROUP_KEY } from 'vs/workbench/contrib/tasks/common/taskService'; export interface IExtHostTask extends ExtHostTaskShape { @@ -192,9 +193,11 @@ export namespace CustomExecutionDTO { export namespace TaskHandleDTO { export function from(value: types.Task): tasks.TaskHandleDTO { - let folder: UriComponents | undefined; + let folder: UriComponents | string; if (value.scope !== undefined && typeof value.scope !== 'number') { folder = value.scope.uri; + } else if (value.scope !== undefined && typeof value.scope === 'number') { + folder = USER_TASKS_GROUP_KEY; } return { id: value._id!, diff --git a/src/vs/workbench/api/common/shared/tasks.ts b/src/vs/workbench/api/common/shared/tasks.ts index 465a042d8b5..17396177e5b 100644 --- a/src/vs/workbench/api/common/shared/tasks.ts +++ b/src/vs/workbench/api/common/shared/tasks.ts @@ -78,7 +78,7 @@ export interface TaskSourceDTO { export interface TaskHandleDTO { id: string; - workspaceFolder: UriComponents; + workspaceFolder: UriComponents | string; } export interface TaskDTO { diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index 09ba6cc1fa9..4eff5ae0290 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -57,7 +57,7 @@ import { TaskSorter, TaskIdentifier, KeyedTaskIdentifier, TASK_RUNNING_STATE, TaskRunSource, KeyedTaskIdentifier as NKeyedTaskIdentifier, TaskDefinition } from 'vs/workbench/contrib/tasks/common/tasks'; -import { ITaskService, ITaskProvider, ProblemMatcherRunOptions, CustomizationProperties, TaskFilter, WorkspaceFolderTaskResult } from 'vs/workbench/contrib/tasks/common/taskService'; +import { ITaskService, ITaskProvider, ProblemMatcherRunOptions, CustomizationProperties, TaskFilter, WorkspaceFolderTaskResult, USER_TASKS_GROUP_KEY } from 'vs/workbench/contrib/tasks/common/taskService'; import { getTemplates as getTaskTemplates } from 'vs/workbench/contrib/tasks/common/taskTemplates'; import * as TaskConfig from '../common/taskConfiguration'; @@ -86,8 +86,6 @@ const QUICKOPEN_DETAIL_CONFIG = 'task.quickOpen.detail'; const PROBLEM_MATCHER_NEVER_CONFIG = 'task.problemMatchers.neverPrompt'; const QUICKOPEN_SKIP_CONFIG = 'task.quickOpen.skip'; -const SETTINGS_GROUP_KEY = 'settings'; - export namespace ConfigureTaskAction { export const ID = 'workbench.action.tasks.configureTaskRunner'; export const TEXT = nls.localize('ConfigureTaskRunnerAction.label', "Configure Task"); @@ -551,7 +549,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } return this.getGroupedTasks().then((map) => { let values = map.get(folder); - values = values.concat(map.get(SETTINGS_GROUP_KEY)); + values = values.concat(map.get(USER_TASKS_GROUP_KEY)); if (!values) { return undefined; @@ -1584,7 +1582,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } const userTasks = await this.computeUserTasks(this.workspaceFolders[0], runSource).then((value) => value, () => undefined); if (userTasks) { - result.set(SETTINGS_GROUP_KEY, userTasks); + result.set(USER_TASKS_GROUP_KEY, userTasks); } const workspaceFileTasks = await this.computeWorkspaceFileTasks(this.workspaceFolders[0], runSource).then((value) => value, () => undefined); if (workspaceFileTasks && this._workspace && this._workspace.configuration) { diff --git a/src/vs/workbench/contrib/tasks/common/taskService.ts b/src/vs/workbench/contrib/tasks/common/taskService.ts index fb2167017af..2342fedf370 100644 --- a/src/vs/workbench/contrib/tasks/common/taskService.ts +++ b/src/vs/workbench/contrib/tasks/common/taskService.ts @@ -50,6 +50,8 @@ export interface WorkspaceFolderTaskResult extends WorkspaceTaskResult { workspaceFolder: IWorkspaceFolder; } +export const USER_TASKS_GROUP_KEY = 'settings'; + export interface ITaskService { _serviceBrand: undefined; onDidStateChange: Event; From c6d4c7e25e7b884f456ecfca414146ff0ba54b7c Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 21 Feb 2020 11:06:24 +0100 Subject: [PATCH 004/200] improve backup - retain atleast 10 entries - do not fail when writing to backup fails --- .../common/abstractSynchronizer.ts | 53 +++++++++++-------- .../userDataSync/common/extensionsSync.ts | 4 +- .../userDataSync/common/globalStateSync.ts | 4 +- .../userDataSync/common/keybindingsSync.ts | 4 +- .../userDataSync/common/settingsSync.ts | 4 +- 5 files changed, 40 insertions(+), 29 deletions(-) diff --git a/src/vs/platform/userDataSync/common/abstractSynchronizer.ts b/src/vs/platform/userDataSync/common/abstractSynchronizer.ts index cdf2baeef81..45e1e01f6f7 100644 --- a/src/vs/platform/userDataSync/common/abstractSynchronizer.ts +++ b/src/vs/platform/userDataSync/common/abstractSynchronizer.ts @@ -18,8 +18,7 @@ import { ParseError, parse } from 'vs/base/common/json'; import { FormattingOptions } from 'vs/base/common/jsonFormatter'; import { IStringDictionary } from 'vs/base/common/collections'; import { localize } from 'vs/nls'; - -const BACK_UP_MAX_AGE = 1000 * 60 * 60 * 24 * 30; /* 30 days */ +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; type SyncSourceClassification = { source?: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; @@ -65,6 +64,7 @@ export abstract class AbstractSynchroniser extends Disposable { @IUserDataSyncEnablementService protected readonly userDataSyncEnablementService: IUserDataSyncEnablementService, @ITelemetryService private readonly telemetryService: ITelemetryService, @IUserDataSyncLogService protected readonly logService: IUserDataSyncLogService, + @IConfigurationService protected readonly configurationService: IConfigurationService, ) { super(); this.syncFolder = joinPath(environmentService.userDataSyncHome, source); @@ -191,31 +191,38 @@ export abstract class AbstractSynchroniser extends Disposable { protected async backupLocal(content: VSBuffer): Promise { const resource = joinPath(this.syncFolder, toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')); - await this.fileService.writeFile(resource, content); + try { + await this.fileService.writeFile(resource, content); + } catch (e) { + this.logService.error(e); + } this.cleanUpDelayer.trigger(() => this.cleanUpBackup()); } private async cleanUpBackup(): Promise { const stat = await this.fileService.resolve(this.syncFolder); if (stat.children) { - const all = stat.children.filter(stat => stat.isFile && /^\d{8}T\d{6}$/.test(stat.name)); - if (all.length > 10) { - const toDelete = all.filter(stat => { - const ctime = stat.ctime || new Date( - parseInt(stat.name.substring(0, 4)), - parseInt(stat.name.substring(4, 6)) - 1, - parseInt(stat.name.substring(6, 8)), - parseInt(stat.name.substring(9, 11)), - parseInt(stat.name.substring(11, 13)), - parseInt(stat.name.substring(13, 15)) - ).getTime(); - return Date.now() - ctime > BACK_UP_MAX_AGE; - }); - await Promise.all(toDelete.map(stat => { - this.logService.info('Deleting from backup', stat.resource.path); - this.fileService.del(stat.resource); - })); + const all = stat.children.filter(stat => stat.isFile && /^\d{8}T\d{6}$/.test(stat.name)).sort(); + const backUpMaxAge = 1000 * 60 * 60 * 24 * (this.configurationService.getValue('sync.localBackupDuration') || 30 /* Default 30 days */); + let toDelete = all.filter(stat => { + const ctime = stat.ctime || new Date( + parseInt(stat.name.substring(0, 4)), + parseInt(stat.name.substring(4, 6)) - 1, + parseInt(stat.name.substring(6, 8)), + parseInt(stat.name.substring(9, 11)), + parseInt(stat.name.substring(11, 13)), + parseInt(stat.name.substring(13, 15)) + ).getTime(); + return Date.now() - ctime > backUpMaxAge; + }); + const remaining = all.length - toDelete.length; + if (remaining < 10) { + toDelete = toDelete.slice(10 - remaining); } + await Promise.all(toDelete.map(stat => { + this.logService.info('Deleting from backup', stat.resource.path); + this.fileService.del(stat.resource); + })); } } @@ -247,8 +254,9 @@ export abstract class AbstractFileSynchroniser extends AbstractSynchroniser { @IUserDataSyncEnablementService userDataSyncEnablementService: IUserDataSyncEnablementService, @ITelemetryService telemetryService: ITelemetryService, @IUserDataSyncLogService logService: IUserDataSyncLogService, + @IConfigurationService configurationService: IConfigurationService, ) { - super(source, fileService, environmentService, userDataSyncStoreService, userDataSyncEnablementService, telemetryService, logService); + super(source, fileService, environmentService, userDataSyncStoreService, userDataSyncEnablementService, telemetryService, logService, configurationService); this._register(this.fileService.watch(dirname(file))); this._register(this.fileService.onDidFilesChange(e => this.onFileChanges(e))); } @@ -346,8 +354,9 @@ export abstract class AbstractJsonFileSynchroniser extends AbstractFileSynchroni @ITelemetryService telemetryService: ITelemetryService, @IUserDataSyncLogService logService: IUserDataSyncLogService, @IUserDataSyncUtilService protected readonly userDataSyncUtilService: IUserDataSyncUtilService, + @IConfigurationService configurationService: IConfigurationService, ) { - super(file, source, fileService, environmentService, userDataSyncStoreService, userDataSyncEnablementService, telemetryService, logService); + super(file, source, fileService, environmentService, userDataSyncStoreService, userDataSyncEnablementService, telemetryService, logService, configurationService); } protected hasErrors(content: string): boolean { diff --git a/src/vs/platform/userDataSync/common/extensionsSync.ts b/src/vs/platform/userDataSync/common/extensionsSync.ts index 0c189d343c9..d9a082cfb6f 100644 --- a/src/vs/platform/userDataSync/common/extensionsSync.ts +++ b/src/vs/platform/userDataSync/common/extensionsSync.ts @@ -46,11 +46,11 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse @IGlobalExtensionEnablementService private readonly extensionEnablementService: IGlobalExtensionEnablementService, @IUserDataSyncLogService logService: IUserDataSyncLogService, @IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService, - @IConfigurationService private readonly configurationService: IConfigurationService, + @IConfigurationService configurationService: IConfigurationService, @IUserDataSyncEnablementService userDataSyncEnablementService: IUserDataSyncEnablementService, @ITelemetryService telemetryService: ITelemetryService, ) { - super(SyncSource.Extensions, fileService, environmentService, userDataSyncStoreService, userDataSyncEnablementService, telemetryService, logService); + super(SyncSource.Extensions, fileService, environmentService, userDataSyncStoreService, userDataSyncEnablementService, telemetryService, logService, configurationService); this._register( Event.debounce( Event.any( diff --git a/src/vs/platform/userDataSync/common/globalStateSync.ts b/src/vs/platform/userDataSync/common/globalStateSync.ts index 6e76d260dc2..4cae464d938 100644 --- a/src/vs/platform/userDataSync/common/globalStateSync.ts +++ b/src/vs/platform/userDataSync/common/globalStateSync.ts @@ -15,6 +15,7 @@ import { merge } from 'vs/platform/userDataSync/common/globalStateMerge'; import { parse } from 'vs/base/common/json'; import { AbstractSynchroniser, IRemoteUserData } from 'vs/platform/userDataSync/common/abstractSynchronizer'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; const argvProperties: string[] = ['locale']; @@ -38,8 +39,9 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs @IEnvironmentService private readonly environmentService: IEnvironmentService, @IUserDataSyncEnablementService userDataSyncEnablementService: IUserDataSyncEnablementService, @ITelemetryService telemetryService: ITelemetryService, + @IConfigurationService configurationService: IConfigurationService, ) { - super(SyncSource.GlobalState, fileService, environmentService, userDataSyncStoreService, userDataSyncEnablementService, telemetryService, logService); + super(SyncSource.GlobalState, fileService, environmentService, userDataSyncStoreService, userDataSyncEnablementService, telemetryService, logService, configurationService); this._register(this.fileService.watch(dirname(this.environmentService.argvResource))); this._register(Event.filter(this.fileService.onDidFilesChange, e => e.contains(this.environmentService.argvResource))(() => this._onDidChangeLocal.fire())); } diff --git a/src/vs/platform/userDataSync/common/keybindingsSync.ts b/src/vs/platform/userDataSync/common/keybindingsSync.ts index 251999ef64d..4d1134dfbf4 100644 --- a/src/vs/platform/userDataSync/common/keybindingsSync.ts +++ b/src/vs/platform/userDataSync/common/keybindingsSync.ts @@ -36,14 +36,14 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem constructor( @IUserDataSyncStoreService userDataSyncStoreService: IUserDataSyncStoreService, @IUserDataSyncLogService logService: IUserDataSyncLogService, - @IConfigurationService private readonly configurationService: IConfigurationService, + @IConfigurationService configurationService: IConfigurationService, @IUserDataSyncEnablementService userDataSyncEnablementService: IUserDataSyncEnablementService, @IFileService fileService: IFileService, @IEnvironmentService private readonly environmentService: IEnvironmentService, @IUserDataSyncUtilService userDataSyncUtilService: IUserDataSyncUtilService, @ITelemetryService telemetryService: ITelemetryService, ) { - super(environmentService.keybindingsResource, SyncSource.Keybindings, fileService, environmentService, userDataSyncStoreService, userDataSyncEnablementService, telemetryService, logService, userDataSyncUtilService); + super(environmentService.keybindingsResource, SyncSource.Keybindings, fileService, environmentService, userDataSyncStoreService, userDataSyncEnablementService, telemetryService, logService, userDataSyncUtilService, configurationService); } async pull(): Promise { diff --git a/src/vs/platform/userDataSync/common/settingsSync.ts b/src/vs/platform/userDataSync/common/settingsSync.ts index abb2996f999..7e1834fcedb 100644 --- a/src/vs/platform/userDataSync/common/settingsSync.ts +++ b/src/vs/platform/userDataSync/common/settingsSync.ts @@ -51,11 +51,11 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement @IUserDataSyncStoreService userDataSyncStoreService: IUserDataSyncStoreService, @IUserDataSyncLogService logService: IUserDataSyncLogService, @IUserDataSyncUtilService userDataSyncUtilService: IUserDataSyncUtilService, - @IConfigurationService private readonly configurationService: IConfigurationService, + @IConfigurationService configurationService: IConfigurationService, @IUserDataSyncEnablementService userDataSyncEnablementService: IUserDataSyncEnablementService, @ITelemetryService telemetryService: ITelemetryService, ) { - super(environmentService.settingsResource, SyncSource.Settings, fileService, environmentService, userDataSyncStoreService, userDataSyncEnablementService, telemetryService, logService, userDataSyncUtilService); + super(environmentService.settingsResource, SyncSource.Settings, fileService, environmentService, userDataSyncStoreService, userDataSyncEnablementService, telemetryService, logService, userDataSyncUtilService, configurationService); } protected setStatus(status: SyncStatus): void { From 11fd274e2e3e916b53615eebda7cf0ab3ddfb8d0 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 21 Feb 2020 09:42:58 +0100 Subject: [PATCH 005/200] tests - add test for out of workspace watchers --- .../editor/test/browser/editorService.test.ts | 22 +++++++++++++++++++ .../test/browser/workbenchTestServices.ts | 9 +++++++- 2 files changed, 30 insertions(+), 1 deletion(-) 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 f3b9f8dd63a..8c0a378ee5c 100644 --- a/src/vs/workbench/services/editor/test/browser/editorService.test.ts +++ b/src/vs/workbench/services/editor/test/browser/editorService.test.ts @@ -896,4 +896,26 @@ suite('EditorService', () => { Event.once(editorService.onDidActiveEditorChange)(c); }); } + + test('file watcher gets installed for out of workspace files', async function () { + const [part, service, testInstantiationService, accessor] = createEditorService(); + + const input1 = testInstantiationService.createInstance(TestEditorInput, URI.parse('file://resource1-openside')); + const input2 = testInstantiationService.createInstance(TestEditorInput, URI.parse('file://resource2-openside')); + + await part.whenRestored; + + await service.openEditor(input1, { pinned: true }); + assert.equal(accessor.fileService.watches.length, 1); + assert.equal(accessor.fileService.watches[0].toString(), input1.resource.toString()); + + const editor = await service.openEditor(input2, { pinned: true }); + assert.equal(accessor.fileService.watches.length, 1); + assert.equal(accessor.fileService.watches[0].toString(), input2.resource.toString()); + + await editor?.group?.closeAllEditors(); + assert.equal(accessor.fileService.watches.length, 0); + + part.dispose(); + }); }); diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index 9e00ead1308..16dac785423 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -739,7 +739,14 @@ export class TestFileService implements IFileService { } del(_resource: URI, _options?: { useTrash?: boolean, recursive?: boolean }): Promise { return Promise.resolve(); } - watch(_resource: URI): IDisposable { return Disposable.None; } + + readonly watches: URI[] = []; + watch(_resource: URI): IDisposable { + this.watches.push(_resource); + + return toDisposable(() => this.watches.splice(this.watches.indexOf(_resource), 1)); + } + getWriteEncoding(_resource: URI): IResourceEncoding { return { encoding: 'utf8', hasBOM: false }; } dispose(): void { } } From 4aa7935e32ed5defa3d273c7f4410fe07d27beb6 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 21 Feb 2020 11:07:56 +0100 Subject: [PATCH 006/200] tests - reduce code duplication --- .../electron-browser/backupRestorer.test.ts | 17 +- .../electron-browser/backupTracker.test.ts | 33 +-- .../test/browser/saveParticipant.test.ts | 14 +- .../files/test/browser/editorAutoSave.test.ts | 18 +- .../test/browser/fileEditorInput.test.ts | 19 +- .../test/browser/fileEditorTracker.test.ts | 22 +- .../test/browser/fileOnDiskProvider.test.ts | 14 +- .../test/browser/editorGroupsService.test.ts | 132 +++------ .../editor/test/browser/editorService.test.ts | 199 ++++--------- .../test/browser/editorsObserver.test.ts | 128 ++------- .../history/test/browser/history.test.ts | 103 ++----- .../test/browser/textFileEditorModel.test.ts | 59 ++-- .../textFileEditorModelManager.test.ts | 17 +- .../test/browser/textFileService.test.ts | 29 +- .../textFileService.io.test.ts | 43 +-- .../browser/textModelResolverService.test.ts | 23 +- .../test/browser/untitledTextEditor.test.ts | 23 +- .../browser/workingCopyFileService.test.ts | 18 +- .../test/browser/parts/editor/editor.test.ts | 31 +- .../parts/editor/editorDiffModel.test.ts | 18 +- .../parts/editor/resourceEditorInput.test.ts | 15 +- .../test/browser/workbenchTestServices.ts | 268 ++++++++++++++---- .../electron-browser/workbenchTestServices.ts | 53 +++- 23 files changed, 488 insertions(+), 808 deletions(-) diff --git a/src/vs/workbench/contrib/backup/test/electron-browser/backupRestorer.test.ts b/src/vs/workbench/contrib/backup/test/electron-browser/backupRestorer.test.ts index ee976c15a01..eab7952a824 100644 --- a/src/vs/workbench/contrib/backup/test/electron-browser/backupRestorer.test.ts +++ b/src/vs/workbench/contrib/backup/test/electron-browser/backupRestorer.test.ts @@ -12,12 +12,10 @@ import { URI } from 'vs/base/common/uri'; import { createTextBufferFactory } from 'vs/editor/common/model/textModel'; import { getRandomTestPath } from 'vs/base/test/node/testUtils'; import { DefaultEndOfLine } from 'vs/editor/common/model'; -import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { hashPath } from 'vs/workbench/services/backup/node/backupFileService'; import { NativeBackupTracker } from 'vs/workbench/contrib/backup/electron-browser/backupTracker'; -import { TestTextFileService, workbenchInstantiationService } from 'vs/workbench/test/electron-browser/workbenchTestServices'; +import { workbenchInstantiationService } from 'vs/workbench/test/electron-browser/workbenchTestServices'; import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager'; -import { BackupRestorer } from 'vs/workbench/contrib/backup/common/backupRestorer'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { EditorPart } from 'vs/workbench/browser/parts/editor/editorPart'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; @@ -33,6 +31,8 @@ import { NodeTestBackupFileService } from 'vs/workbench/services/backup/test/ele import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; import { isEqual } from 'vs/base/common/resources'; +import { TestServiceAccessor } from 'vs/workbench/test/browser/workbenchTestServices'; +import { BackupRestorer } from 'vs/workbench/contrib/backup/common/backupRestorer'; const userdataDir = getRandomTestPath(os.tmpdir(), 'vsctests', 'backuprestorer'); const backupHome = path.join(userdataDir, 'Backups'); @@ -51,15 +51,8 @@ class TestBackupRestorer extends BackupRestorer { } } -class ServiceAccessor { - constructor( - @ITextFileService public textFileService: TestTextFileService - ) { - } -} - suite('BackupRestorer', () => { - let accessor: ServiceAccessor; + let accessor: TestServiceAccessor; let disposables: IDisposable[] = []; @@ -105,7 +98,7 @@ suite('BackupRestorer', () => { const editorService: EditorService = instantiationService.createInstance(EditorService); instantiationService.stub(IEditorService, editorService); - accessor = instantiationService.createInstance(ServiceAccessor); + accessor = instantiationService.createInstance(TestServiceAccessor); await part.whenRestored; diff --git a/src/vs/workbench/contrib/backup/test/electron-browser/backupTracker.test.ts b/src/vs/workbench/contrib/backup/test/electron-browser/backupTracker.test.ts index a98b63d16a9..8420b62c436 100644 --- a/src/vs/workbench/contrib/backup/test/electron-browser/backupTracker.test.ts +++ b/src/vs/workbench/contrib/backup/test/electron-browser/backupTracker.test.ts @@ -10,10 +10,8 @@ import * as path from 'vs/base/common/path'; import * as pfs from 'vs/base/node/pfs'; import { URI } from 'vs/base/common/uri'; import { getRandomTestPath } from 'vs/base/test/node/testUtils'; -import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { hashPath } from 'vs/workbench/services/backup/node/backupFileService'; import { NativeBackupTracker } from 'vs/workbench/contrib/backup/electron-browser/backupTracker'; -import { TestLifecycleService, TestFilesConfigurationService, TestContextService, TestFileService, TestFileDialogService } from 'vs/workbench/test/browser/workbenchTestServices'; import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { EditorPart } from 'vs/workbench/browser/parts/editor/editorPart'; @@ -32,16 +30,14 @@ import { toResource } from 'vs/base/test/common/utils'; import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { ILogService } from 'vs/platform/log/common/log'; -import { HotExitConfiguration, IFileService } from 'vs/platform/files/common/files'; +import { HotExitConfiguration } from 'vs/platform/files/common/files'; import { ShutdownReason, ILifecycleService, BeforeShutdownEvent } from 'vs/platform/lifecycle/common/lifecycle'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IFileDialogService, ConfirmResult, IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IWorkspaceContextService, Workspace } from 'vs/platform/workspace/common/workspace'; import { IElectronService } from 'vs/platform/electron/node/electron'; import { BackupTracker } from 'vs/workbench/contrib/backup/common/backupTracker'; -import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; -import { IModelService } from 'vs/editor/common/services/modelService'; -import { TestTextFileService, TestElectronService, workbenchInstantiationService } from 'vs/workbench/test/electron-browser/workbenchTestServices'; +import { workbenchInstantiationService, TestServiceAccessor } from 'vs/workbench/test/electron-browser/workbenchTestServices'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput'; @@ -52,23 +48,6 @@ const workspacesJsonPath = path.join(backupHome, 'workspaces.json'); const workspaceResource = URI.file(platform.isWindows ? 'c:\\workspace' : '/workspace'); const workspaceBackupPath = path.join(backupHome, hashPath(workspaceResource)); -class ServiceAccessor { - constructor( - @ILifecycleService public lifecycleService: TestLifecycleService, - @ITextFileService public textFileService: TestTextFileService, - @IFilesConfigurationService public filesConfigurationService: TestFilesConfigurationService, - @IWorkspaceContextService public contextService: TestContextService, - @IModelService public modelService: ModelServiceImpl, - @IFileService public fileService: TestFileService, - @IElectronService public electronService: TestElectronService, - @IFileDialogService public fileDialogService: TestFileDialogService, - @IBackupFileService public backupFileService: NodeTestBackupFileService, - @IWorkingCopyService public workingCopyService: IWorkingCopyService, - @IEditorService public editorService: IEditorService - ) { - } -} - class TestBackupTracker extends NativeBackupTracker { constructor( @@ -102,12 +81,12 @@ class BeforeShutdownEventImpl implements BeforeShutdownEvent { } suite('BackupTracker', () => { - let accessor: ServiceAccessor; + let accessor: TestServiceAccessor; let disposables: IDisposable[] = []; setup(async () => { const instantiationService = workbenchInstantiationService(); - accessor = instantiationService.createInstance(ServiceAccessor); + accessor = instantiationService.createInstance(TestServiceAccessor); disposables.push(Registry.as(EditorExtensions.Editors).registerEditor( EditorDescriptor.create( @@ -134,7 +113,7 @@ suite('BackupTracker', () => { return pfs.rimraf(backupHome, pfs.RimRafMode.MOVE); }); - async function createTracker(): Promise<[ServiceAccessor, EditorPart, BackupTracker, IInstantiationService]> { + async function createTracker(): Promise<[TestServiceAccessor, EditorPart, BackupTracker, IInstantiationService]> { const backupFileService = new NodeTestBackupFileService(workspaceBackupPath); const instantiationService = workbenchInstantiationService(); instantiationService.stub(IBackupFileService, backupFileService); @@ -148,7 +127,7 @@ suite('BackupTracker', () => { const editorService: EditorService = instantiationService.createInstance(EditorService); instantiationService.stub(IEditorService, editorService); - accessor = instantiationService.createInstance(ServiceAccessor); + accessor = instantiationService.createInstance(TestServiceAccessor); await part.whenRestored; diff --git a/src/vs/workbench/contrib/codeEditor/test/browser/saveParticipant.test.ts b/src/vs/workbench/contrib/codeEditor/test/browser/saveParticipant.test.ts index c695bd3eaef..5c7917b7350 100644 --- a/src/vs/workbench/contrib/codeEditor/test/browser/saveParticipant.test.ts +++ b/src/vs/workbench/contrib/codeEditor/test/browser/saveParticipant.test.ts @@ -7,29 +7,23 @@ import * as assert from 'assert'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { FinalNewLineParticipant, TrimFinalNewLinesParticipant } from 'vs/workbench/contrib/codeEditor/browser/saveParticipants'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; -import { workbenchInstantiationService, TestTextFileService } from 'vs/workbench/test/browser/workbenchTestServices'; +import { workbenchInstantiationService, TestServiceAccessor } from 'vs/workbench/test/browser/workbenchTestServices'; import { toResource } from 'vs/base/test/common/utils'; -import { IModelService } from 'vs/editor/common/services/modelService'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; -import { ITextFileService, IResolvedTextFileEditorModel, snapshotToString } from 'vs/workbench/services/textfile/common/textfiles'; +import { IResolvedTextFileEditorModel, snapshotToString } from 'vs/workbench/services/textfile/common/textfiles'; import { SaveReason } from 'vs/workbench/common/editor'; import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager'; -class ServiceAccessor { - constructor(@ITextFileService public textFileService: TestTextFileService, @IModelService public modelService: IModelService) { - } -} - suite('MainThreadSaveParticipant', function () { let instantiationService: IInstantiationService; - let accessor: ServiceAccessor; + let accessor: TestServiceAccessor; setup(() => { instantiationService = workbenchInstantiationService(); - accessor = instantiationService.createInstance(ServiceAccessor); + accessor = instantiationService.createInstance(TestServiceAccessor); }); teardown(() => { diff --git a/src/vs/workbench/contrib/files/test/browser/editorAutoSave.test.ts b/src/vs/workbench/contrib/files/test/browser/editorAutoSave.test.ts index 93617ce7314..4c12705aa05 100644 --- a/src/vs/workbench/contrib/files/test/browser/editorAutoSave.test.ts +++ b/src/vs/workbench/contrib/files/test/browser/editorAutoSave.test.ts @@ -7,9 +7,8 @@ import * as assert from 'assert'; import { Event } from 'vs/base/common/event'; import { toResource } from 'vs/base/test/common/utils'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { TestFileService, TestFilesConfigurationService, TestEnvironmentService, workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices'; -import { ITextFileService, IResolvedTextFileEditorModel, ITextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles'; -import { IFileService } from 'vs/platform/files/common/files'; +import { TestFilesConfigurationService, TestEnvironmentService, workbenchInstantiationService, TestServiceAccessor } from 'vs/workbench/test/browser/workbenchTestServices'; +import { IResolvedTextFileEditorModel, ITextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { IEditorRegistry, EditorDescriptor, Extensions as EditorExtensions } from 'vs/workbench/browser/editor'; @@ -28,17 +27,6 @@ import { IFilesConfigurationService } from 'vs/workbench/services/filesConfigura import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; -class ServiceAccessor { - constructor( - @IEditorService public editorService: IEditorService, - @IEditorGroupsService public editorGroupService: IEditorGroupsService, - @ITextFileService public textFileService: ITextFileService, - @IFileService public fileService: TestFileService, - @IConfigurationService public configurationService: TestConfigurationService - ) { - } -} - suite('EditorAutoSave', () => { let disposables: IDisposable[] = []; @@ -81,7 +69,7 @@ suite('EditorAutoSave', () => { const editorService: EditorService = instantiationService.createInstance(EditorService); instantiationService.stub(IEditorService, editorService); - const accessor = instantiationService.createInstance(ServiceAccessor); + const accessor = instantiationService.createInstance(TestServiceAccessor); const editorAutoSave = instantiationService.createInstance(EditorAutoSave); diff --git a/src/vs/workbench/contrib/files/test/browser/fileEditorInput.test.ts b/src/vs/workbench/contrib/files/test/browser/fileEditorInput.test.ts index 2ff0f6bde41..9aaea5c645b 100644 --- a/src/vs/workbench/contrib/files/test/browser/fileEditorInput.test.ts +++ b/src/vs/workbench/contrib/files/test/browser/fileEditorInput.test.ts @@ -7,34 +7,23 @@ import * as assert from 'assert'; import { URI } from 'vs/base/common/uri'; import { toResource } from 'vs/base/test/common/utils'; import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { workbenchInstantiationService, TestTextFileService } from 'vs/workbench/test/browser/workbenchTestServices'; +import { workbenchInstantiationService, TestServiceAccessor } from 'vs/workbench/test/browser/workbenchTestServices'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { EncodingMode, Verbosity } from 'vs/workbench/common/editor'; -import { ITextFileService, TextFileOperationError, TextFileOperationResult } from 'vs/workbench/services/textfile/common/textfiles'; +import { TextFileOperationError, TextFileOperationResult } from 'vs/workbench/services/textfile/common/textfiles'; import { FileOperationResult, FileOperationError } from 'vs/platform/files/common/files'; import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; -import { IModelService } from 'vs/editor/common/services/modelService'; import { timeout } from 'vs/base/common/async'; import { ModesRegistry, PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry'; import { DisposableStore } from 'vs/base/common/lifecycle'; -class ServiceAccessor { - constructor( - @IEditorService public editorService: IEditorService, - @ITextFileService public textFileService: TestTextFileService, - @IModelService public modelService: IModelService - ) { - } -} - suite('Files - FileEditorInput', () => { let instantiationService: IInstantiationService; - let accessor: ServiceAccessor; + let accessor: TestServiceAccessor; setup(() => { instantiationService = workbenchInstantiationService(); - accessor = instantiationService.createInstance(ServiceAccessor); + accessor = instantiationService.createInstance(TestServiceAccessor); }); test('Basics', async function () { diff --git a/src/vs/workbench/contrib/files/test/browser/fileEditorTracker.test.ts b/src/vs/workbench/contrib/files/test/browser/fileEditorTracker.test.ts index 026624314a0..d2b3385e6df 100644 --- a/src/vs/workbench/contrib/files/test/browser/fileEditorTracker.test.ts +++ b/src/vs/workbench/contrib/files/test/browser/fileEditorTracker.test.ts @@ -8,9 +8,9 @@ import { Event } from 'vs/base/common/event'; import { TextFileEditorTracker } from 'vs/workbench/contrib/files/browser/editors/textFileEditorTracker'; import { toResource } from 'vs/base/test/common/utils'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { TestFileService, TestTextFileService, workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices'; -import { ITextFileService, IResolvedTextFileEditorModel, snapshotToString } from 'vs/workbench/services/textfile/common/textfiles'; -import { FileChangesEvent, FileChangeType, IFileService } from 'vs/platform/files/common/files'; +import { workbenchInstantiationService, TestServiceAccessor } from 'vs/workbench/test/browser/workbenchTestServices'; +import { IResolvedTextFileEditorModel, snapshotToString } from 'vs/workbench/services/textfile/common/textfiles'; +import { FileChangesEvent, FileChangeType } from 'vs/platform/files/common/files'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { timeout } from 'vs/base/common/async'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; @@ -26,16 +26,6 @@ import { EditorService } from 'vs/workbench/services/editor/browser/editorServic import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput'; -class ServiceAccessor { - constructor( - @IEditorService public editorService: IEditorService, - @IEditorGroupsService public editorGroupService: IEditorGroupsService, - @ITextFileService public textFileService: TestTextFileService, - @IFileService public fileService: TestFileService - ) { - } -} - suite('Files - TextFileEditorTracker', () => { let disposables: IDisposable[] = []; @@ -58,7 +48,7 @@ suite('Files - TextFileEditorTracker', () => { test('file change event updates model', async function () { const instantiationService = workbenchInstantiationService(); - const accessor = instantiationService.createInstance(ServiceAccessor); + const accessor = instantiationService.createInstance(TestServiceAccessor); const tracker = instantiationService.createInstance(TextFileEditorTracker); @@ -82,7 +72,7 @@ suite('Files - TextFileEditorTracker', () => { (accessor.textFileService.files).dispose(); }); - async function createTracker(): Promise<[EditorPart, ServiceAccessor, TextFileEditorTracker, IInstantiationService, IEditorService]> { + async function createTracker(): Promise<[EditorPart, TestServiceAccessor, TextFileEditorTracker, IInstantiationService, IEditorService]> { const instantiationService = workbenchInstantiationService(); const part = instantiationService.createInstance(EditorPart); @@ -94,7 +84,7 @@ suite('Files - TextFileEditorTracker', () => { const editorService: EditorService = instantiationService.createInstance(EditorService); instantiationService.stub(IEditorService, editorService); - const accessor = instantiationService.createInstance(ServiceAccessor); + const accessor = instantiationService.createInstance(TestServiceAccessor); await part.whenRestored; diff --git a/src/vs/workbench/contrib/files/test/browser/fileOnDiskProvider.test.ts b/src/vs/workbench/contrib/files/test/browser/fileOnDiskProvider.test.ts index 18735013da0..6b29c6e66d9 100644 --- a/src/vs/workbench/contrib/files/test/browser/fileOnDiskProvider.test.ts +++ b/src/vs/workbench/contrib/files/test/browser/fileOnDiskProvider.test.ts @@ -5,27 +5,19 @@ import * as assert from 'assert'; import { URI } from 'vs/base/common/uri'; -import { workbenchInstantiationService, TestFileService } from 'vs/workbench/test/browser/workbenchTestServices'; +import { workbenchInstantiationService, TestServiceAccessor } from 'vs/workbench/test/browser/workbenchTestServices'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { TextFileContentProvider } from 'vs/workbench/contrib/files/common/files'; import { snapshotToString } from 'vs/workbench/services/textfile/common/textfiles'; -import { IFileService } from 'vs/platform/files/common/files'; - -class ServiceAccessor { - constructor( - @IFileService public fileService: TestFileService - ) { - } -} suite('Files - FileOnDiskContentProvider', () => { let instantiationService: IInstantiationService; - let accessor: ServiceAccessor; + let accessor: TestServiceAccessor; setup(() => { instantiationService = workbenchInstantiationService(); - accessor = instantiationService.createInstance(ServiceAccessor); + accessor = instantiationService.createInstance(TestServiceAccessor); }); test('provideTextContent', async () => { diff --git a/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts b/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts index 6f05876c24c..4066348ec73 100644 --- a/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts +++ b/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts @@ -5,89 +5,23 @@ import * as assert from 'assert'; import { EditorPart } from 'vs/workbench/browser/parts/editor/editorPart'; -import { workbenchInstantiationService, TestStorageService } from 'vs/workbench/test/browser/workbenchTestServices'; +import { workbenchInstantiationService, registerTestEditor, TestFileEditorInput } from 'vs/workbench/test/browser/workbenchTestServices'; import { GroupDirection, GroupsOrder, MergeGroupMode, GroupOrientation, GroupChangeKind, GroupLocation } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { EditorInput, IFileEditorInput, IEditorInputFactory, IEditorInputFactoryRegistry, Extensions as EditorExtensions, EditorOptions, CloseDirection, IEditorPartOptions, EditorsOrder } from 'vs/workbench/common/editor'; -import { IEditorModel } from 'vs/platform/editor/common/editor'; +import { EditorOptions, CloseDirection, IEditorPartOptions, EditorsOrder } from 'vs/workbench/common/editor'; import { URI } from 'vs/base/common/uri'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { IEditorRegistry, Extensions, EditorDescriptor } from 'vs/workbench/browser/editor'; -import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; -import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; -import { CancellationToken } from 'vs/base/common/cancellation'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; const TEST_EDITOR_ID = 'MyFileEditorForEditorGroupService'; const TEST_EDITOR_INPUT_ID = 'testEditorInputForEditorGroupService'; -class TestEditorControl extends BaseEditor { - - constructor(@ITelemetryService telemetryService: ITelemetryService) { super(TEST_EDITOR_ID, NullTelemetryService, new TestThemeService(), new TestStorageService()); } - - async setInput(input: EditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise { - super.setInput(input, options, token); - - await input.resolve(); - } - - getId(): string { return TEST_EDITOR_ID; } - layout(): void { } - createEditor(): any { } -} - -class TestEditorInput extends EditorInput implements IFileEditorInput { - - constructor(public resource: URI) { super(); } - - getTypeId() { return TEST_EDITOR_INPUT_ID; } - resolve(): Promise { return Promise.resolve(null); } - matches(other: TestEditorInput): boolean { return other && this.resource.toString() === other.resource.toString() && other instanceof TestEditorInput; } - setEncoding(encoding: string) { } - getEncoding() { return undefined; } - setPreferredEncoding(encoding: string) { } - setMode(mode: string) { } - setPreferredMode(mode: string) { } - setForceOpenAsBinary(): void { } - isResolved(): boolean { return false; } -} - suite('EditorGroupsService', () => { let disposables: IDisposable[] = []; setup(() => { - interface ISerializedTestEditorInput { - resource: string; - } - - class TestEditorInputFactory implements IEditorInputFactory { - - canSerialize(editorInput: EditorInput): boolean { - return true; - } - - serialize(editorInput: EditorInput): string { - const testEditorInput = editorInput; - const testInput: ISerializedTestEditorInput = { - resource: testEditorInput.resource.toString() - }; - - return JSON.stringify(testInput); - } - - deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): EditorInput { - const testInput: ISerializedTestEditorInput = JSON.parse(serializedEditorInput); - - return new TestEditorInput(URI.parse(testInput.resource)); - } - } - - disposables.push((Registry.as(EditorExtensions.EditorInputFactories)).registerEditorInputFactory(TEST_EDITOR_INPUT_ID, TestEditorInputFactory)); - disposables.push((Registry.as(Extensions.Editors)).registerEditor(EditorDescriptor.create(TestEditorControl, TEST_EDITOR_ID, 'My Test File Editor'), [new SyncDescriptor(TestEditorInput)])); + disposables.push(registerTestEditor(TEST_EDITOR_ID, [new SyncDescriptor(TestFileEditorInput)], TEST_EDITOR_INPUT_ID)); }); teardown(() => { @@ -344,17 +278,17 @@ suite('EditorGroupsService', () => { rootGroupDisposed = true; }); - const input = new TestEditorInput(URI.file('foo/bar')); + const input = new TestFileEditorInput(URI.file('foo/bar'), TEST_EDITOR_INPUT_ID); await rootGroup.openEditor(input, EditorOptions.create({ pinned: true })); const rightGroup = part.addGroup(rootGroup, GroupDirection.RIGHT, { activate: true }); const downGroup = part.copyGroup(rootGroup, rightGroup, GroupDirection.DOWN); assert.equal(groupAddedCounter, 2); assert.equal(downGroup.count, 1); - assert.ok(downGroup.activeEditor instanceof TestEditorInput); + assert.ok(downGroup.activeEditor instanceof TestFileEditorInput); part.mergeGroup(rootGroup, rightGroup, { mode: MergeGroupMode.COPY_EDITORS }); assert.equal(rightGroup.count, 1); - assert.ok(rightGroup.activeEditor instanceof TestEditorInput); + assert.ok(rightGroup.activeEditor instanceof TestFileEditorInput); part.mergeGroup(rootGroup, rightGroup, { mode: MergeGroupMode.MOVE_EDITORS }); assert.equal(rootGroup.count, 0); part.mergeGroup(rootGroup, downGroup); @@ -437,8 +371,8 @@ suite('EditorGroupsService', () => { editorWillCloseCounter++; }); - const input = new TestEditorInput(URI.file('foo/bar')); - const inputInactive = new TestEditorInput(URI.file('foo/bar/inactive')); + const input = new TestFileEditorInput(URI.file('foo/bar'), TEST_EDITOR_INPUT_ID); + const inputInactive = new TestFileEditorInput(URI.file('foo/bar/inactive'), TEST_EDITOR_INPUT_ID); await group.openEditor(input, EditorOptions.create({ pinned: true })); await group.openEditor(inputInactive, EditorOptions.create({ inactive: true })); @@ -465,7 +399,7 @@ suite('EditorGroupsService', () => { assert.ok(!group.previewEditor); assert.equal(group.activeEditor, input); - assert.ok(group.activeControl instanceof TestEditorControl); + assert.equal(group.activeControl?.getId(), TEST_EDITOR_ID); assert.equal(group.count, 2); const mru = group.getEditors(EditorsOrder.MOST_RECENTLY_ACTIVE); @@ -498,8 +432,8 @@ suite('EditorGroupsService', () => { const group = part.activeGroup; assert.equal(group.isEmpty, true); - const input = new TestEditorInput(URI.file('foo/bar')); - const inputInactive = new TestEditorInput(URI.file('foo/bar/inactive')); + const input = new TestFileEditorInput(URI.file('foo/bar'), TEST_EDITOR_INPUT_ID); + const inputInactive = new TestFileEditorInput(URI.file('foo/bar/inactive'), TEST_EDITOR_INPUT_ID); await group.openEditors([{ editor: input, options: { pinned: true } }, { editor: inputInactive }]); assert.equal(group.count, 2); @@ -516,9 +450,9 @@ suite('EditorGroupsService', () => { const group = part.activeGroup; assert.equal(group.isEmpty, true); - const input1 = new TestEditorInput(URI.file('foo/bar1')); - const input2 = new TestEditorInput(URI.file('foo/bar2')); - const input3 = new TestEditorInput(URI.file('foo/bar3')); + const input1 = new TestFileEditorInput(URI.file('foo/bar1'), TEST_EDITOR_INPUT_ID); + const input2 = new TestFileEditorInput(URI.file('foo/bar2'), TEST_EDITOR_INPUT_ID); + const input3 = new TestFileEditorInput(URI.file('foo/bar3'), TEST_EDITOR_INPUT_ID); await group.openEditors([{ editor: input1, options: { pinned: true } }, { editor: input2, options: { pinned: true } }, { editor: input3 }]); assert.equal(group.count, 3); @@ -537,9 +471,9 @@ suite('EditorGroupsService', () => { const group = part.activeGroup; assert.equal(group.isEmpty, true); - const input1 = new TestEditorInput(URI.file('foo/bar1')); - const input2 = new TestEditorInput(URI.file('foo/bar2')); - const input3 = new TestEditorInput(URI.file('foo/bar3')); + const input1 = new TestFileEditorInput(URI.file('foo/bar1'), TEST_EDITOR_INPUT_ID); + const input2 = new TestFileEditorInput(URI.file('foo/bar2'), TEST_EDITOR_INPUT_ID); + const input3 = new TestFileEditorInput(URI.file('foo/bar3'), TEST_EDITOR_INPUT_ID); await group.openEditors([{ editor: input1, options: { pinned: true } }, { editor: input2, options: { pinned: true } }, { editor: input3 }]); assert.equal(group.count, 3); @@ -557,9 +491,9 @@ suite('EditorGroupsService', () => { const group = part.activeGroup; assert.equal(group.isEmpty, true); - const input1 = new TestEditorInput(URI.file('foo/bar1')); - const input2 = new TestEditorInput(URI.file('foo/bar2')); - const input3 = new TestEditorInput(URI.file('foo/bar3')); + const input1 = new TestFileEditorInput(URI.file('foo/bar1'), TEST_EDITOR_INPUT_ID); + const input2 = new TestFileEditorInput(URI.file('foo/bar2'), TEST_EDITOR_INPUT_ID); + const input3 = new TestFileEditorInput(URI.file('foo/bar3'), TEST_EDITOR_INPUT_ID); await group.openEditors([{ editor: input1, options: { pinned: true } }, { editor: input2, options: { pinned: true } }, { editor: input3 }]); assert.equal(group.count, 3); @@ -579,9 +513,9 @@ suite('EditorGroupsService', () => { const group = part.activeGroup; assert.equal(group.isEmpty, true); - const input1 = new TestEditorInput(URI.file('foo/bar1')); - const input2 = new TestEditorInput(URI.file('foo/bar2')); - const input3 = new TestEditorInput(URI.file('foo/bar3')); + const input1 = new TestFileEditorInput(URI.file('foo/bar1'), TEST_EDITOR_INPUT_ID); + const input2 = new TestFileEditorInput(URI.file('foo/bar2'), TEST_EDITOR_INPUT_ID); + const input3 = new TestFileEditorInput(URI.file('foo/bar3'), TEST_EDITOR_INPUT_ID); await group.openEditors([{ editor: input1, options: { pinned: true } }, { editor: input2, options: { pinned: true } }, { editor: input3 }]); assert.equal(group.count, 3); @@ -601,8 +535,8 @@ suite('EditorGroupsService', () => { const group = part.activeGroup; assert.equal(group.isEmpty, true); - const input = new TestEditorInput(URI.file('foo/bar')); - const inputInactive = new TestEditorInput(URI.file('foo/bar/inactive')); + const input = new TestFileEditorInput(URI.file('foo/bar'), TEST_EDITOR_INPUT_ID); + const inputInactive = new TestFileEditorInput(URI.file('foo/bar/inactive'), TEST_EDITOR_INPUT_ID); await group.openEditors([{ editor: input, options: { pinned: true } }, { editor: inputInactive }]); assert.equal(group.count, 2); @@ -619,8 +553,8 @@ suite('EditorGroupsService', () => { const group = part.activeGroup; assert.equal(group.isEmpty, true); - const input = new TestEditorInput(URI.file('foo/bar')); - const inputInactive = new TestEditorInput(URI.file('foo/bar/inactive')); + const input = new TestFileEditorInput(URI.file('foo/bar'), TEST_EDITOR_INPUT_ID); + const inputInactive = new TestFileEditorInput(URI.file('foo/bar/inactive'), TEST_EDITOR_INPUT_ID); let editorMoveCounter = 0; const editorGroupChangeListener = group.onDidGroupChange(e => { @@ -649,8 +583,8 @@ suite('EditorGroupsService', () => { const rightGroup = part.addGroup(group, GroupDirection.RIGHT); - const input = new TestEditorInput(URI.file('foo/bar')); - const inputInactive = new TestEditorInput(URI.file('foo/bar/inactive')); + const input = new TestFileEditorInput(URI.file('foo/bar'), TEST_EDITOR_INPUT_ID); + const inputInactive = new TestFileEditorInput(URI.file('foo/bar/inactive'), TEST_EDITOR_INPUT_ID); await group.openEditors([{ editor: input, options: { pinned: true } }, { editor: inputInactive }]); assert.equal(group.count, 2); @@ -671,8 +605,8 @@ suite('EditorGroupsService', () => { const rightGroup = part.addGroup(group, GroupDirection.RIGHT); - const input = new TestEditorInput(URI.file('foo/bar')); - const inputInactive = new TestEditorInput(URI.file('foo/bar/inactive')); + const input = new TestFileEditorInput(URI.file('foo/bar'), TEST_EDITOR_INPUT_ID); + const inputInactive = new TestFileEditorInput(URI.file('foo/bar/inactive'), TEST_EDITOR_INPUT_ID); await group.openEditors([{ editor: input, options: { pinned: true } }, { editor: inputInactive }]); assert.equal(group.count, 2); @@ -692,8 +626,8 @@ suite('EditorGroupsService', () => { const group = part.activeGroup; assert.equal(group.isEmpty, true); - const input = new TestEditorInput(URI.file('foo/bar')); - const inputInactive = new TestEditorInput(URI.file('foo/bar/inactive')); + const input = new TestFileEditorInput(URI.file('foo/bar'), TEST_EDITOR_INPUT_ID); + const inputInactive = new TestFileEditorInput(URI.file('foo/bar/inactive'), TEST_EDITOR_INPUT_ID); await group.openEditor(input); assert.equal(group.count, 1); 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 8c0a378ee5c..a57f108b8fb 100644 --- a/src/vs/workbench/services/editor/test/browser/editorService.test.ts +++ b/src/vs/workbench/services/editor/test/browser/editorService.test.ts @@ -4,21 +4,19 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { EditorActivation, IEditorModel } from 'vs/platform/editor/common/editor'; +import { EditorActivation } from 'vs/platform/editor/common/editor'; import { URI } from 'vs/base/common/uri'; import { Event } from 'vs/base/common/event'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; -import { EditorInput, EditorOptions, IFileEditorInput, GroupIdentifier, ISaveOptions, IRevertOptions, EditorsOrder, IEditorInput, IMoveResult } from 'vs/workbench/common/editor'; -import { workbenchInstantiationService, TestStorageService, TestFileService } from 'vs/workbench/test/browser/workbenchTestServices'; +import { EditorInput, EditorsOrder } from 'vs/workbench/common/editor'; +import { workbenchInstantiationService, TestStorageService, TestServiceAccessor, registerTestEditor, TestFileEditorInput } from 'vs/workbench/test/browser/workbenchTestServices'; import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; import { EditorService, DelegatingEditorService } from 'vs/workbench/services/editor/browser/editorService'; import { IEditorGroup, IEditorGroupsService, GroupDirection, GroupsArrangement } from 'vs/workbench/services/editor/common/editorGroupsService'; import { EditorPart } from 'vs/workbench/browser/parts/editor/editorPart'; import { IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; -import { IEditorRegistry, EditorDescriptor, Extensions } from 'vs/workbench/browser/editor'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; -import { Registry } from 'vs/platform/registry/common/platform'; import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput'; import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput'; import { timeout } from 'vs/base/common/async'; @@ -28,85 +26,10 @@ import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; import { ModesRegistry } from 'vs/editor/common/modes/modesRegistry'; import { UntitledTextEditorModel } from 'vs/workbench/services/untitled/common/untitledTextEditorModel'; import { NullFileSystemProvider } from 'vs/platform/files/test/common/nullFileSystemProvider'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; -import { CancellationToken } from 'vs/base/common/cancellation'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; const TEST_EDITOR_ID = 'MyTestEditorForEditorService'; const TEST_EDITOR_INPUT_ID = 'testEditorInputForEditorService'; -class ServicesAccessor { - constructor( - @IFileService public fileService: TestFileService - ) { } -} - -class TestEditorControl extends BaseEditor { - - constructor(@ITelemetryService telemetryService: ITelemetryService) { super(TEST_EDITOR_ID, NullTelemetryService, new TestThemeService(), new TestStorageService()); } - - async setInput(input: EditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise { - super.setInput(input, options, token); - - await input.resolve(); - } - - getId(): string { return TEST_EDITOR_ID; } - layout(): void { } - createEditor(): any { } -} - -class TestEditorInput extends EditorInput implements IFileEditorInput { - gotDisposed = false; - gotSaved = false; - gotSavedAs = false; - gotReverted = false; - dirty = false; - private fails = false; - constructor(public resource: URI) { super(); } - - getTypeId() { return TEST_EDITOR_INPUT_ID; } - resolve(): Promise { return !this.fails ? Promise.resolve(null) : Promise.reject(new Error('fails')); } - matches(other: TestEditorInput): boolean { return other && other.resource && this.resource.toString() === other.resource.toString() && other instanceof TestEditorInput; } - setEncoding(encoding: string) { } - getEncoding() { return undefined; } - setPreferredEncoding(encoding: string) { } - setMode(mode: string) { } - setPreferredMode(mode: string) { } - setForceOpenAsBinary(): void { } - setFailToOpen(): void { - this.fails = true; - } - async save(groupId: GroupIdentifier, options?: ISaveOptions): Promise { - this.gotSaved = true; - return this; - } - async saveAs(groupId: GroupIdentifier, options?: ISaveOptions): Promise { - this.gotSavedAs = true; - return this; - } - async revert(group: GroupIdentifier, options?: IRevertOptions): Promise { - this.gotReverted = true; - this.gotSaved = false; - this.gotSavedAs = false; - return true; - } - isDirty(): boolean { - return this.dirty; - } - isReadonly(): boolean { - return false; - } - isResolved(): boolean { return false; } - dispose(): void { - super.dispose(); - this.gotDisposed = true; - } - movedEditor: IMoveResult | undefined = undefined; - move(): IMoveResult | undefined { return this.movedEditor; } -} - class FileServiceProvider extends Disposable { constructor(scheme: string, @IFileService fileService: IFileService) { super(); @@ -120,7 +43,7 @@ suite('EditorService', () => { let disposables: IDisposable[] = []; setup(() => { - disposables.push(Registry.as(Extensions.Editors).registerEditor(EditorDescriptor.create(TestEditorControl, TEST_EDITOR_ID, 'My Test Editor For Next Editor Service'), [new SyncDescriptor(TestEditorInput)])); + disposables.push(registerTestEditor(TEST_EDITOR_ID, [new SyncDescriptor(TestFileEditorInput)], TEST_EDITOR_INPUT_ID)); }); teardown(() => { @@ -128,7 +51,7 @@ suite('EditorService', () => { disposables = []; }); - function createEditorService(): [EditorPart, EditorService, IInstantiationService, ServicesAccessor] { + function createEditorService(): [EditorPart, EditorService, TestServiceAccessor] { const instantiationService = workbenchInstantiationService(); const part = instantiationService.createInstance(EditorPart); @@ -140,14 +63,14 @@ suite('EditorService', () => { const editorService = instantiationService.createInstance(EditorService); instantiationService.stub(IEditorService, editorService); - return [part, editorService, instantiationService, instantiationService.createInstance(ServicesAccessor)]; + return [part, editorService, instantiationService.createInstance(TestServiceAccessor)]; } test('basics', async () => { - const [part, service, testInstantiationService] = createEditorService(); + const [part, service] = createEditorService(); - let input = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource-basics')); - let otherInput = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource2-basics')); + let input = new TestFileEditorInput(URI.parse('my://resource-basics'), TEST_EDITOR_INPUT_ID); + let otherInput = new TestFileEditorInput(URI.parse('my://resource2-basics'), TEST_EDITOR_INPUT_ID); let activeEditorChangeEventCounter = 0; const activeEditorChangeListener = service.onDidActiveEditorChange(() => { @@ -169,7 +92,7 @@ suite('EditorService', () => { // Open input let editor = await service.openEditor(input, { pinned: true }); - assert.ok(editor instanceof TestEditorControl); + assert.equal(editor?.getId(), TEST_EDITOR_ID); assert.equal(editor, service.activeControl); assert.equal(1, service.count); assert.equal(input, service.getEditors(EditorsOrder.MOST_RECENTLY_ACTIVE)[0].editor); @@ -200,8 +123,8 @@ suite('EditorService', () => { assert.equal(0, service.count); // Open again 2 inputs (recreate because disposed) - input = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource-basics')); - otherInput = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource2-basics')); + input = new TestFileEditorInput(URI.parse('my://resource-basics'), TEST_EDITOR_INPUT_ID); + otherInput = new TestFileEditorInput(URI.parse('my://resource2-basics'), TEST_EDITOR_INPUT_ID); await service.openEditor(input, { pinned: true }); editor = await service.openEditor(otherInput, { pinned: true }); @@ -226,11 +149,11 @@ suite('EditorService', () => { }); test('openEditors() / replaceEditors()', async () => { - const [part, service, testInstantiationService] = createEditorService(); + const [part, service] = createEditorService(); - const input = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource-openEditors')); - const otherInput = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource2-openEditors')); - const replaceInput = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource3-openEditors')); + const input = new TestFileEditorInput(URI.parse('my://resource-openEditors'), TEST_EDITOR_INPUT_ID); + const otherInput = new TestFileEditorInput(URI.parse('my://resource2-openEditors'), TEST_EDITOR_INPUT_ID); + const replaceInput = new TestFileEditorInput(URI.parse('my://resource3-openEditors'), TEST_EDITOR_INPUT_ID); await part.whenRestored; @@ -401,9 +324,9 @@ suite('EditorService', () => { }); test('close editor does not dispose when editor opened in other group', async () => { - const [part, service, testInstantiationService] = createEditorService(); + const [part, service] = createEditorService(); - const input = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource-close1')); + const input = new TestFileEditorInput(URI.parse('my://resource-close1'), TEST_EDITOR_INPUT_ID); const rootGroup = part.activeGroup; const rightGroup = part.addGroup(rootGroup, GroupDirection.RIGHT); @@ -430,10 +353,10 @@ suite('EditorService', () => { }); test('open to the side', async () => { - const [part, service, testInstantiationService] = createEditorService(); + const [part, service] = createEditorService(); - const input1 = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource1-openside')); - const input2 = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource2-openside')); + const input1 = new TestFileEditorInput(URI.parse('my://resource1-openside'), TEST_EDITOR_INPUT_ID); + const input2 = new TestFileEditorInput(URI.parse('my://resource2-openside'), TEST_EDITOR_INPUT_ID); const rootGroup = part.activeGroup; @@ -456,10 +379,10 @@ suite('EditorService', () => { }); test('editor group activation', async () => { - const [part, service, testInstantiationService] = createEditorService(); + const [part, service] = createEditorService(); - const input1 = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource1-openside')); - const input2 = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource2-openside')); + const input1 = new TestFileEditorInput(URI.parse('my://resource1-openside'), TEST_EDITOR_INPUT_ID); + const input2 = new TestFileEditorInput(URI.parse('my://resource2-openside'), TEST_EDITOR_INPUT_ID); const rootGroup = part.activeGroup; @@ -491,10 +414,10 @@ suite('EditorService', () => { }); test('active editor change / visible editor change events', async function () { - const [part, service, testInstantiationService] = createEditorService(); + const [part, service] = createEditorService(); - let input = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource-active')); - let otherInput = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource2-active')); + let input = new TestFileEditorInput(URI.parse('my://resource-active'), TEST_EDITOR_INPUT_ID); + let otherInput = new TestFileEditorInput(URI.parse('my://resource2-active'), TEST_EDITOR_INPUT_ID); let activeEditorChangeEventFired = false; const activeEditorChangeListener = service.onDidActiveEditorChange(() => { @@ -546,8 +469,8 @@ suite('EditorService', () => { assertVisibleEditorsChangedEvent(true); // 2.) open, open same (forced open) (recreate inputs that got disposed) - input = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource-active')); - otherInput = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource2-active')); + input = new TestFileEditorInput(URI.parse('my://resource-active'), TEST_EDITOR_INPUT_ID); + otherInput = new TestFileEditorInput(URI.parse('my://resource2-active'), TEST_EDITOR_INPUT_ID); editor = await service.openEditor(input); assertActiveEditorChangedEvent(true); assertVisibleEditorsChangedEvent(true); @@ -559,8 +482,8 @@ suite('EditorService', () => { await closeEditorAndWaitForNextToOpen(group, input); // 3.) open, open inactive, close (recreate inputs that got disposed) - input = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource-active')); - otherInput = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource2-active')); + input = new TestFileEditorInput(URI.parse('my://resource-active'), TEST_EDITOR_INPUT_ID); + otherInput = new TestFileEditorInput(URI.parse('my://resource2-active'), TEST_EDITOR_INPUT_ID); editor = await service.openEditor(input, { pinned: true }); assertActiveEditorChangedEvent(true); assertVisibleEditorsChangedEvent(true); @@ -574,8 +497,8 @@ suite('EditorService', () => { assertVisibleEditorsChangedEvent(true); // 4.) open, open inactive, close inactive (recreate inputs that got disposed) - input = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource-active')); - otherInput = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource2-active')); + input = new TestFileEditorInput(URI.parse('my://resource-active'), TEST_EDITOR_INPUT_ID); + otherInput = new TestFileEditorInput(URI.parse('my://resource2-active'), TEST_EDITOR_INPUT_ID); editor = await service.openEditor(input, { pinned: true }); assertActiveEditorChangedEvent(true); assertVisibleEditorsChangedEvent(true); @@ -593,8 +516,8 @@ suite('EditorService', () => { assertVisibleEditorsChangedEvent(true); // 5.) add group, remove group (recreate inputs that got disposed) - input = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource-active')); - otherInput = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource2-active')); + input = new TestFileEditorInput(URI.parse('my://resource-active'), TEST_EDITOR_INPUT_ID); + otherInput = new TestFileEditorInput(URI.parse('my://resource2-active'), TEST_EDITOR_INPUT_ID); editor = await service.openEditor(input, { pinned: true }); assertActiveEditorChangedEvent(true); assertVisibleEditorsChangedEvent(true); @@ -616,8 +539,8 @@ suite('EditorService', () => { assertVisibleEditorsChangedEvent(true); // 6.) open editor in inactive group (recreate inputs that got disposed) - input = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource-active')); - otherInput = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource2-active')); + input = new TestFileEditorInput(URI.parse('my://resource-active'), TEST_EDITOR_INPUT_ID); + otherInput = new TestFileEditorInput(URI.parse('my://resource2-active'), TEST_EDITOR_INPUT_ID); editor = await service.openEditor(input, { pinned: true }); assertActiveEditorChangedEvent(true); assertVisibleEditorsChangedEvent(true); @@ -639,8 +562,8 @@ suite('EditorService', () => { assertVisibleEditorsChangedEvent(true); // 7.) activate group (recreate inputs that got disposed) - input = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource-active')); - otherInput = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource2-active')); + input = new TestFileEditorInput(URI.parse('my://resource-active'), TEST_EDITOR_INPUT_ID); + otherInput = new TestFileEditorInput(URI.parse('my://resource2-active'), TEST_EDITOR_INPUT_ID); editor = await service.openEditor(input, { pinned: true }); assertActiveEditorChangedEvent(true); assertVisibleEditorsChangedEvent(true); @@ -666,8 +589,8 @@ suite('EditorService', () => { assertVisibleEditorsChangedEvent(true); // 8.) move editor (recreate inputs that got disposed) - input = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource-active')); - otherInput = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource2-active')); + input = new TestFileEditorInput(URI.parse('my://resource-active'), TEST_EDITOR_INPUT_ID); + otherInput = new TestFileEditorInput(URI.parse('my://resource2-active'), TEST_EDITOR_INPUT_ID); editor = await service.openEditor(input, { pinned: true }); assertActiveEditorChangedEvent(true); assertVisibleEditorsChangedEvent(true); @@ -685,8 +608,8 @@ suite('EditorService', () => { assertVisibleEditorsChangedEvent(true); // 9.) close editor in inactive group (recreate inputs that got disposed) - input = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource-active')); - otherInput = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource2-active')); + input = new TestFileEditorInput(URI.parse('my://resource-active'), TEST_EDITOR_INPUT_ID); + otherInput = new TestFileEditorInput(URI.parse('my://resource2-active'), TEST_EDITOR_INPUT_ID); editor = await service.openEditor(input, { pinned: true }); assertActiveEditorChangedEvent(true); assertVisibleEditorsChangedEvent(true); @@ -711,9 +634,9 @@ suite('EditorService', () => { }); test('two active editor change events when opening editor to the side', async function () { - const [part, service, testInstantiationService] = createEditorService(); + const [part, service] = createEditorService(); - let input = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource-active')); + let input = new TestFileEditorInput(URI.parse('my://resource-active'), TEST_EDITOR_INPUT_ID); let activeEditorChangeEvents = 0; const activeEditorChangeListener = service.onDidActiveEditorChange(() => { @@ -763,11 +686,11 @@ suite('EditorService', () => { }); test('openEditor returns NULL when opening fails or is inactive', async function () { - const [part, service, testInstantiationService] = createEditorService(); + const [part, service] = createEditorService(); - const input = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource-active')); - const otherInput = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource2-inactive')); - const failingInput = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource3-failing')); + const input = new TestFileEditorInput(URI.parse('my://resource-active'), TEST_EDITOR_INPUT_ID); + const otherInput = new TestFileEditorInput(URI.parse('my://resource2-inactive'), TEST_EDITOR_INPUT_ID); + const failingInput = new TestFileEditorInput(URI.parse('my://resource3-failing'), TEST_EDITOR_INPUT_ID); failingInput.setFailToOpen(); await part.whenRestored; @@ -785,11 +708,11 @@ suite('EditorService', () => { }); test('save, saveAll, revertAll', async function () { - const [part, service, testInstantiationService] = createEditorService(); + const [part, service] = createEditorService(); - const input1 = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource1-openside')); + const input1 = new TestFileEditorInput(URI.parse('my://resource1-openside'), TEST_EDITOR_INPUT_ID); input1.dirty = true; - const input2 = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource2-openside')); + const input2 = new TestFileEditorInput(URI.parse('my://resource2-openside'), TEST_EDITOR_INPUT_ID); input2.dirty = true; const rootGroup = part.activeGroup; @@ -828,11 +751,11 @@ suite('EditorService', () => { }); async function testFileDeleteEditorClose(dirty: boolean): Promise { - const [part, service, testInstantiationService, accessor] = createEditorService(); + const [part, service, accessor] = createEditorService(); - const input1 = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource1-openside')); + const input1 = new TestFileEditorInput(URI.parse('my://resource1-openside'), TEST_EDITOR_INPUT_ID); input1.dirty = dirty; - const input2 = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource2-openside')); + const input2 = new TestFileEditorInput(URI.parse('my://resource2-openside'), TEST_EDITOR_INPUT_ID); input2.dirty = dirty; const rootGroup = part.activeGroup; @@ -860,10 +783,10 @@ suite('EditorService', () => { } test('file move asks input to move', async function () { - const [part, service, testInstantiationService, accessor] = createEditorService(); + const [part, service, accessor] = createEditorService(); - const input1 = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource1-openside')); - const movedInput = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource2-openside')); + const input1 = new TestFileEditorInput(URI.parse('my://resource1-openside'), TEST_EDITOR_INPUT_ID); + const movedInput = new TestFileEditorInput(URI.parse('my://resource2-openside'), TEST_EDITOR_INPUT_ID); input1.movedEditor = { editor: movedInput }; const rootGroup = part.activeGroup; @@ -898,10 +821,10 @@ suite('EditorService', () => { } test('file watcher gets installed for out of workspace files', async function () { - const [part, service, testInstantiationService, accessor] = createEditorService(); + const [part, service, accessor] = createEditorService(); - const input1 = testInstantiationService.createInstance(TestEditorInput, URI.parse('file://resource1-openside')); - const input2 = testInstantiationService.createInstance(TestEditorInput, URI.parse('file://resource2-openside')); + const input1 = new TestFileEditorInput(URI.parse('file://resource1-openside'), TEST_EDITOR_INPUT_ID); + const input2 = new TestFileEditorInput(URI.parse('file://resource2-openside'), TEST_EDITOR_INPUT_ID); await part.whenRestored; diff --git a/src/vs/workbench/services/editor/test/browser/editorsObserver.test.ts b/src/vs/workbench/services/editor/test/browser/editorsObserver.test.ts index a13db24ac3e..2f25fec289d 100644 --- a/src/vs/workbench/services/editor/test/browser/editorsObserver.test.ts +++ b/src/vs/workbench/services/editor/test/browser/editorsObserver.test.ts @@ -4,21 +4,15 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { EditorOptions, EditorInput, IEditorInputFactoryRegistry, Extensions as EditorExtensions, IEditorInputFactory, IFileEditorInput } from 'vs/workbench/common/editor'; +import { EditorOptions, IEditorInputFactoryRegistry, Extensions as EditorExtensions } from 'vs/workbench/common/editor'; import { URI } from 'vs/base/common/uri'; -import { workbenchInstantiationService, TestStorageService } from 'vs/workbench/test/browser/workbenchTestServices'; +import { workbenchInstantiationService, TestStorageService, TestFileEditorInput, registerTestEditor } from 'vs/workbench/test/browser/workbenchTestServices'; import { Registry } from 'vs/platform/registry/common/platform'; import { EditorPart } from 'vs/workbench/browser/parts/editor/editorPart'; -import { IEditorRegistry, EditorDescriptor, Extensions } from 'vs/workbench/browser/editor'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { GroupDirection } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { EditorActivation, IEditorModel } from 'vs/platform/editor/common/editor'; -import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; -import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; -import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; -import { CancellationToken } from 'vs/base/common/cancellation'; +import { EditorActivation } from 'vs/platform/editor/common/editor'; import { WillSaveStateReason } from 'vs/platform/storage/common/storage'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { EditorsObserver } from 'vs/workbench/browser/parts/editor/editorsObserver'; import { timeout } from 'vs/base/common/async'; @@ -27,78 +21,12 @@ const TEST_EDITOR_ID = 'MyTestEditorForEditorsObserver'; const TEST_EDITOR_INPUT_ID = 'testEditorInputForEditorsObserver'; const TEST_SERIALIZABLE_EDITOR_INPUT_ID = 'testSerializableEditorInputForEditorsObserver'; -class TestEditorControl extends BaseEditor { - - constructor() { super(TEST_EDITOR_ID, NullTelemetryService, new TestThemeService(), new TestStorageService()); } - - async setInput(input: EditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise { - super.setInput(input, options, token); - - await input.resolve(); - } - - getId(): string { return TEST_EDITOR_ID; } - layout(): void { } - createEditor(): any { } -} - -class TestEditorInput extends EditorInput implements IFileEditorInput { - - private dirty = false; - - constructor(public resource: URI) { super(); } - - getTypeId() { return TEST_EDITOR_INPUT_ID; } - resolve(): Promise { return Promise.resolve(null); } - matches(other: TestEditorInput): boolean { return other && this.resource.toString() === other.resource.toString() && other instanceof TestEditorInput; } - setEncoding(encoding: string) { } - getEncoding() { return undefined; } - setPreferredEncoding(encoding: string) { } - setMode(mode: string) { } - setPreferredMode(mode: string) { } - setForceOpenAsBinary(): void { } - isDirty(): boolean { return this.dirty; } - setDirty(): void { this.dirty = true; } - isResolved(): boolean { return false; } -} - -class EditorsObserverTestEditorInput extends TestEditorInput { - getTypeId() { return TEST_SERIALIZABLE_EDITOR_INPUT_ID; } -} - -interface ISerializedTestInput { - resource: string; -} - -class EditorsObserverTestEditorInputFactory implements IEditorInputFactory { - - canSerialize(editorInput: EditorInput): boolean { - return true; - } - - serialize(editorInput: EditorInput): string { - let testEditorInput = editorInput; - let testInput: ISerializedTestInput = { - resource: testEditorInput.resource.toString() - }; - - return JSON.stringify(testInput); - } - - deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): EditorInput { - let testInput: ISerializedTestInput = JSON.parse(serializedEditorInput); - - return new EditorsObserverTestEditorInput(URI.parse(testInput.resource)); - } -} - suite('EditorsObserver', function () { let disposables: IDisposable[] = []; setup(() => { - disposables.push(Registry.as(EditorExtensions.EditorInputFactories).registerEditorInputFactory(TEST_SERIALIZABLE_EDITOR_INPUT_ID, EditorsObserverTestEditorInputFactory)); - disposables.push(Registry.as(Extensions.Editors).registerEditor(EditorDescriptor.create(TestEditorControl, TEST_EDITOR_ID, 'My Test Editor For Editors Observer'), [new SyncDescriptor(TestEditorInput), new SyncDescriptor(EditorsObserverTestEditorInput)])); + disposables.push(registerTestEditor(TEST_EDITOR_ID, [new SyncDescriptor(TestFileEditorInput)], TEST_SERIALIZABLE_EDITOR_INPUT_ID)); }); teardown(() => { @@ -139,7 +67,7 @@ suite('EditorsObserver', function () { assert.equal(currentEditorsMRU.length, 0); assert.equal(observerChangeListenerCalled, false); - const input1 = new EditorsObserverTestEditorInput(URI.parse('foo://bar1')); + const input1 = new TestFileEditorInput(URI.parse('foo://bar1'), TEST_SERIALIZABLE_EDITOR_INPUT_ID); await part.activeGroup.openEditor(input1, EditorOptions.create({ pinned: true })); @@ -149,8 +77,8 @@ suite('EditorsObserver', function () { assert.equal(currentEditorsMRU[0].editor, input1); assert.equal(observerChangeListenerCalled, true); - const input2 = new EditorsObserverTestEditorInput(URI.parse('foo://bar2')); - const input3 = new EditorsObserverTestEditorInput(URI.parse('foo://bar3')); + const input2 = new TestFileEditorInput(URI.parse('foo://bar2'), TEST_SERIALIZABLE_EDITOR_INPUT_ID); + const input3 = new TestFileEditorInput(URI.parse('foo://bar3'), TEST_SERIALIZABLE_EDITOR_INPUT_ID); await part.activeGroup.openEditor(input2, EditorOptions.create({ pinned: true })); await part.activeGroup.openEditor(input3, EditorOptions.create({ pinned: true })); @@ -204,7 +132,7 @@ suite('EditorsObserver', function () { const sideGroup = part.addGroup(rootGroup, GroupDirection.RIGHT); - const input1 = new EditorsObserverTestEditorInput(URI.parse('foo://bar1')); + const input1 = new TestFileEditorInput(URI.parse('foo://bar1'), TEST_SERIALIZABLE_EDITOR_INPUT_ID); await rootGroup.openEditor(input1, EditorOptions.create({ pinned: true, activation: EditorActivation.ACTIVATE })); await sideGroup.openEditor(input1, EditorOptions.create({ pinned: true, activation: EditorActivation.ACTIVATE })); @@ -227,7 +155,7 @@ suite('EditorsObserver', function () { // Opening an editor inactive should not change // the most recent editor, but rather put it behind - const input2 = new EditorsObserverTestEditorInput(URI.parse('foo://bar2')); + const input2 = new TestFileEditorInput(URI.parse('foo://bar2'), TEST_SERIALIZABLE_EDITOR_INPUT_ID); await rootGroup.openEditor(input2, EditorOptions.create({ inactive: true })); @@ -258,9 +186,9 @@ suite('EditorsObserver', function () { test('copy group', async () => { const [part, observer] = await createEditorObserver(); - const input1 = new EditorsObserverTestEditorInput(URI.parse('foo://bar1')); - const input2 = new EditorsObserverTestEditorInput(URI.parse('foo://bar2')); - const input3 = new EditorsObserverTestEditorInput(URI.parse('foo://bar3')); + const input1 = new TestFileEditorInput(URI.parse('foo://bar1'), TEST_SERIALIZABLE_EDITOR_INPUT_ID); + const input2 = new TestFileEditorInput(URI.parse('foo://bar2'), TEST_SERIALIZABLE_EDITOR_INPUT_ID); + const input3 = new TestFileEditorInput(URI.parse('foo://bar3'), TEST_SERIALIZABLE_EDITOR_INPUT_ID); const rootGroup = part.activeGroup; @@ -303,9 +231,9 @@ suite('EditorsObserver', function () { const rootGroup = part.activeGroup; - const input1 = new EditorsObserverTestEditorInput(URI.parse('foo://bar1')); - const input2 = new EditorsObserverTestEditorInput(URI.parse('foo://bar2')); - const input3 = new EditorsObserverTestEditorInput(URI.parse('foo://bar3')); + const input1 = new TestFileEditorInput(URI.parse('foo://bar1'), TEST_SERIALIZABLE_EDITOR_INPUT_ID); + const input2 = new TestFileEditorInput(URI.parse('foo://bar2'), TEST_SERIALIZABLE_EDITOR_INPUT_ID); + const input3 = new TestFileEditorInput(URI.parse('foo://bar3'), TEST_SERIALIZABLE_EDITOR_INPUT_ID); await rootGroup.openEditor(input1, EditorOptions.create({ pinned: true })); await rootGroup.openEditor(input2, EditorOptions.create({ pinned: true })); @@ -346,9 +274,9 @@ suite('EditorsObserver', function () { const rootGroup = part.activeGroup; - const input1 = new EditorsObserverTestEditorInput(URI.parse('foo://bar1')); - const input2 = new EditorsObserverTestEditorInput(URI.parse('foo://bar2')); - const input3 = new EditorsObserverTestEditorInput(URI.parse('foo://bar3')); + const input1 = new TestFileEditorInput(URI.parse('foo://bar1'), TEST_SERIALIZABLE_EDITOR_INPUT_ID); + const input2 = new TestFileEditorInput(URI.parse('foo://bar2'), TEST_SERIALIZABLE_EDITOR_INPUT_ID); + const input3 = new TestFileEditorInput(URI.parse('foo://bar3'), TEST_SERIALIZABLE_EDITOR_INPUT_ID); await rootGroup.openEditor(input1, EditorOptions.create({ pinned: true })); await rootGroup.openEditor(input2, EditorOptions.create({ pinned: true })); @@ -391,7 +319,7 @@ suite('EditorsObserver', function () { const rootGroup = part.activeGroup; - const input1 = new TestEditorInput(URI.parse('foo://bar1')); + const input1 = new TestFileEditorInput(URI.parse('foo://bar1'), TEST_EDITOR_INPUT_ID); await rootGroup.openEditor(input1, EditorOptions.create({ pinned: true })); @@ -425,10 +353,10 @@ suite('EditorsObserver', function () { const rootGroup = part.activeGroup; const sideGroup = part.addGroup(rootGroup, GroupDirection.RIGHT); - const input1 = new TestEditorInput(URI.parse('foo://bar1')); - const input2 = new TestEditorInput(URI.parse('foo://bar2')); - const input3 = new TestEditorInput(URI.parse('foo://bar3')); - const input4 = new TestEditorInput(URI.parse('foo://bar4')); + const input1 = new TestFileEditorInput(URI.parse('foo://bar1'), TEST_EDITOR_INPUT_ID); + const input2 = new TestFileEditorInput(URI.parse('foo://bar2'), TEST_EDITOR_INPUT_ID); + const input3 = new TestFileEditorInput(URI.parse('foo://bar3'), TEST_EDITOR_INPUT_ID); + const input4 = new TestFileEditorInput(URI.parse('foo://bar4'), TEST_EDITOR_INPUT_ID); await rootGroup.openEditor(input1, EditorOptions.create({ pinned: true })); await rootGroup.openEditor(input2, EditorOptions.create({ pinned: true })); @@ -452,7 +380,7 @@ suite('EditorsObserver', function () { assert.equal(rootGroup.isOpened(input3), false); assert.equal(rootGroup.isOpened(input4), true); - const input5 = new TestEditorInput(URI.parse('foo://bar5')); + const input5 = new TestFileEditorInput(URI.parse('foo://bar5'), TEST_EDITOR_INPUT_ID); await sideGroup.openEditor(input5, EditorOptions.create({ pinned: true })); assert.equal(rootGroup.count, 1); @@ -477,10 +405,10 @@ suite('EditorsObserver', function () { const rootGroup = part.activeGroup; const sideGroup = part.addGroup(rootGroup, GroupDirection.RIGHT); - const input1 = new TestEditorInput(URI.parse('foo://bar1')); - const input2 = new TestEditorInput(URI.parse('foo://bar2')); - const input3 = new TestEditorInput(URI.parse('foo://bar3')); - const input4 = new TestEditorInput(URI.parse('foo://bar4')); + const input1 = new TestFileEditorInput(URI.parse('foo://bar1'), TEST_EDITOR_INPUT_ID); + const input2 = new TestFileEditorInput(URI.parse('foo://bar2'), TEST_EDITOR_INPUT_ID); + const input3 = new TestFileEditorInput(URI.parse('foo://bar3'), TEST_EDITOR_INPUT_ID); + const input4 = new TestFileEditorInput(URI.parse('foo://bar4'), TEST_EDITOR_INPUT_ID); await rootGroup.openEditor(input1, EditorOptions.create({ pinned: true })); await rootGroup.openEditor(input2, EditorOptions.create({ pinned: true })); diff --git a/src/vs/workbench/services/history/test/browser/history.test.ts b/src/vs/workbench/services/history/test/browser/history.test.ts index 77715cbc34a..e2e5b97413a 100644 --- a/src/vs/workbench/services/history/test/browser/history.test.ts +++ b/src/vs/workbench/services/history/test/browser/history.test.ts @@ -4,21 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { EditorOptions, EditorInput, IEditorInputFactoryRegistry, Extensions as EditorExtensions, IEditorInputFactory, IFileEditorInput } from 'vs/workbench/common/editor'; +import { EditorOptions } from 'vs/workbench/common/editor'; import { URI } from 'vs/base/common/uri'; -import { workbenchInstantiationService, TestStorageService } from 'vs/workbench/test/browser/workbenchTestServices'; -import { Registry } from 'vs/platform/registry/common/platform'; +import { workbenchInstantiationService, TestFileEditorInput, registerTestEditor } from 'vs/workbench/test/browser/workbenchTestServices'; import { EditorPart } from 'vs/workbench/browser/parts/editor/editorPart'; -import { IEditorRegistry, EditorDescriptor, Extensions } from 'vs/workbench/browser/editor'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { IEditorGroupsService, GroupDirection } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { IEditorModel } from 'vs/platform/editor/common/editor'; -import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; -import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; -import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; -import { CancellationToken } from 'vs/base/common/cancellation'; import { HistoryService } from 'vs/workbench/services/history/browser/history'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { EditorService } from 'vs/workbench/services/editor/browser/editorService'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; @@ -27,68 +19,6 @@ import { timeout } from 'vs/base/common/async'; const TEST_EDITOR_ID = 'MyTestEditorForEditorHistory'; const TEST_EDITOR_INPUT_ID = 'testEditorInputForHistoyService'; -const TEST_SERIALIZABLE_EDITOR_INPUT_ID = 'testSerializableEditorInputForHistoyService'; - -class TestEditorControl extends BaseEditor { - - constructor() { super(TEST_EDITOR_ID, NullTelemetryService, new TestThemeService(), new TestStorageService()); } - - async setInput(input: EditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise { - super.setInput(input, options, token); - - await input.resolve(); - } - - getId(): string { return TEST_EDITOR_ID; } - layout(): void { } - createEditor(): any { } -} - -class TestEditorInput extends EditorInput implements IFileEditorInput { - - constructor(public resource: URI) { super(); } - - getTypeId() { return TEST_EDITOR_INPUT_ID; } - resolve(): Promise { return Promise.resolve(null); } - matches(other: TestEditorInput): boolean { return other && this.resource.toString() === other.resource.toString() && other instanceof TestEditorInput; } - setEncoding(encoding: string) { } - getEncoding() { return undefined; } - setPreferredEncoding(encoding: string) { } - setMode(mode: string) { } - setPreferredMode(mode: string) { } - setForceOpenAsBinary(): void { } - isResolved(): boolean { return false; } -} - -class HistoryTestEditorInput extends TestEditorInput { - getTypeId() { return TEST_SERIALIZABLE_EDITOR_INPUT_ID; } -} - -interface ISerializedTestInput { - resource: string; -} - -class HistoryTestEditorInputFactory implements IEditorInputFactory { - - canSerialize(editorInput: EditorInput): boolean { - return true; - } - - serialize(editorInput: EditorInput): string { - let testEditorInput = editorInput; - let testInput: ISerializedTestInput = { - resource: testEditorInput.resource.toString() - }; - - return JSON.stringify(testInput); - } - - deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): EditorInput { - let testInput: ISerializedTestInput = JSON.parse(serializedEditorInput); - - return new HistoryTestEditorInput(URI.parse(testInput.resource)); - } -} async function createServices(): Promise<[EditorPart, HistoryService, EditorService]> { const instantiationService = workbenchInstantiationService(); @@ -115,8 +45,7 @@ suite('HistoryService', function () { let disposables: IDisposable[] = []; setup(() => { - disposables.push(Registry.as(EditorExtensions.EditorInputFactories).registerEditorInputFactory(TEST_SERIALIZABLE_EDITOR_INPUT_ID, HistoryTestEditorInputFactory)); - disposables.push(Registry.as(Extensions.Editors).registerEditor(EditorDescriptor.create(TestEditorControl, TEST_EDITOR_ID, 'My Test Editor For History Editor Service'), [new SyncDescriptor(TestEditorInput), new SyncDescriptor(HistoryTestEditorInput)])); + disposables.push(registerTestEditor(TEST_EDITOR_ID, [new SyncDescriptor(TestFileEditorInput)])); }); teardown(() => { @@ -127,11 +56,11 @@ suite('HistoryService', function () { test('back / forward', async () => { const [part, historyService] = await createServices(); - const input1 = new TestEditorInput(URI.parse('foo://bar1')); + const input1 = new TestFileEditorInput(URI.parse('foo://bar1'), TEST_EDITOR_INPUT_ID); await part.activeGroup.openEditor(input1, EditorOptions.create({ pinned: true })); assert.equal(part.activeGroup.activeEditor, input1); - const input2 = new TestEditorInput(URI.parse('foo://bar2')); + const input2 = new TestFileEditorInput(URI.parse('foo://bar2'), TEST_EDITOR_INPUT_ID); await part.activeGroup.openEditor(input2, EditorOptions.create({ pinned: true })); assert.equal(part.activeGroup.activeEditor, input2); @@ -150,10 +79,10 @@ suite('HistoryService', function () { let history = historyService.getHistory(); assert.equal(history.length, 0); - const input1 = new TestEditorInput(URI.parse('foo://bar1')); + const input1 = new TestFileEditorInput(URI.parse('foo://bar1'), TEST_EDITOR_INPUT_ID); await part.activeGroup.openEditor(input1, EditorOptions.create({ pinned: true })); - const input2 = new TestEditorInput(URI.parse('foo://bar2')); + const input2 = new TestFileEditorInput(URI.parse('foo://bar2'), TEST_EDITOR_INPUT_ID); await part.activeGroup.openEditor(input2, EditorOptions.create({ pinned: true })); history = historyService.getHistory(); @@ -172,7 +101,7 @@ suite('HistoryService', function () { assert.ok(!historyService.getLastActiveFile('foo')); - const input1 = new TestEditorInput(URI.parse('foo://bar1')); + const input1 = new TestFileEditorInput(URI.parse('foo://bar1'), TEST_EDITOR_INPUT_ID); await part.activeGroup.openEditor(input1, EditorOptions.create({ pinned: true })); assert.equal(historyService.getLastActiveFile('foo')?.toString(), input1.resource.toString()); @@ -183,8 +112,8 @@ suite('HistoryService', function () { test('open next/previous recently used editor (single group)', async () => { const [part, historyService] = await createServices(); - const input1 = new TestEditorInput(URI.parse('foo://bar1')); - const input2 = new TestEditorInput(URI.parse('foo://bar2')); + const input1 = new TestFileEditorInput(URI.parse('foo://bar1'), TEST_EDITOR_INPUT_ID); + const input2 = new TestFileEditorInput(URI.parse('foo://bar2'), TEST_EDITOR_INPUT_ID); await part.activeGroup.openEditor(input1, EditorOptions.create({ pinned: true })); assert.equal(part.activeGroup.activeEditor, input1); @@ -211,8 +140,8 @@ suite('HistoryService', function () { const [part, historyService] = await createServices(); const rootGroup = part.activeGroup; - const input1 = new TestEditorInput(URI.parse('foo://bar1')); - const input2 = new TestEditorInput(URI.parse('foo://bar2')); + const input1 = new TestFileEditorInput(URI.parse('foo://bar1'), TEST_EDITOR_INPUT_ID); + const input2 = new TestFileEditorInput(URI.parse('foo://bar2'), TEST_EDITOR_INPUT_ID); const sideGroup = part.addGroup(rootGroup, GroupDirection.RIGHT); @@ -233,10 +162,10 @@ suite('HistoryService', function () { test('open next/previous recently is reset when other input opens', async () => { const [part, historyService] = await createServices(); - const input1 = new TestEditorInput(URI.parse('foo://bar1')); - const input2 = new TestEditorInput(URI.parse('foo://bar2')); - const input3 = new TestEditorInput(URI.parse('foo://bar3')); - const input4 = new TestEditorInput(URI.parse('foo://bar4')); + const input1 = new TestFileEditorInput(URI.parse('foo://bar1'), TEST_EDITOR_INPUT_ID); + const input2 = new TestFileEditorInput(URI.parse('foo://bar2'), TEST_EDITOR_INPUT_ID); + const input3 = new TestFileEditorInput(URI.parse('foo://bar3'), TEST_EDITOR_INPUT_ID); + const input4 = new TestFileEditorInput(URI.parse('foo://bar4'), TEST_EDITOR_INPUT_ID); await part.activeGroup.openEditor(input1, EditorOptions.create({ pinned: true })); await part.activeGroup.openEditor(input2, EditorOptions.create({ pinned: true })); diff --git a/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts b/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts index f1847f3189d..7140b161af9 100644 --- a/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts +++ b/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts @@ -7,50 +7,31 @@ import * as assert from 'assert'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { EncodingMode } from 'vs/workbench/common/editor'; import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; -import { ITextFileService, TextFileEditorModelState, snapshotToString } from 'vs/workbench/services/textfile/common/textfiles'; -import { createFileInput, TestFileService, TestTextFileService, workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices'; +import { TextFileEditorModelState, snapshotToString } from 'vs/workbench/services/textfile/common/textfiles'; +import { createFileInput, workbenchInstantiationService, TestServiceAccessor, TestReadonlyTextFileEditorModel } from 'vs/workbench/test/browser/workbenchTestServices'; import { toResource } from 'vs/base/test/common/utils'; import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager'; -import { FileOperationResult, FileOperationError, IFileService } from 'vs/platform/files/common/files'; -import { IModelService } from 'vs/editor/common/services/modelService'; +import { FileOperationResult, FileOperationError } from 'vs/platform/files/common/files'; import { timeout } from 'vs/base/common/async'; import { ModesRegistry } from 'vs/editor/common/modes/modesRegistry'; import { assertIsDefined } from 'vs/base/common/types'; -import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { createTextBufferFactory } from 'vs/editor/common/model/textModel'; -class ServiceAccessor { - constructor( - @ITextFileService public readonly textFileService: TestTextFileService, - @IModelService public readonly modelService: IModelService, - @IFileService public readonly fileService: TestFileService, - @IWorkingCopyService public readonly workingCopyService: IWorkingCopyService - ) { - } -} - function getLastModifiedTime(model: TextFileEditorModel): number { const stat = model.getStat(); return stat ? stat.mtime : -1; } -class TestTextFileEditorModel extends TextFileEditorModel { - - isReadonly(): boolean { - return true; - } -} - suite('Files - TextFileEditorModel', () => { let instantiationService: IInstantiationService; - let accessor: ServiceAccessor; + let accessor: TestServiceAccessor; let content: string; setup(() => { instantiationService = workbenchInstantiationService(); - accessor = instantiationService.createInstance(ServiceAccessor); + accessor = instantiationService.createInstance(TestServiceAccessor); content = accessor.fileService.getContent(); }); @@ -107,7 +88,7 @@ suite('Files - TextFileEditorModel', () => { assert.equal(accessor.workingCopyService.isDirty(model.resource), true); let savedEvent = false; - model.onDidSave(e => savedEvent = true); + model.onDidSave(() => savedEvent = true); let workingCopyEvent = false; accessor.workingCopyService.onDidChangeDirty(e => { @@ -139,7 +120,7 @@ suite('Files - TextFileEditorModel', () => { await model.load(); let savedEvent = false; - model.onDidSave(e => savedEvent = true); + model.onDidSave(() => savedEvent = true); let workingCopyEvent = false; accessor.workingCopyService.onDidChangeDirty(e => { @@ -165,7 +146,7 @@ suite('Files - TextFileEditorModel', () => { model.updateTextEditorModel(createTextBufferFactory('bar')); let saveErrorEvent = false; - model.onDidSaveError(e => saveErrorEvent = true); + model.onDidSaveError(() => saveErrorEvent = true); accessor.fileService.writeShouldThrowError = new Error('failed to write'); try { @@ -195,7 +176,7 @@ suite('Files - TextFileEditorModel', () => { model.updateTextEditorModel(createTextBufferFactory('bar')); let saveErrorEvent = false; - model.onDidSaveError(e => saveErrorEvent = true); + model.onDidSaveError(() => saveErrorEvent = true); accessor.fileService.writeShouldThrowError = new FileOperationError('save conflict', FileOperationResult.FILE_MODIFIED_SINCE); try { @@ -221,7 +202,7 @@ suite('Files - TextFileEditorModel', () => { const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); let encodingEvent = false; - model.onDidChangeEncoding(e => encodingEvent = true); + model.onDidChangeEncoding(() => encodingEvent = true); model.setEncoding('utf8', EncodingMode.Encode); // no-op assert.equal(getLastModifiedTime(model), -1); @@ -276,8 +257,8 @@ suite('Files - TextFileEditorModel', () => { const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index.txt'), 'utf8', undefined); assert.ok(model.hasState(TextFileEditorModelState.SAVED)); - model.onDidSave(e => assert.fail()); - model.onDidChangeDirty(e => assert.fail()); + model.onDidSave(() => assert.fail()); + model.onDidChangeDirty(() => assert.fail()); await model.load(); assert.ok(model.isResolved()); @@ -303,7 +284,7 @@ suite('Files - TextFileEditorModel', () => { const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); - model.onDidRevert(e => eventCounter++); + model.onDidRevert(() => eventCounter++); let workingCopyEvent = false; accessor.workingCopyService.onDidChangeDirty(e => { @@ -336,7 +317,7 @@ suite('Files - TextFileEditorModel', () => { const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); - model.onDidRevert(e => eventCounter++); + model.onDidRevert(() => eventCounter++); let workingCopyEvent = false; accessor.workingCopyService.onDidChangeDirty(e => { @@ -402,7 +383,7 @@ suite('Files - TextFileEditorModel', () => { await model.revert({ soft: true }); assert.ok(!model.isDirty()); - model.onDidChangeDirty(e => eventCounter++); + model.onDidChangeDirty(() => eventCounter++); let workingCopyEvent = false; accessor.workingCopyService.onDidChangeDirty(e => { @@ -431,7 +412,7 @@ suite('Files - TextFileEditorModel', () => { } }); - const model = instantiationService.createInstance(TestTextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); + const model = instantiationService.createInstance(TestReadonlyTextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); await model.load(); model.updateTextEditorModel(createTextBufferFactory('foo')); @@ -507,7 +488,7 @@ suite('Files - TextFileEditorModel', () => { let eventCounter = 0; const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); - model.onDidSave(e => { + model.onDidSave(() => { assert.equal(snapshotToString(model.createSnapshot()!), eventCounter === 1 ? 'bar' : 'foobar'); assert.ok(!model.isDirty()); eventCounter++; @@ -544,7 +525,7 @@ suite('Files - TextFileEditorModel', () => { const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); const participant = accessor.textFileService.files.addSaveParticipant({ - participate: async model => { + participate: async () => { eventCounter++; } }); @@ -563,7 +544,7 @@ suite('Files - TextFileEditorModel', () => { let eventCounter = 0; const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); - model.onDidSave(e => { + model.onDidSave(() => { assert.ok(!model.isDirty()); eventCounter++; }); @@ -595,7 +576,7 @@ suite('Files - TextFileEditorModel', () => { const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); const participant = accessor.textFileService.files.addSaveParticipant({ - participate: async model => { + participate: async () => { new Error('boom'); } }); diff --git a/src/vs/workbench/services/textfile/test/browser/textFileEditorModelManager.test.ts b/src/vs/workbench/services/textfile/test/browser/textFileEditorModelManager.test.ts index 89e8379ac1c..ec0904baeb6 100644 --- a/src/vs/workbench/services/textfile/test/browser/textFileEditorModelManager.test.ts +++ b/src/vs/workbench/services/textfile/test/browser/textFileEditorModelManager.test.ts @@ -7,31 +7,22 @@ import * as assert from 'assert'; import { URI } from 'vs/base/common/uri'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager'; -import { workbenchInstantiationService, TestFileService } from 'vs/workbench/test/browser/workbenchTestServices'; +import { workbenchInstantiationService, TestServiceAccessor } from 'vs/workbench/test/browser/workbenchTestServices'; import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; -import { IFileService, FileChangesEvent, FileChangeType } from 'vs/platform/files/common/files'; -import { IModelService } from 'vs/editor/common/services/modelService'; +import { FileChangesEvent, FileChangeType } from 'vs/platform/files/common/files'; import { toResource } from 'vs/base/test/common/utils'; import { ModesRegistry, PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry'; import { ITextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles'; import { createTextBufferFactory } from 'vs/editor/common/model/textModel'; -class ServiceAccessor { - constructor( - @IFileService public fileService: TestFileService, - @IModelService public modelService: IModelService - ) { - } -} - suite('Files - TextFileEditorModelManager', () => { let instantiationService: IInstantiationService; - let accessor: ServiceAccessor; + let accessor: TestServiceAccessor; setup(() => { instantiationService = workbenchInstantiationService(); - accessor = instantiationService.createInstance(ServiceAccessor); + accessor = instantiationService.createInstance(TestServiceAccessor); }); test('add, remove, clear, get, getAll', function () { diff --git a/src/vs/workbench/services/textfile/test/browser/textFileService.test.ts b/src/vs/workbench/services/textfile/test/browser/textFileService.test.ts index 2279232737c..d90c11ca710 100644 --- a/src/vs/workbench/services/textfile/test/browser/textFileService.test.ts +++ b/src/vs/workbench/services/textfile/test/browser/textFileService.test.ts @@ -4,44 +4,21 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; -import { TestLifecycleService, TestContextService, TestFileService, TestFilesConfigurationService, TestFileDialogService, TestTextFileService, workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices'; +import { workbenchInstantiationService, TestServiceAccessor } from 'vs/workbench/test/browser/workbenchTestServices'; import { toResource } from 'vs/base/test/common/utils'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; -import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; -import { IFileService } from 'vs/platform/files/common/files'; import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { IModelService } from 'vs/editor/common/services/modelService'; -import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; -import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; -import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService'; - -class ServiceAccessor { - constructor( - @ILifecycleService public lifecycleService: TestLifecycleService, - @ITextFileService public textFileService: TestTextFileService, - @IWorkingCopyFileService public workingCopyFileService: IWorkingCopyFileService, - @IFilesConfigurationService public filesConfigurationService: TestFilesConfigurationService, - @IWorkspaceContextService public contextService: TestContextService, - @IModelService public modelService: ModelServiceImpl, - @IFileService public fileService: TestFileService, - @IFileDialogService public fileDialogService: TestFileDialogService - ) { - } -} suite('Files - TextFileService', () => { let instantiationService: IInstantiationService; let model: TextFileEditorModel; - let accessor: ServiceAccessor; + let accessor: TestServiceAccessor; setup(() => { instantiationService = workbenchInstantiationService(); - accessor = instantiationService.createInstance(ServiceAccessor); + accessor = instantiationService.createInstance(TestServiceAccessor); }); teardown(() => { diff --git a/src/vs/workbench/services/textfile/test/electron-browser/textFileService.io.test.ts b/src/vs/workbench/services/textfile/test/electron-browser/textFileService.io.test.ts index d50d6a3bf5e..22504c3b467 100644 --- a/src/vs/workbench/services/textfile/test/electron-browser/textFileService.io.test.ts +++ b/src/vs/workbench/services/textfile/test/electron-browser/textFileService.io.test.ts @@ -2,6 +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 * as assert from 'assert'; import { URI } from 'vs/base/common/uri'; import { ITextFileService, snapshotToString, TextFileOperationResult, TextFileOperationError } from 'vs/workbench/services/textfile/common/textfiles'; @@ -20,57 +21,23 @@ import { generateUuid } from 'vs/base/common/uuid'; import { join, basename } from 'vs/base/common/path'; import { getPathFromAmdModule } from 'vs/base/common/amd'; import { UTF16be, UTF16le, UTF8_with_bom, UTF8 } from 'vs/base/node/encoding'; -import { NativeTextFileService, EncodingOracle, IEncodingOverride } from 'vs/workbench/services/textfile/electron-browser/nativeTextFileService'; import { DefaultEndOfLine, ITextSnapshot } from 'vs/editor/common/model'; import { TextModel } from 'vs/editor/common/model/textModel'; import { isWindows } from 'vs/base/common/platform'; import { readFileSync, statSync } from 'fs'; import { detectEncodingByBOM } from 'vs/base/test/node/encoding/encoding.test'; -import { workbenchInstantiationService, TestTextFileService } from 'vs/workbench/test/electron-browser/workbenchTestServices'; - -class ServiceAccessor { - constructor( - @ITextFileService public textFileService: TestTextFileService - ) { - } -} - -class TestNativeTextFileService extends NativeTextFileService { - - private _testEncoding: TestEncodingOracle | undefined; - get encoding(): TestEncodingOracle { - if (!this._testEncoding) { - this._testEncoding = this._register(this.instantiationService.createInstance(TestEncodingOracle)); - } - - return this._testEncoding; - } -} - -class TestEncodingOracle extends EncodingOracle { - - protected get encodingOverrides(): IEncodingOverride[] { - return [ - { extension: 'utf16le', encoding: UTF16le }, - { extension: 'utf16be', encoding: UTF16be }, - { extension: 'utf8bom', encoding: UTF8_with_bom } - ]; - } - - protected set encodingOverrides(overrides: IEncodingOverride[]) { } -} +import { workbenchInstantiationService, TestNativeTextFileServiceWithEncodingOverrides } from 'vs/workbench/test/electron-browser/workbenchTestServices'; suite('Files - TextFileService i/o', () => { const parentDir = getRandomTestPath(tmpdir(), 'vsctests', 'textfileservice'); - let accessor: ServiceAccessor; const disposables = new DisposableStore(); + let service: ITextFileService; let testDir: string; setup(async () => { const instantiationService = workbenchInstantiationService(); - accessor = instantiationService.createInstance(ServiceAccessor); const logService = new NullLogService(); const fileService = new FileService(logService); @@ -82,7 +49,7 @@ suite('Files - TextFileService i/o', () => { const collection = new ServiceCollection(); collection.set(IFileService, fileService); - service = instantiationService.createChild(collection).createInstance(TestNativeTextFileService); + service = instantiationService.createChild(collection).createInstance(TestNativeTextFileServiceWithEncodingOverrides); const id = generateUuid(); testDir = join(parentDir, id); @@ -92,7 +59,7 @@ suite('Files - TextFileService i/o', () => { }); teardown(async () => { - (accessor.textFileService.files).dispose(); + (service.files).dispose(); disposables.clear(); diff --git a/src/vs/workbench/services/textmodelResolver/test/browser/textModelResolverService.test.ts b/src/vs/workbench/services/textmodelResolver/test/browser/textModelResolverService.test.ts index b322438b8d4..d8ca80f38f4 100644 --- a/src/vs/workbench/services/textmodelResolver/test/browser/textModelResolverService.test.ts +++ b/src/vs/workbench/services/textmodelResolver/test/browser/textModelResolverService.test.ts @@ -9,39 +9,24 @@ import { URI } from 'vs/base/common/uri'; import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; import { ResourceEditorModel } from 'vs/workbench/common/editor/resourceEditorModel'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices'; +import { workbenchInstantiationService, TestServiceAccessor } from 'vs/workbench/test/browser/workbenchTestServices'; import { toResource } from 'vs/base/test/common/utils'; -import { ITextModelService } from 'vs/editor/common/services/resolverService'; -import { IModelService } from 'vs/editor/common/services/modelService'; -import { IModeService } from 'vs/editor/common/services/modeService'; import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; -import { ITextFileService, snapshotToString } from 'vs/workbench/services/textfile/common/textfiles'; -import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService'; +import { snapshotToString } from 'vs/workbench/services/textfile/common/textfiles'; import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager'; import { Event } from 'vs/base/common/event'; import { timeout } from 'vs/base/common/async'; import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput'; -class ServiceAccessor { - constructor( - @ITextModelService public textModelResolverService: ITextModelService, - @IModelService public modelService: IModelService, - @IModeService public modeService: IModeService, - @ITextFileService public textFileService: ITextFileService, - @IUntitledTextEditorService public untitledTextEditorService: IUntitledTextEditorService - ) { - } -} - suite('Workbench - TextModelResolverService', () => { let instantiationService: IInstantiationService; - let accessor: ServiceAccessor; + let accessor: TestServiceAccessor; let model: TextFileEditorModel; setup(() => { instantiationService = workbenchInstantiationService(); - accessor = instantiationService.createInstance(ServiceAccessor); + accessor = instantiationService.createInstance(TestServiceAccessor); }); teardown(() => { diff --git a/src/vs/workbench/services/untitled/test/browser/untitledTextEditor.test.ts b/src/vs/workbench/services/untitled/test/browser/untitledTextEditor.test.ts index 7234536f6e9..1e44a563f4c 100644 --- a/src/vs/workbench/services/untitled/test/browser/untitledTextEditor.test.ts +++ b/src/vs/workbench/services/untitled/test/browser/untitledTextEditor.test.ts @@ -8,37 +8,22 @@ import * as assert from 'assert'; import { join } from 'vs/base/common/path'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IUntitledTextEditorService, UntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; -import { workbenchInstantiationService, TestEditorService } from 'vs/workbench/test/browser/workbenchTestServices'; -import { IModeService } from 'vs/editor/common/services/modeService'; -import { ModeServiceImpl } from 'vs/editor/common/services/modeServiceImpl'; +import { workbenchInstantiationService, TestServiceAccessor } from 'vs/workbench/test/browser/workbenchTestServices'; import { snapshotToString } from 'vs/workbench/services/textfile/common/textfiles'; import { ModesRegistry, PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry'; -import { IWorkingCopyService, IWorkingCopy } from 'vs/workbench/services/workingCopy/common/workingCopyService'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IWorkingCopy } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; import { Range } from 'vs/editor/common/core/range'; import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput'; -class ServiceAccessor { - constructor( - @IUntitledTextEditorService public readonly untitledTextEditorService: IUntitledTextEditorService, - @IEditorService public readonly editorService: TestEditorService, - @IWorkingCopyService public readonly workingCopyService: IWorkingCopyService, - @IModeService public readonly modeService: ModeServiceImpl, - @IConfigurationService public readonly testConfigurationService: TestConfigurationService - ) { } -} - suite('Untitled text editors', () => { let instantiationService: IInstantiationService; - let accessor: ServiceAccessor; + let accessor: TestServiceAccessor; setup(() => { instantiationService = workbenchInstantiationService(); - accessor = instantiationService.createInstance(ServiceAccessor); + accessor = instantiationService.createInstance(TestServiceAccessor); }); teardown(() => { diff --git a/src/vs/workbench/services/workingCopy/test/browser/workingCopyFileService.test.ts b/src/vs/workbench/services/workingCopy/test/browser/workingCopyFileService.test.ts index ffcf97d9edc..9d05c8dd213 100644 --- a/src/vs/workbench/services/workingCopy/test/browser/workingCopyFileService.test.ts +++ b/src/vs/workbench/services/workingCopy/test/browser/workingCopyFileService.test.ts @@ -8,31 +8,19 @@ import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textF import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { toResource } from 'vs/base/test/common/utils'; -import { workbenchInstantiationService, TestTextFileService } from 'vs/workbench/test/browser/workbenchTestServices'; -import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; -import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService'; -import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; +import { workbenchInstantiationService, TestServiceAccessor } from 'vs/workbench/test/browser/workbenchTestServices'; import { URI } from 'vs/base/common/uri'; import { FileOperation } from 'vs/platform/files/common/files'; -class ServiceAccessor { - constructor( - @ITextFileService public textFileService: TestTextFileService, - @IWorkingCopyFileService public workingCopyFileService: IWorkingCopyFileService, - @IWorkingCopyService public workingCopyService: IWorkingCopyService - ) { - } -} - suite('WorkingCopyFileService', () => { let instantiationService: IInstantiationService; let model: TextFileEditorModel; - let accessor: ServiceAccessor; + let accessor: TestServiceAccessor; setup(() => { instantiationService = workbenchInstantiationService(); - accessor = instantiationService.createInstance(ServiceAccessor); + accessor = instantiationService.createInstance(TestServiceAccessor); }); teardown(() => { diff --git a/src/vs/workbench/test/browser/parts/editor/editor.test.ts b/src/vs/workbench/test/browser/parts/editor/editor.test.ts index d86598da7ff..96f97d1ad98 100644 --- a/src/vs/workbench/test/browser/parts/editor/editor.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/editor.test.ts @@ -4,43 +4,22 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { EditorInput, toResource, SideBySideEditor } from 'vs/workbench/common/editor'; +import { toResource, SideBySideEditor } from 'vs/workbench/common/editor'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; -import { IEditorModel } from 'vs/platform/editor/common/editor'; import { URI } from 'vs/base/common/uri'; -import { IUntitledTextEditorService, UntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices'; +import { workbenchInstantiationService, TestServiceAccessor, TestEditorInput } from 'vs/workbench/test/browser/workbenchTestServices'; import { Schemas } from 'vs/base/common/network'; import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput'; -class ServiceAccessor { - constructor(@IUntitledTextEditorService public untitledTextEditorService: UntitledTextEditorService) { } -} - -class FileEditorInput extends EditorInput { - - constructor(public resource: URI) { - super(); - } - - getTypeId(): string { - return 'editorResourceFileTest'; - } - - resolve(): Promise { - return Promise.resolve(null); - } -} - suite('Workbench editor', () => { let instantiationService: IInstantiationService; - let accessor: ServiceAccessor; + let accessor: TestServiceAccessor; setup(() => { instantiationService = workbenchInstantiationService(); - accessor = instantiationService.createInstance(ServiceAccessor); + accessor = instantiationService.createInstance(TestServiceAccessor); }); teardown(() => { @@ -60,7 +39,7 @@ suite('Workbench editor', () => { assert.equal(toResource(untitled, { filterByScheme: [Schemas.file, Schemas.untitled] })!.toString(), untitled.resource.toString()); assert.ok(!toResource(untitled, { filterByScheme: Schemas.file })); - const file = new FileEditorInput(URI.file('/some/path.txt')); + const file = new TestEditorInput(URI.file('/some/path.txt'), 'editorResourceFileTest'); assert.equal(toResource(file)!.toString(), file.resource.toString()); assert.equal(toResource(file, { supportSideBySide: SideBySideEditor.MASTER })!.toString(), file.resource.toString()); diff --git a/src/vs/workbench/test/browser/parts/editor/editorDiffModel.test.ts b/src/vs/workbench/test/browser/parts/editor/editorDiffModel.test.ts index 369db0b1abb..9de4ac90c2e 100644 --- a/src/vs/workbench/test/browser/parts/editor/editorDiffModel.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/editorDiffModel.test.ts @@ -6,31 +6,19 @@ import * as assert from 'assert'; import { TextDiffEditorModel } from 'vs/workbench/common/editor/textDiffEditorModel'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; -import { IModelService } from 'vs/editor/common/services/modelService'; -import { IModeService } from 'vs/editor/common/services/modeService'; import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; import { URI } from 'vs/base/common/uri'; -import { ITextModelService } from 'vs/editor/common/services/resolverService'; -import { workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices'; +import { workbenchInstantiationService, TestServiceAccessor } from 'vs/workbench/test/browser/workbenchTestServices'; import { ITextModel } from 'vs/editor/common/model'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -class ServiceAccessor { - constructor( - @ITextModelService public textModelResolverService: ITextModelService, - @IModelService public modelService: IModelService, - @IModeService public modeService: IModeService, - ) { - } -} - suite('Workbench editor model', () => { let instantiationService: IInstantiationService; - let accessor: ServiceAccessor; + let accessor: TestServiceAccessor; setup(() => { instantiationService = workbenchInstantiationService(); - accessor = instantiationService.createInstance(ServiceAccessor); + accessor = instantiationService.createInstance(TestServiceAccessor); }); test('TextDiffEditorModel', async () => { diff --git a/src/vs/workbench/test/browser/parts/editor/resourceEditorInput.test.ts b/src/vs/workbench/test/browser/parts/editor/resourceEditorInput.test.ts index 2eaede4adbc..0593d4a5fd2 100644 --- a/src/vs/workbench/test/browser/parts/editor/resourceEditorInput.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/resourceEditorInput.test.ts @@ -8,26 +8,17 @@ import { URI } from 'vs/base/common/uri'; import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; import { ResourceEditorModel } from 'vs/workbench/common/editor/resourceEditorModel'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices'; -import { IModelService } from 'vs/editor/common/services/modelService'; -import { IModeService } from 'vs/editor/common/services/modeService'; +import { workbenchInstantiationService, TestServiceAccessor } from 'vs/workbench/test/browser/workbenchTestServices'; import { snapshotToString } from 'vs/workbench/services/textfile/common/textfiles'; import { ModesRegistry, PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry'; -class ServiceAccessor { - constructor( - @IModelService public modelService: IModelService, - @IModeService public modeService: IModeService - ) { } -} - suite('Resource text editors', () => { let instantiationService: IInstantiationService; - let accessor: ServiceAccessor; + let accessor: TestServiceAccessor; setup(() => { instantiationService = workbenchInstantiationService(); - accessor = instantiationService.createInstance(ServiceAccessor); + accessor = instantiationService.createInstance(TestServiceAccessor); }); test('basics', async () => { diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index 16dac785423..ec6a2da19a4 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -10,7 +10,7 @@ import * as resources from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; -import { IEditorInputWithOptions, CloseDirection, IEditorIdentifier, IUntitledTextResourceInput, IResourceDiffInput, IResourceSideBySideInput, IEditorInput, IEditor, IEditorCloseEvent, IEditorPartOptions, IRevertOptions, GroupIdentifier, EditorInput, EditorOptions, EditorsOrder } from 'vs/workbench/common/editor'; +import { IEditorInputWithOptions, CloseDirection, IEditorIdentifier, IUntitledTextResourceInput, IResourceDiffInput, IResourceSideBySideInput, IEditorInput, IEditor, IEditorCloseEvent, IEditorPartOptions, IRevertOptions, GroupIdentifier, EditorInput, EditorOptions, EditorsOrder, IFileEditorInput, IEditorInputFactoryRegistry, IEditorInputFactory, Extensions as EditorExtensions, ISaveOptions, IMoveResult } from 'vs/workbench/common/editor'; import { IEditorOpeningEvent, EditorServiceImpl, IEditorGroupView, IEditorGroupsAccessor } from 'vs/workbench/browser/parts/editor/editor'; import { Event, Emitter } from 'vs/base/common/event'; import Severity from 'vs/base/common/severity'; @@ -19,7 +19,7 @@ import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configur import { IWorkbenchLayoutService, Parts, Position as PartPosition } from 'vs/workbench/services/layout/browser/layoutService'; import { TextModelResolverService } from 'vs/workbench/services/textmodelResolver/common/textModelResolverService'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; -import { IEditorOptions, IResourceInput } from 'vs/platform/editor/common/editor'; +import { IEditorOptions, IResourceInput, IEditorModel } from 'vs/platform/editor/common/editor'; import { IUntitledTextEditorService, UntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { ILifecycleService, BeforeShutdownEvent, ShutdownReason, StartupKind, LifecyclePhase, WillShutdownEvent } from 'vs/platform/lifecycle/common/lifecycle'; @@ -51,11 +51,12 @@ import { TestNotificationService } from 'vs/platform/notification/test/common/te import { IExtensionService, NullExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IDecorationsService, IResourceDecorationChangeEvent, IDecoration, IDecorationData, IDecorationsProvider } from 'vs/workbench/services/decorations/browser/decorations'; -import { IDisposable, toDisposable, Disposable } from 'vs/base/common/lifecycle'; +import { IDisposable, toDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { IEditorGroupsService, IEditorGroup, GroupsOrder, GroupsArrangement, GroupDirection, IAddGroupOptions, IMergeGroupOptions, IMoveEditorOptions, ICopyEditorOptions, IEditorReplacement, IGroupChangeEvent, IFindGroupScope, EditorGroupLayout, ICloseEditorOptions } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService, IOpenEditorOverrideHandler, IVisibleEditor, ISaveEditorsOptions, IRevertAllEditorsOptions, IResourceEditor } from 'vs/workbench/services/editor/common/editorService'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser'; +import { IEditorRegistry, EditorDescriptor, Extensions } from 'vs/workbench/browser/editor'; import { IDecorationRenderOptions } from 'vs/editor/common/editorCommon'; import { EditorGroup } from 'vs/workbench/common/editor/editorGroup'; import { Dimension } from 'vs/base/browser/dom'; @@ -93,6 +94,11 @@ import { IProgressService, IProgressOptions, IProgressWindowOptions, IProgressNo import { IWorkingCopyFileService, WorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService'; import { UndoRedoService } from 'vs/platform/undoRedo/common/undoRedoService'; import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; +import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; export import TestTextResourcePropertiesService = CommonWorkbenchTestServices.TestTextResourcePropertiesService; export import TestContextService = CommonWorkbenchTestServices.TestContextService; @@ -107,6 +113,78 @@ export interface ITestInstantiationService extends IInstantiationService { stub(service: ServiceIdentifier, ctor: any): T; } +export function workbenchInstantiationService(overrides?: { textFileService?: (instantiationService: IInstantiationService) => ITextFileService }): ITestInstantiationService { + const instantiationService = new TestInstantiationService(new ServiceCollection([ILifecycleService, new TestLifecycleService()])); + + instantiationService.stub(IWorkingCopyService, new TestWorkingCopyService()); + instantiationService.stub(IEnvironmentService, TestEnvironmentService); + const contextKeyService = instantiationService.createInstance(MockContextKeyService); + instantiationService.stub(IContextKeyService, contextKeyService); + instantiationService.stub(IProgressService, new TestProgressService()); + const workspaceContextService = new TestContextService(TestWorkspace); + instantiationService.stub(IWorkspaceContextService, workspaceContextService); + const configService = new TestConfigurationService(); + instantiationService.stub(IConfigurationService, configService); + instantiationService.stub(IFilesConfigurationService, new TestFilesConfigurationService(contextKeyService, configService, TestEnvironmentService)); + instantiationService.stub(ITextResourceConfigurationService, new TestTextResourceConfigurationService(configService)); + instantiationService.stub(IUntitledTextEditorService, instantiationService.createInstance(UntitledTextEditorService)); + instantiationService.stub(IStorageService, new TestStorageService()); + instantiationService.stub(IWorkbenchLayoutService, new TestLayoutService()); + instantiationService.stub(IDialogService, new TestDialogService()); + instantiationService.stub(IAccessibilityService, new TestAccessibilityService()); + instantiationService.stub(IFileDialogService, new TestFileDialogService()); + instantiationService.stub(IModeService, instantiationService.createInstance(ModeServiceImpl)); + instantiationService.stub(IHistoryService, new TestHistoryService()); + instantiationService.stub(ITextResourcePropertiesService, new TestTextResourcePropertiesService(configService)); + instantiationService.stub(IUndoRedoService, instantiationService.createInstance(UndoRedoService)); + instantiationService.stub(IModelService, instantiationService.createInstance(ModelServiceImpl)); + instantiationService.stub(IFileService, new TestFileService()); + instantiationService.stub(IBackupFileService, new TestBackupFileService()); + instantiationService.stub(ITelemetryService, NullTelemetryService); + instantiationService.stub(INotificationService, new TestNotificationService()); + instantiationService.stub(IUntitledTextEditorService, instantiationService.createInstance(UntitledTextEditorService)); + instantiationService.stub(IMenuService, new TestMenuService()); + instantiationService.stub(IKeybindingService, new MockKeybindingService()); + instantiationService.stub(IDecorationsService, new TestDecorationsService()); + instantiationService.stub(IExtensionService, new TestExtensionService()); + instantiationService.stub(IWorkingCopyFileService, instantiationService.createInstance(WorkingCopyFileService)); + instantiationService.stub(ITextFileService, overrides?.textFileService ? overrides.textFileService(instantiationService) : instantiationService.createInstance(TestTextFileService)); + instantiationService.stub(IHostService, instantiationService.createInstance(TestHostService)); + instantiationService.stub(ITextModelService, instantiationService.createInstance(TextModelResolverService)); + instantiationService.stub(IThemeService, new TestThemeService()); + instantiationService.stub(ILogService, new NullLogService()); + const editorGroupService = new TestEditorGroupsService([new TestEditorGroupView(0)]); + instantiationService.stub(IEditorGroupsService, editorGroupService); + instantiationService.stub(ILabelService, instantiationService.createInstance(LabelService)); + const editorService = new TestEditorService(editorGroupService); + instantiationService.stub(IEditorService, editorService); + instantiationService.stub(ICodeEditorService, new TestCodeEditorService()); + instantiationService.stub(IViewletService, new TestViewletService()); + + return instantiationService; +} + +export class TestServiceAccessor { + constructor( + @ILifecycleService public lifecycleService: TestLifecycleService, + @ITextFileService public textFileService: TestTextFileService, + @IWorkingCopyFileService public workingCopyFileService: IWorkingCopyFileService, + @IFilesConfigurationService public filesConfigurationService: TestFilesConfigurationService, + @IWorkspaceContextService public contextService: TestContextService, + @IModelService public modelService: ModelServiceImpl, + @IFileService public fileService: TestFileService, + @IFileDialogService public fileDialogService: TestFileDialogService, + @IWorkingCopyService public workingCopyService: IWorkingCopyService, + @IEditorService public editorService: TestEditorService, + @IEditorGroupsService public editorGroupService: IEditorGroupsService, + @IModeService public modeService: IModeService, + @ITextModelService public textModelResolverService: ITextModelService, + @IUntitledTextEditorService public untitledTextEditorService: UntitledTextEditorService, + @IConfigurationService public testConfigurationService: TestConfigurationService, + @IBackupFileService public backupFileService: TestBackupFileService + ) { } +} + export class TestTextFileService extends BrowserTextFileService { private resolveTextContentError!: FileOperationError | null; @@ -173,57 +251,6 @@ export class TestTextFileService extends BrowserTextFileService { export const TestEnvironmentService = new BrowserWorkbenchEnvironmentService(Object.create(null)); -export function workbenchInstantiationService(overrides?: { textFileService?: (instantiationService: IInstantiationService) => ITextFileService }): ITestInstantiationService { - const instantiationService = new TestInstantiationService(new ServiceCollection([ILifecycleService, new TestLifecycleService()])); - - instantiationService.stub(IWorkingCopyService, new TestWorkingCopyService()); - instantiationService.stub(IEnvironmentService, TestEnvironmentService); - const contextKeyService = instantiationService.createInstance(MockContextKeyService); - instantiationService.stub(IContextKeyService, contextKeyService); - instantiationService.stub(IProgressService, new TestProgressService()); - const workspaceContextService = new TestContextService(TestWorkspace); - instantiationService.stub(IWorkspaceContextService, workspaceContextService); - const configService = new TestConfigurationService(); - instantiationService.stub(IConfigurationService, configService); - instantiationService.stub(IFilesConfigurationService, new TestFilesConfigurationService(contextKeyService, configService, TestEnvironmentService)); - instantiationService.stub(ITextResourceConfigurationService, new TestTextResourceConfigurationService(configService)); - instantiationService.stub(IUntitledTextEditorService, instantiationService.createInstance(UntitledTextEditorService)); - instantiationService.stub(IStorageService, new TestStorageService()); - instantiationService.stub(IWorkbenchLayoutService, new TestLayoutService()); - instantiationService.stub(IDialogService, new TestDialogService()); - instantiationService.stub(IAccessibilityService, new TestAccessibilityService()); - instantiationService.stub(IFileDialogService, new TestFileDialogService()); - instantiationService.stub(IModeService, instantiationService.createInstance(ModeServiceImpl)); - instantiationService.stub(IHistoryService, new TestHistoryService()); - instantiationService.stub(ITextResourcePropertiesService, new TestTextResourcePropertiesService(configService)); - instantiationService.stub(IUndoRedoService, instantiationService.createInstance(UndoRedoService)); - instantiationService.stub(IModelService, instantiationService.createInstance(ModelServiceImpl)); - instantiationService.stub(IFileService, new TestFileService()); - instantiationService.stub(IBackupFileService, new TestBackupFileService()); - instantiationService.stub(ITelemetryService, NullTelemetryService); - instantiationService.stub(INotificationService, new TestNotificationService()); - instantiationService.stub(IUntitledTextEditorService, instantiationService.createInstance(UntitledTextEditorService)); - instantiationService.stub(IMenuService, new TestMenuService()); - instantiationService.stub(IKeybindingService, new MockKeybindingService()); - instantiationService.stub(IDecorationsService, new TestDecorationsService()); - instantiationService.stub(IExtensionService, new TestExtensionService()); - instantiationService.stub(IWorkingCopyFileService, instantiationService.createInstance(WorkingCopyFileService)); - instantiationService.stub(ITextFileService, overrides?.textFileService ? overrides.textFileService(instantiationService) : instantiationService.createInstance(TestTextFileService)); - instantiationService.stub(IHostService, instantiationService.createInstance(TestHostService)); - instantiationService.stub(ITextModelService, instantiationService.createInstance(TextModelResolverService)); - instantiationService.stub(IThemeService, new TestThemeService()); - instantiationService.stub(ILogService, new NullLogService()); - const editorGroupService = new TestEditorGroupsService([new TestEditorGroupView(0)]); - instantiationService.stub(IEditorGroupsService, editorGroupService); - instantiationService.stub(ILabelService, instantiationService.createInstance(LabelService)); - const editorService = new TestEditorService(editorGroupService); - instantiationService.stub(IEditorService, editorService); - instantiationService.stub(ICodeEditorService, new TestCodeEditorService()); - instantiationService.stub(IViewletService, new TestViewletService()); - - return instantiationService; -} - export class TestProgressService implements IProgressService { _serviceBrand: undefined; @@ -906,3 +933,136 @@ export class TestFilesConfigurationService extends FilesConfigurationService { super.onFilesConfigurationChange(configuration); } } + +export class TestReadonlyTextFileEditorModel extends TextFileEditorModel { + + isReadonly(): boolean { + return true; + } +} + +export class TestEditorInput extends EditorInput { + + constructor(public resource: URI, private typeId: string) { + super(); + } + + getTypeId(): string { + return this.typeId; + } + + resolve(): Promise { + return Promise.resolve(null); + } +} + +export function registerTestEditor(id: string, inputs: SyncDescriptor[], factoryInputId?: string): IDisposable { + class TestEditorControl extends BaseEditor { + + constructor() { super(id, NullTelemetryService, new TestThemeService(), new TestStorageService()); } + + async setInput(input: EditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise { + super.setInput(input, options, token); + + await input.resolve(); + } + + getId(): string { return id; } + layout(): void { } + createEditor(): any { } + } + + const disposables = new DisposableStore(); + + disposables.add(Registry.as(Extensions.Editors).registerEditor(EditorDescriptor.create(TestEditorControl, id, 'Test Editor Control'), inputs)); + + if (factoryInputId) { + + interface ISerializedTestInput { + resource: string; + } + + class EditorsObserverTestEditorInputFactory implements IEditorInputFactory { + + canSerialize(editorInput: EditorInput): boolean { + return true; + } + + serialize(editorInput: EditorInput): string { + let testEditorInput = editorInput; + let testInput: ISerializedTestInput = { + resource: testEditorInput.resource.toString() + }; + + return JSON.stringify(testInput); + } + + deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): EditorInput { + let testInput: ISerializedTestInput = JSON.parse(serializedEditorInput); + + return new TestFileEditorInput(URI.parse(testInput.resource), factoryInputId!); + } + } + + disposables.add(Registry.as(EditorExtensions.EditorInputFactories).registerEditorInputFactory(factoryInputId, EditorsObserverTestEditorInputFactory)); + } + + return disposables; +} + +export class TestFileEditorInput extends EditorInput implements IFileEditorInput { + gotDisposed = false; + gotSaved = false; + gotSavedAs = false; + gotReverted = false; + dirty = false; + private fails = false; + + constructor( + public resource: URI, + private typeId: string + ) { + super(); + } + + getTypeId() { return this.typeId; } + resolve(): Promise { return !this.fails ? Promise.resolve(null) : Promise.reject(new Error('fails')); } + matches(other: TestEditorInput): boolean { return other && other.resource && this.resource.toString() === other.resource.toString() && other instanceof TestFileEditorInput && other.getTypeId() === this.typeId; } + setEncoding(encoding: string) { } + getEncoding() { return undefined; } + setPreferredEncoding(encoding: string) { } + setMode(mode: string) { } + setPreferredMode(mode: string) { } + setForceOpenAsBinary(): void { } + setFailToOpen(): void { + this.fails = true; + } + async save(groupId: GroupIdentifier, options?: ISaveOptions): Promise { + this.gotSaved = true; + return this; + } + async saveAs(groupId: GroupIdentifier, options?: ISaveOptions): Promise { + this.gotSavedAs = true; + return this; + } + async revert(group: GroupIdentifier, options?: IRevertOptions): Promise { + this.gotReverted = true; + this.gotSaved = false; + this.gotSavedAs = false; + return true; + } + setDirty(): void { this.dirty = true; } + isDirty(): boolean { + return this.dirty; + } + isReadonly(): boolean { + return false; + } + isResolved(): boolean { return false; } + dispose(): void { + super.dispose(); + this.gotDisposed = true; + } + movedEditor: IMoveResult | undefined = undefined; + move(): IMoveResult | undefined { return this.movedEditor; } +} diff --git a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts index 769dfdcb2ce..929d1d748ad 100644 --- a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts @@ -3,11 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { workbenchInstantiationService as browserWorkbenchInstantiationService, ITestInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices'; +import { workbenchInstantiationService as browserWorkbenchInstantiationService, ITestInstantiationService, TestLifecycleService, TestFilesConfigurationService, TestContextService, TestFileService, TestFileDialogService } from 'vs/workbench/test/browser/workbenchTestServices'; import { Event } from 'vs/base/common/event'; import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; import { NativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-browser/environmentService'; -import { NativeTextFileService } from 'vs/workbench/services/textfile/electron-browser/nativeTextFileService'; +import { NativeTextFileService, EncodingOracle, IEncodingOverride } from 'vs/workbench/services/textfile/electron-browser/nativeTextFileService'; import { IElectronService } from 'vs/platform/electron/node/electron'; import { INativeOpenDialogOptions } from 'vs/platform/dialogs/node/dialogs'; import { FileOperationError, IFileService } from 'vs/platform/files/common/files'; @@ -30,6 +30,13 @@ import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv'; import { LogLevel } from 'vs/platform/log/common/log'; import { IRemotePathService } from 'vs/workbench/services/path/common/remotePathService'; import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService'; +import { UTF16le, UTF16be, UTF8_with_bom } from 'vs/base/node/encoding'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; +import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; +import { NodeTestBackupFileService } from 'vs/workbench/services/backup/test/electron-browser/backupFileService.test'; +import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; export const TestWindowConfiguration: IWindowConfiguration = { windowId: 0, @@ -110,6 +117,31 @@ export class TestTextFileService extends NativeTextFileService { } } +export class TestNativeTextFileServiceWithEncodingOverrides extends NativeTextFileService { + + private _testEncoding: TestEncodingOracle | undefined; + get encoding(): TestEncodingOracle { + if (!this._testEncoding) { + this._testEncoding = this._register(this.instantiationService.createInstance(TestEncodingOracle)); + } + + return this._testEncoding; + } +} + +class TestEncodingOracle extends EncodingOracle { + + protected get encodingOverrides(): IEncodingOverride[] { + return [ + { extension: 'utf16le', encoding: UTF16le }, + { extension: 'utf16be', encoding: UTF16be }, + { extension: 'utf8bom', encoding: UTF8_with_bom } + ]; + } + + protected set encodingOverrides(overrides: IEncodingOverride[]) { } +} + export class TestSharedProcessService implements ISharedProcessService { _serviceBrand: undefined; @@ -187,3 +219,20 @@ export function workbenchInstantiationService(): ITestInstantiationService { return instantiationService; } + +export class TestServiceAccessor { + constructor( + @ILifecycleService public lifecycleService: TestLifecycleService, + @ITextFileService public textFileService: TestTextFileService, + @IFilesConfigurationService public filesConfigurationService: TestFilesConfigurationService, + @IWorkspaceContextService public contextService: TestContextService, + @IModelService public modelService: ModelServiceImpl, + @IFileService public fileService: TestFileService, + @IElectronService public electronService: TestElectronService, + @IFileDialogService public fileDialogService: TestFileDialogService, + @IBackupFileService public backupFileService: NodeTestBackupFileService, + @IWorkingCopyService public workingCopyService: IWorkingCopyService, + @IEditorService public editorService: IEditorService + ) { + } +} From 43076ce316a3142290ddb9dbd1609f9ddd4b324c Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Fri, 21 Feb 2020 11:17:51 +0100 Subject: [PATCH 007/200] Command of root task with sequence dependsOn shouldn't execute Fixes #90352 --- src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts index ae20fb999fe..bf6617e3b90 100644 --- a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts @@ -438,6 +438,7 @@ export class TerminalTaskSystem implements ITaskSystem { this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.DependsOnStarted, task)); promise = this.executeTask(dependencyTask, resolver, trigger, alreadyResolved); } + promises.push(promise); if (task.configurationProperties.dependsOrder === DependsOrder.sequence) { const promiseResult = await promise; if (promiseResult.exitCode === 0) { From a30ddfa505d5899768c4c8a6dea87a4527022873 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 21 Feb 2020 11:29:31 +0100 Subject: [PATCH 008/200] editors - remove ugly cast --- src/vs/workbench/common/editor.ts | 2 +- src/vs/workbench/services/editor/browser/editorService.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index 87caf812d9b..b63812fdc96 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -346,7 +346,7 @@ export interface IRevertOptions { } export interface IMoveResult { - editor: IEditorInput | IResourceEditor; + editor: EditorInput | IResourceEditor; options?: IEditorOptions; } diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index 8dc5bc3dc8f..2583458a30d 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -277,7 +277,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { replacement: { ...moveResult.editor, options: { - ...(moveResult.editor as IResourceEditor /* TS fail */).options, + ...moveResult.editor.options, ...optionOverrides } } From 1f86149904dfa8f2b61b48439533dc3e5d003fd6 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Fri, 21 Feb 2020 11:40:07 +0100 Subject: [PATCH 009/200] Allow user tasks to be passed as parameters to Run Task Fixes #90344 --- .../contrib/tasks/browser/abstractTaskService.ts | 13 +++++++------ src/vs/workbench/contrib/tasks/common/taskSystem.ts | 2 +- src/vs/workbench/contrib/tasks/common/tasks.ts | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index 4eff5ae0290..728fff5fac2 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -1167,8 +1167,8 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } }); let resolver: ITaskResolver = { - resolve: (uri: URI, alias: string) => { - let data = resolverData.get(uri.toString()); + resolve: (uri: URI | string, alias: string) => { + let data = resolverData.get(typeof uri === 'string' ? uri : uri.toString()); if (!data) { return undefined; } @@ -1234,8 +1234,8 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer }); return { - resolve: (uri: URI, identifier: string | TaskIdentifier | undefined) => { - let data = uri ? resolverData.get(uri.toString()) : undefined; + resolve: (uri: URI | string, identifier: string | TaskIdentifier | undefined) => { + let data = resolverData.get(typeof uri === 'string' ? uri : uri.toString()); if (!data || !identifier) { return undefined; } @@ -2116,9 +2116,10 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer if (identifier !== undefined) { this.getGroupedTasks().then((grouped) => { let resolver = this.createResolver(grouped); - let folders = this.contextService.getWorkspace().folders; + let folders: (IWorkspaceFolder | string)[] = this.contextService.getWorkspace().folders; + folders = folders.concat([USER_TASKS_GROUP_KEY]); for (let folder of folders) { - let task = resolver.resolve(folder.uri, identifier); + let task = resolver.resolve(typeof folder === 'string' ? folder : folder.uri, identifier); if (task) { this.run(task).then(undefined, reason => { // eat the error, it has already been surfaced to the user and we don't care about it here diff --git a/src/vs/workbench/contrib/tasks/common/taskSystem.ts b/src/vs/workbench/contrib/tasks/common/taskSystem.ts index 888d79b912c..0ac0d5799a6 100644 --- a/src/vs/workbench/contrib/tasks/common/taskSystem.ts +++ b/src/vs/workbench/contrib/tasks/common/taskSystem.ts @@ -93,7 +93,7 @@ export interface ITaskExecuteResult { } export interface ITaskResolver { - resolve(uri: URI, identifier: string | KeyedTaskIdentifier | undefined): Task | undefined; + resolve(uri: URI | string, identifier: string | KeyedTaskIdentifier | undefined): Task | undefined; } export interface TaskTerminateResponse extends TerminateResponse { diff --git a/src/vs/workbench/contrib/tasks/common/tasks.ts b/src/vs/workbench/contrib/tasks/common/tasks.ts index 8130ab56092..2fcd3f90195 100644 --- a/src/vs/workbench/contrib/tasks/common/tasks.ts +++ b/src/vs/workbench/contrib/tasks/common/tasks.ts @@ -438,7 +438,7 @@ export interface KeyedTaskIdentifier extends TaskIdentifier { } export interface TaskDependency { - uri: URI; + uri: URI | string; task: string | KeyedTaskIdentifier | undefined; } From af1310a9bf48a5c22152a96e64dbf8e33983f36a Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Fri, 21 Feb 2020 11:50:40 +0100 Subject: [PATCH 010/200] Depends on for user tasks should work Fixes #90286 --- .../contrib/tasks/common/taskConfiguration.ts | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/tasks/common/taskConfiguration.ts b/src/vs/workbench/contrib/tasks/common/taskConfiguration.ts index 5e6b05391ab..8b290a74f53 100644 --- a/src/vs/workbench/contrib/tasks/common/taskConfiguration.ts +++ b/src/vs/workbench/contrib/tasks/common/taskConfiguration.ts @@ -22,6 +22,8 @@ import { IWorkspaceFolder, IWorkspace } from 'vs/platform/workspace/common/works import * as Tasks from './tasks'; import { TaskDefinitionRegistry } from './taskDefinitionRegistry'; import { ConfiguredInput } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; +import { URI } from 'vs/base/common/uri'; +import { USER_TASKS_GROUP_KEY } from 'vs/workbench/contrib/tasks/common/taskService'; export const enum ShellQuoting { @@ -1231,9 +1233,15 @@ namespace GroupKind { } namespace TaskDependency { - export function from(this: void, external: string | TaskIdentifier, context: ParseContext): Tasks.TaskDependency | undefined { + export function from(this: void, external: string | TaskIdentifier, context: ParseContext, source: TaskConfigSource): Tasks.TaskDependency | undefined { if (Types.isString(external)) { - return { uri: context.workspace && context.workspace.configuration ? context.workspace.configuration : context.workspaceFolder.uri, task: external }; + let uri: URI | string; + if (source === TaskConfigSource.User) { + uri = USER_TASKS_GROUP_KEY; + } else { + uri = context.workspace && context.workspace.configuration ? context.workspace.configuration : context.workspaceFolder.uri; + } + return { uri, task: external }; } else if (TaskIdentifier.is(external)) { return { uri: context.workspace && context.workspace.configuration ? context.workspace.configuration : context.workspaceFolder.uri, @@ -1267,7 +1275,7 @@ namespace ConfigurationProperties { { property: 'options' } ]; - export function from(this: void, external: ConfigurationProperties & { [key: string]: any; }, context: ParseContext, includeCommandOptions: boolean, properties?: IJSONSchemaMap): Tasks.ConfigurationProperties | undefined { + export function from(this: void, external: ConfigurationProperties & { [key: string]: any; }, context: ParseContext, includeCommandOptions: boolean, source: TaskConfigSource, properties?: IJSONSchemaMap): Tasks.ConfigurationProperties | undefined { if (!external) { return undefined; } @@ -1311,14 +1319,14 @@ namespace ConfigurationProperties { if (external.dependsOn !== undefined) { if (Types.isArray(external.dependsOn)) { result.dependsOn = external.dependsOn.reduce((dependencies: Tasks.TaskDependency[], item): Tasks.TaskDependency[] => { - const dependency = TaskDependency.from(item, context); + const dependency = TaskDependency.from(item, context, source); if (dependency) { dependencies.push(dependency); } return dependencies; }, []); } else { - const dependsOnValue = TaskDependency.from(external.dependsOn, context); + const dependsOnValue = TaskDependency.from(external.dependsOn, context, source); result.dependsOn = dependsOnValue ? [dependsOnValue] : undefined; } } @@ -1435,7 +1443,7 @@ namespace ConfiguringTask { RunOptions.fromConfiguration(external.runOptions), {} ); - let configuration = ConfigurationProperties.from(external, context, true, typeDeclaration.properties); + let configuration = ConfigurationProperties.from(external, context, true, source, typeDeclaration.properties); if (configuration) { result.configurationProperties = Objects.assign(result.configurationProperties, configuration); if (result.configurationProperties.name) { @@ -1512,7 +1520,7 @@ namespace CustomTask { identifier: taskName, } ); - let configuration = ConfigurationProperties.from(external, context, false); + let configuration = ConfigurationProperties.from(external, context, false, source); if (configuration) { result.configurationProperties = Objects.assign(result.configurationProperties, configuration); } From aeda438ac0d6fe8c518989c55055da3eac63f194 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 21 Feb 2020 12:06:23 +0100 Subject: [PATCH 011/200] Debug groups: only return if the end event does not have additional output in it --- src/vs/workbench/contrib/debug/browser/debugSession.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugSession.ts b/src/vs/workbench/contrib/debug/browser/debugSession.ts index 5b9c4ac37dd..3c9ba78f0d6 100644 --- a/src/vs/workbench/contrib/debug/browser/debugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/debugSession.ts @@ -840,7 +840,10 @@ export class DebugSession implements IDebugSession { } if (event.body.group === 'end') { this.repl.endGroup(); - // Do not return, the end event can have additional output in it + if (!event.body.output) { + // Only return if the end event does not have additional output in it + return; + } } if (event.body.variablesReference) { From d29788c0bc173e175750361c9702a2fdd8189d3d Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 21 Feb 2020 12:07:01 +0100 Subject: [PATCH 012/200] Config to limit/disable the new Untitled tab auto-naming (fix #90378) --- .../browser/workbench.contribution.ts | 13 ++++++++ .../common/untitledTextEditorModel.ts | 31 +++++++++++++------ 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index acb6715b8d3..d06e816ca3f 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -42,6 +42,19 @@ import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuratio key: 'tabDescription' }, "Controls the format of the label for an editor."), }, + 'workbench.editor.untitled.labelFormat': { + 'type': 'string', + 'enum': ['content', 'name'], + 'enumDescriptions': [ + nls.localize('workbench.editor.untitled.labelFormat.content', "The name of the untitled file is derived from the contents of its first line with a fallback to the name in case the line is empty or contains no word characters."), + nls.localize('workbench.editor.untitled.labelFormat.name', "The name of the untitled file is not derived from the contents of the file."), + ], + 'default': 'content', + 'description': nls.localize({ + comment: ['This is the description for a setting. Values surrounded by parenthesis are not to be translated.'], + key: 'untitledLabelFormat' + }, "Controls the format of the label for an untitled editor."), + }, 'workbench.editor.tabCloseButton': { 'type': 'string', 'enum': ['left', 'right', 'off'], diff --git a/src/vs/workbench/services/untitled/common/untitledTextEditorModel.ts b/src/vs/workbench/services/untitled/common/untitledTextEditorModel.ts index 05e918a797d..d520cd9674b 100644 --- a/src/vs/workbench/services/untitled/common/untitledTextEditorModel.ts +++ b/src/vs/workbench/services/untitled/common/untitledTextEditorModel.ts @@ -92,7 +92,7 @@ export class UntitledTextEditorModel extends BaseTextEditorModel implements IUnt // Take name from first line if present and only if // we have no associated file path. In that case we // prefer the file name as title. - if (!this.hasAssociatedFilePath && this.cachedModelFirstLineWords) { + if (this.configuredLabelFormat === 'content' && !this.hasAssociatedFilePath && this.cachedModelFirstLineWords) { return this.cachedModelFirstLineWords; } @@ -104,7 +104,9 @@ export class UntitledTextEditorModel extends BaseTextEditorModel implements IUnt private ignoreDirtyOnModelContentChange = false; private versionId = 0; + private configuredEncoding: string | undefined; + private configuredLabelFormat: 'content' | 'name' = 'content'; constructor( public readonly resource: URI, @@ -130,25 +132,39 @@ export class UntitledTextEditorModel extends BaseTextEditorModel implements IUnt this.setMode(preferredMode); } + // Fetch config + this.onConfigurationChange(false); + this.registerListeners(); } private registerListeners(): void { // Config Changes - this._register(this.textResourceConfigurationService.onDidChangeConfiguration(e => this.onConfigurationChange())); + this._register(this.textResourceConfigurationService.onDidChangeConfiguration(e => this.onConfigurationChange(true))); } - private onConfigurationChange(): void { - const configuredEncoding = this.textResourceConfigurationService.getValue(this.resource, 'files.encoding'); + private onConfigurationChange(fromEvent: boolean): void { + // Encoding + const configuredEncoding = this.textResourceConfigurationService.getValue(this.resource, 'files.encoding'); if (this.configuredEncoding !== configuredEncoding) { this.configuredEncoding = configuredEncoding; - if (!this.preferredEncoding) { + if (fromEvent && !this.preferredEncoding) { this._onDidChangeEncoding.fire(); // do not fire event if we have a preferred encoding set } } + + // Label Format + const configuredLabelFormat = this.textResourceConfigurationService.getValue(this.resource, 'workbench.editor.untitled.labelFormat'); + if (this.configuredLabelFormat !== configuredLabelFormat && (configuredLabelFormat === 'content' || configuredLabelFormat === 'name')) { + this.configuredLabelFormat = configuredLabelFormat; + + if (fromEvent) { + this._onDidChangeName.fire(); + } + } } getVersionId(): number { @@ -281,13 +297,10 @@ export class UntitledTextEditorModel extends BaseTextEditorModel implements IUnt this.updateTextEditorModel(undefined, this.preferredMode); } - // Figure out encoding now that model is present - this.configuredEncoding = this.textResourceConfigurationService.getValue(this.resource, 'files.encoding'); - // Listen to text model events const textEditorModel = assertIsDefined(this.textEditorModel); this._register(textEditorModel.onDidChangeContent(e => this.onModelContentChanged(textEditorModel, e))); - this._register(textEditorModel.onDidChangeLanguage(() => this.onConfigurationChange())); // mode change can have impact on config + this._register(textEditorModel.onDidChangeLanguage(() => this.onConfigurationChange(true))); // mode change can have impact on config // Only adjust name and dirty state etc. if we // actually created the untitled model From b90641f8c9fd1f6e1dcf1891213c381a72cd9156 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 21 Feb 2020 12:09:44 +0100 Subject: [PATCH 013/200] :lipstick: wording --- src/vs/workbench/browser/workbench.contribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index d06e816ca3f..2fa180e6968 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -46,7 +46,7 @@ import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuratio 'type': 'string', 'enum': ['content', 'name'], 'enumDescriptions': [ - nls.localize('workbench.editor.untitled.labelFormat.content', "The name of the untitled file is derived from the contents of its first line with a fallback to the name in case the line is empty or contains no word characters."), + nls.localize('workbench.editor.untitled.labelFormat.content', "The name of the untitled file is derived from the contents of its first line unless it has an associated file path. It will fallback to the name in case the line is empty or contains no word characters."), nls.localize('workbench.editor.untitled.labelFormat.name', "The name of the untitled file is not derived from the contents of the file."), ], 'default': 'content', From 9c31bce0c77f12fd410430fd38de3de027bc6d1f Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 21 Feb 2020 12:20:10 +0100 Subject: [PATCH 014/200] untitled - prevent slash as description only --- src/vs/workbench/browser/labels.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/browser/labels.ts b/src/vs/workbench/browser/labels.ts index 1d346c89ff3..895d41d509e 100644 --- a/src/vs/workbench/browser/labels.ts +++ b/src/vs/workbench/browser/labels.ts @@ -4,7 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from 'vs/base/common/uri'; -import * as resources from 'vs/base/common/resources'; +import { posix } from 'vs/base/common/path'; +import { dirname, isEqual, basenameOrAuthority } from 'vs/base/common/resources'; import { IconLabel, IIconLabelValueOptions, IIconLabelCreationOptions } from 'vs/base/browser/ui/iconLabel/iconLabel'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IModeService } from 'vs/editor/common/services/modeService'; @@ -323,7 +324,7 @@ class ResourceLabelWidget extends IconLabel { } notifyUntitledLabelChange(resource: URI): void { - if (resources.isEqual(resource, this.label?.resource)) { + if (isEqual(resource, this.label?.resource)) { this.render(false); } } @@ -340,13 +341,13 @@ class ResourceLabelWidget extends IconLabel { } if (!name) { - name = resources.basenameOrAuthority(resource); + name = basenameOrAuthority(resource); } } let description: string | undefined; if (!options?.hidePath) { - description = this.labelService.getUriLabel(resources.dirname(resource), { relative: true }); + description = this.labelService.getUriLabel(dirname(resource), { relative: true }); } this.setResource({ resource, name, description }, options); @@ -370,13 +371,15 @@ class ResourceLabelWidget extends IconLabel { if (typeof label.description === 'string') { let untitledDescription: string; if (untitledModel.hasAssociatedFilePath) { - untitledDescription = this.labelService.getUriLabel(resources.dirname(untitledModel.resource), { relative: true }); + untitledDescription = this.labelService.getUriLabel(dirname(untitledModel.resource), { relative: true }); } else { untitledDescription = untitledModel.resource.path; } if (label.name !== untitledDescription) { label.description = untitledDescription; + } else if (label.description === posix.sep) { + label.description = undefined; // unset showing just "/" for untitled without associated resource } } } From 68a1013668b6587ed5cc6489de16b0d22bbb3352 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 21 Feb 2020 12:29:13 +0100 Subject: [PATCH 015/200] Explore replacing notification count with alternate icon (fix #91130) --- .../browser/parts/notifications/notificationsStatus.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/parts/notifications/notificationsStatus.ts b/src/vs/workbench/browser/parts/notifications/notificationsStatus.ts index 3545ab9c02b..f0be978d55c 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsStatus.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsStatus.ts @@ -58,14 +58,20 @@ export class NotificationsStatus extends Disposable { private updateNotificationsCenterStatusItem(): void { const statusProperties: IStatusbarEntry = { - text: this.currentNotifications.size === 0 ? '$(bell)' : `$(bell) ${this.currentNotifications.size}`, + text: this.currentNotifications.size === 0 ? '$(bell)' : '$(bell-dot)', command: this.isNotificationsCenterVisible ? HIDE_NOTIFICATIONS_CENTER : SHOW_NOTIFICATIONS_CENTER, tooltip: this.getTooltip(), showBeak: this.isNotificationsCenterVisible }; if (!this.notificationsCenterStatusItem) { - this.notificationsCenterStatusItem = this.statusbarService.addEntry(statusProperties, 'status.notifications', localize('status.notifications', "Notifications"), StatusbarAlignment.RIGHT, -Number.MAX_VALUE /* towards the far end of the right hand side */); + this.notificationsCenterStatusItem = this.statusbarService.addEntry( + statusProperties, + 'status.notifications', + localize('status.notifications', "Notifications"), + StatusbarAlignment.RIGHT, + -Number.MAX_VALUE /* towards the far end of the right hand side */ + ); } else { this.notificationsCenterStatusItem.update(statusProperties); } From 49ef020fb9da4ee6a4e894f1a039c3aa66a9e9c1 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 21 Feb 2020 12:31:12 +0100 Subject: [PATCH 016/200] debug start view: debugStartLanguage context key fixes #85548 --- .../browser/debugConfigurationManager.ts | 12 ++-------- .../contrib/debug/browser/startView.ts | 24 ++++++++++++++----- .../workbench/contrib/debug/common/debug.ts | 2 +- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts index 405b81daeed..29dcf72e752 100644 --- a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts +++ b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts @@ -11,7 +11,6 @@ import * as objects from 'vs/base/common/objects'; import { URI as uri } from 'vs/base/common/uri'; import * as resources from 'vs/base/common/resources'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; -import * as editorCommon from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; import { IEditor } from 'vs/workbench/common/editor'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; @@ -434,15 +433,8 @@ export class ConfigurationManager implements IConfigurationManager { return this.debuggers.filter(dbg => strings.equalsIgnoreCase(dbg.type, type)).pop(); } - getDebuggerLabelsForEditor(editor: editorCommon.IEditor | undefined): string[] { - if (isCodeEditor(editor)) { - const model = editor.getModel(); - const language = model ? model.getLanguageIdentifier().language : undefined; - - return this.debuggers.filter(a => language && a.languages && a.languages.indexOf(language) >= 0).map(d => d.label); - } - - return []; + isDebuggerInterestedInLanguage(language: string): boolean { + return this.debuggers.filter(a => language && a.languages && a.languages.indexOf(language) >= 0).length > 0; } async guessDebugger(type?: string): Promise { diff --git a/src/vs/workbench/contrib/debug/browser/startView.ts b/src/vs/workbench/contrib/debug/browser/startView.ts index 89a25705c94..3362a4a32b9 100644 --- a/src/vs/workbench/contrib/debug/browser/startView.ts +++ b/src/vs/workbench/contrib/debug/browser/startView.ts @@ -21,14 +21,17 @@ import { IOpenerService } from 'vs/platform/opener/common/opener'; import { WorkbenchStateContext } from 'vs/workbench/browser/contextkeys'; import { OpenFolderAction, OpenFileAction, OpenFileFolderAction } from 'vs/workbench/browser/actions/workspaceActions'; import { isMacintosh } from 'vs/base/common/platform'; +import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; -const CONTEXT_DEBUGGER_INTERESTED = new RawContextKey('debuggerInterested', false); +const CONTEXT_DEBUG_START_LANGUAGE = new RawContextKey('debugStartLanguage', undefined); +const CONTEXT_DEBUGGER_INTERESTED_IN_ACTIVE_EDITOR = new RawContextKey('debuggerInterestedInActiveEditor', false); export class StartView extends ViewPane { static ID = 'workbench.debug.startView'; static LABEL = localize('start', "Start"); + private debugStartLanguageContext: IContextKey; private debuggerInterestedContext: IContextKey; constructor( @@ -46,11 +49,20 @@ export class StartView extends ViewPane { ) { super({ ...(options as IViewPaneOptions), ariaHeaderLabel: localize('debugStart', "Debug Start Section") }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService); - this.debuggerInterestedContext = CONTEXT_DEBUGGER_INTERESTED.bindTo(contextKeyService); + this.debugStartLanguageContext = CONTEXT_DEBUG_START_LANGUAGE.bindTo(contextKeyService); + this.debuggerInterestedContext = CONTEXT_DEBUGGER_INTERESTED_IN_ACTIVE_EDITOR.bindTo(contextKeyService); const setContextKey = () => { - const activeEditor = this.editorService.activeTextEditorWidget; - const debuggerLabels = this.debugService.getConfigurationManager().getDebuggerLabelsForEditor(activeEditor); - this.debuggerInterestedContext.set(debuggerLabels.length > 0); + const editor = this.editorService.activeTextEditorWidget; + if (isCodeEditor(editor)) { + const model = editor.getModel(); + const language = model ? model.getLanguageIdentifier().language : undefined; + if (language && this.debugService.getConfigurationManager().isDebuggerInterestedInLanguage(language)) { + this.debugStartLanguageContext.set(language); + this.debuggerInterestedContext.set(true); + return; + } + } + this.debuggerInterestedContext.set(false); }; this._register(editorService.onDidActiveEditorChange(setContextKey)); this._register(this.debugService.getConfigurationManager().onDidRegisterDebugger(setContextKey)); @@ -64,7 +76,7 @@ export class StartView extends ViewPane { const viewsRegistry = Registry.as(Extensions.ViewsRegistry); viewsRegistry.registerViewWelcomeContent(StartView.ID, { content: localize('openAFileWhichCanBeDebugged', "[Open a file](command:{0}) which can be debugged or run.", isMacintosh ? OpenFileFolderAction.ID : OpenFileAction.ID), - when: CONTEXT_DEBUGGER_INTERESTED.toNegated() + when: CONTEXT_DEBUGGER_INTERESTED_IN_ACTIVE_EDITOR }); viewsRegistry.registerViewWelcomeContent(StartView.ID, { diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index 9a9fbceb7bf..d753ab2f888 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -647,7 +647,7 @@ export interface IConfigurationManager { activateDebuggers(activationEvent: string, debugType?: string): Promise; - getDebuggerLabelsForEditor(editor: editorCommon.IEditor | undefined): string[]; + isDebuggerInterestedInLanguage(language: string): boolean; hasDebugConfigurationProvider(debugType: string): boolean; registerDebugConfigurationProvider(debugConfigurationProvider: IDebugConfigurationProvider): IDisposable; From 5f7ec944defdf50af11a5e43e81221ca2c404c58 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 21 Feb 2020 12:36:31 +0100 Subject: [PATCH 017/200] debug start label #85548 --- .../contrib/debug/browser/startView.ts | 45 ++++++++++--------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/startView.ts b/src/vs/workbench/contrib/debug/browser/startView.ts index 3362a4a32b9..0a6a3b175c2 100644 --- a/src/vs/workbench/contrib/debug/browser/startView.ts +++ b/src/vs/workbench/contrib/debug/browser/startView.ts @@ -66,29 +66,34 @@ export class StartView extends ViewPane { }; this._register(editorService.onDidActiveEditorChange(setContextKey)); this._register(this.debugService.getConfigurationManager().onDidRegisterDebugger(setContextKey)); + this.registerViews(); } shouldShowWelcome(): boolean { return true; } + + private registerViews(): void { + const viewsRegistry = Registry.as(Extensions.ViewsRegistry); + viewsRegistry.registerViewWelcomeContent(StartView.ID, { + content: localize('openAFileWhichCanBeDebugged', "[Open a file](command:{0}) which can be debugged or run.", isMacintosh ? OpenFileFolderAction.ID : OpenFileAction.ID), + when: CONTEXT_DEBUGGER_INTERESTED_IN_ACTIVE_EDITOR + }); + + const debugKeybinding = this.keybindingService.lookupKeybinding(StartAction.ID); + const debugKeybindingLabel = debugKeybinding ? ` (${debugKeybinding.getLabel()})` : ''; + viewsRegistry.registerViewWelcomeContent(StartView.ID, { + content: localize('runAndDebugAction', "[Run and Debug{0}](command:{1})", debugKeybindingLabel, StartAction.ID) + }); + + viewsRegistry.registerViewWelcomeContent(StartView.ID, { + content: localize('customizeRunAndDebug', "To customize Run and Debug [create a launch.json file](command:{0}).", ConfigureAction.ID), + when: WorkbenchStateContext.notEqualsTo('empty') + }); + + viewsRegistry.registerViewWelcomeContent(StartView.ID, { + content: localize('customizeRunAndDebugOpenFolder', "To customize Run and Debug, [open a folder](command:{0}) and create a launch.json file.", isMacintosh ? OpenFileFolderAction.ID : OpenFolderAction.ID), + when: WorkbenchStateContext.isEqualTo('empty') + }); + } } - -const viewsRegistry = Registry.as(Extensions.ViewsRegistry); -viewsRegistry.registerViewWelcomeContent(StartView.ID, { - content: localize('openAFileWhichCanBeDebugged', "[Open a file](command:{0}) which can be debugged or run.", isMacintosh ? OpenFileFolderAction.ID : OpenFileAction.ID), - when: CONTEXT_DEBUGGER_INTERESTED_IN_ACTIVE_EDITOR -}); - -viewsRegistry.registerViewWelcomeContent(StartView.ID, { - content: localize('runAndDebugAction', "[Run and Debug](command:{0})", StartAction.ID) -}); - -viewsRegistry.registerViewWelcomeContent(StartView.ID, { - content: localize('customizeRunAndDebug', "To customize Run and Debug [create a launch.json file](command:{0}).", ConfigureAction.ID), - when: WorkbenchStateContext.notEqualsTo('empty') -}); - -viewsRegistry.registerViewWelcomeContent(StartView.ID, { - content: localize('customizeRunAndDebugOpenFolder', "To customize Run and Debug, [open a folder](command:{0}) and create a launch.json file.", isMacintosh ? OpenFileFolderAction.ID : OpenFolderAction.ID), - when: WorkbenchStateContext.isEqualTo('empty') -}); From 7d10b5a8faf9520c03510cebcdf37e32dab1c6e4 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 21 Feb 2020 12:58:05 +0100 Subject: [PATCH 018/200] notifications - progress prevents close (#90274) --- .../browser/parts/notifications/notificationsCenter.ts | 10 ++++++---- .../parts/notifications/notificationsCommands.ts | 2 +- .../browser/parts/notifications/notificationsToasts.ts | 4 ++-- .../browser/parts/notifications/notificationsViewer.ts | 8 +++++--- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts b/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts index 0b4cbe77264..094c9e1567f 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts @@ -11,7 +11,7 @@ import { INotificationsModel, INotificationChangeEvent, NotificationChangeType } import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService'; import { Emitter } from 'vs/base/common/event'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { NotificationsCenterVisibleContext } from 'vs/workbench/browser/parts/notifications/notificationsCommands'; +import { NotificationsCenterVisibleContext, INotificationsCenterController } from 'vs/workbench/browser/parts/notifications/notificationsCommands'; import { NotificationsList } from 'vs/workbench/browser/parts/notifications/notificationsList'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { addClass, removeClass, isAncestor, Dimension } from 'vs/base/browser/dom'; @@ -24,7 +24,7 @@ import { IAction } from 'vs/base/common/actions'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { assertAllDefined, assertIsDefined } from 'vs/base/common/types'; -export class NotificationsCenter extends Themable { +export class NotificationsCenter extends Themable implements INotificationsCenterController { private static readonly MAX_DIMENSIONS = new Dimension(450, 400); @@ -284,8 +284,10 @@ export class NotificationsCenter extends Themable { this.hide(); // Close all - while (this.model.notifications.length) { - this.model.notifications[0].close(); + for (const notification of this.model.notifications) { + if (!notification.hasProgress) { + notification.close(); + } } } } diff --git a/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts b/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts index 133bb8ff3ab..413e700668d 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts @@ -107,7 +107,7 @@ export function registerNotificationCommands(center: INotificationsCenterControl }, handler: (accessor, args?: any) => { const notification = getNotificationFromContext(accessor.get(IListService), args); - if (notification) { + if (notification && !notification.hasProgress) { notification.close(); } } diff --git a/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts b/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts index 9461b4887ac..4252392d139 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts @@ -15,7 +15,7 @@ import { Themable, NOTIFICATIONS_TOAST_BORDER, NOTIFICATIONS_BACKGROUND } from ' import { IThemeService } from 'vs/platform/theme/common/themeService'; import { widgetShadow } from 'vs/platform/theme/common/colorRegistry'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { NotificationsToastsVisibleContext } from 'vs/workbench/browser/parts/notifications/notificationsCommands'; +import { NotificationsToastsVisibleContext, INotificationsToastController } from 'vs/workbench/browser/parts/notifications/notificationsCommands'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { localize } from 'vs/nls'; import { Severity, NotificationsFilter } from 'vs/platform/notification/common/notification'; @@ -39,7 +39,7 @@ enum ToastVisibility { VISIBLE } -export class NotificationsToasts extends Themable { +export class NotificationsToasts extends Themable implements INotificationsToastController { private static readonly MAX_WIDTH = 450; private static readonly MAX_NOTIFICATIONS = 3; diff --git a/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts b/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts index 041863ea737..b2b8f0ce9ff 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts @@ -306,7 +306,7 @@ export class NotificationTemplateRenderer extends Disposable { // Container toggleClass(this.template.container, 'expanded', notification.expanded); this.inputDisposables.add(addDisposableListener(this.template.container, EventType.MOUSE_UP, e => { - if (e.button === 1 /* Middle Button */) { + if (!notification.hasProgress && e.button === 1 /* Middle Button */) { EventHelper.stop(e); notification.close(); @@ -403,8 +403,10 @@ export class NotificationTemplateRenderer extends Disposable { actions.push(notification.expanded ? NotificationTemplateRenderer.collapseNotificationAction : NotificationTemplateRenderer.expandNotificationAction); } - // Close - actions.push(NotificationTemplateRenderer.closeNotificationAction); + // Close (unless progress is showing) + if (!notification.hasProgress) { + actions.push(NotificationTemplateRenderer.closeNotificationAction); + } this.template.toolbar.clear(); this.template.toolbar.context = notification; From f4eab75554e1985443e5fe06346f23fee507f314 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 21 Feb 2020 13:04:57 +0100 Subject: [PATCH 019/200] debug start language: store the value of the context key per workspace storage #85548 --- src/vs/workbench/contrib/debug/browser/startView.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/startView.ts b/src/vs/workbench/contrib/debug/browser/startView.ts index 0a6a3b175c2..ac466684def 100644 --- a/src/vs/workbench/contrib/debug/browser/startView.ts +++ b/src/vs/workbench/contrib/debug/browser/startView.ts @@ -22,8 +22,10 @@ import { WorkbenchStateContext } from 'vs/workbench/browser/contextkeys'; import { OpenFolderAction, OpenFileAction, OpenFileFolderAction } from 'vs/workbench/browser/actions/workspaceActions'; import { isMacintosh } from 'vs/base/common/platform'; import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -const CONTEXT_DEBUG_START_LANGUAGE = new RawContextKey('debugStartLanguage', undefined); +const debugStartLanguageKey = 'debugStartLanguage'; +const CONTEXT_DEBUG_START_LANGUAGE = new RawContextKey(debugStartLanguageKey, undefined); const CONTEXT_DEBUGGER_INTERESTED_IN_ACTIVE_EDITOR = new RawContextKey('debuggerInterestedInActiveEditor', false); export class StartView extends ViewPane { @@ -31,7 +33,7 @@ export class StartView extends ViewPane { static ID = 'workbench.debug.startView'; static LABEL = localize('start', "Start"); - private debugStartLanguageContext: IContextKey; + private debugStartLanguageContext: IContextKey; private debuggerInterestedContext: IContextKey; constructor( @@ -46,11 +48,15 @@ export class StartView extends ViewPane { @IInstantiationService instantiationService: IInstantiationService, @IViewDescriptorService viewDescriptorService: IViewDescriptorService, @IOpenerService openerService: IOpenerService, + @IStorageService storageSevice: IStorageService ) { super({ ...(options as IViewPaneOptions), ariaHeaderLabel: localize('debugStart', "Debug Start Section") }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService); this.debugStartLanguageContext = CONTEXT_DEBUG_START_LANGUAGE.bindTo(contextKeyService); this.debuggerInterestedContext = CONTEXT_DEBUGGER_INTERESTED_IN_ACTIVE_EDITOR.bindTo(contextKeyService); + const lastSetLanguage = storageSevice.get(debugStartLanguageKey, StorageScope.WORKSPACE); + this.debugStartLanguageContext.set(lastSetLanguage); + const setContextKey = () => { const editor = this.editorService.activeTextEditorWidget; if (isCodeEditor(editor)) { @@ -59,6 +65,7 @@ export class StartView extends ViewPane { if (language && this.debugService.getConfigurationManager().isDebuggerInterestedInLanguage(language)) { this.debugStartLanguageContext.set(language); this.debuggerInterestedContext.set(true); + storageSevice.store(debugStartLanguageKey, language, StorageScope.WORKSPACE); return; } } From b2e0925ba1db090f467746aad8ecebb226013c25 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 21 Feb 2020 15:03:28 +0100 Subject: [PATCH 020/200] update jsonc-parser --- extensions/configuration-editing/package.json | 4 ++-- extensions/configuration-editing/yarn.lock | 16 ++++++++-------- extensions/extension-editing/package.json | 4 ++-- extensions/extension-editing/yarn.lock | 16 ++++++++-------- extensions/npm/package.json | 4 ++-- extensions/npm/yarn.lock | 13 ++++--------- .../typescript-language-features/package.json | 4 ++-- .../typescript-language-features/yarn.lock | 10 +++++----- extensions/vscode-colorize-tests/package.json | 2 +- extensions/vscode-colorize-tests/yarn.lock | 8 ++++---- 10 files changed, 38 insertions(+), 43 deletions(-) diff --git a/extensions/configuration-editing/package.json b/extensions/configuration-editing/package.json index be2d2e8ebae..0defd3aee6f 100644 --- a/extensions/configuration-editing/package.json +++ b/extensions/configuration-editing/package.json @@ -18,8 +18,8 @@ "watch": "gulp watch-extension:configuration-editing" }, "dependencies": { - "jsonc-parser": "^2.1.1", - "vscode-nls": "^4.0.0" + "jsonc-parser": "^2.2.1", + "vscode-nls": "^4.1.1" }, "contributes": { "languages": [ diff --git a/extensions/configuration-editing/yarn.lock b/extensions/configuration-editing/yarn.lock index d5aafed1189..36aab5fd224 100644 --- a/extensions/configuration-editing/yarn.lock +++ b/extensions/configuration-editing/yarn.lock @@ -7,12 +7,12 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-12.11.7.tgz#57682a9771a3f7b09c2497f28129a0462966524a" integrity sha512-JNbGaHFCLwgHn/iCckiGSOZ1XYHsKFwREtzPwSGCVld1SGhOlmZw2D4ZI94HQCrBHbADzW9m4LER/8olJTRGHA== -jsonc-parser@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.1.1.tgz#83dc3d7a6e7186346b889b1280eefa04446c6d3e" - integrity sha512-VC0CjnWJylKB1iov4u76/W/5Ef0ydDkjtYWxoZ9t3HdWlSnZQwZL5MgFikaB/EtQ4RmMEw3tmQzuYnZA2/Ja1g== +jsonc-parser@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.2.1.tgz#db73cd59d78cce28723199466b2a03d1be1df2bc" + integrity sha512-o6/yDBYccGvTz1+QFevz6l6OBZ2+fMVu2JZ9CIhzsYRX4mjaK5IyX9eldUdCmga16zlgQxyrj5pt9kzuj2C02w== -vscode-nls@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.0.0.tgz#4001c8a6caba5cedb23a9c5ce1090395c0e44002" - integrity sha512-qCfdzcH+0LgQnBpZA53bA32kzp9rpq/f66Som577ObeuDlFIrtbEJ+A/+CCxjIh4G8dpJYNCKIsxpRAHIfsbNw== +vscode-nls@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.1.tgz#f9916b64e4947b20322defb1e676a495861f133c" + integrity sha512-4R+2UoUUU/LdnMnFjePxfLqNhBS8lrAFyX7pjb2ud/lqDkrUavFUTcG7wR0HBZFakae0Q6KLBFjMS6W93F403A== diff --git a/extensions/extension-editing/package.json b/extensions/extension-editing/package.json index 54edf5ccbdc..fe4a7b44d2a 100644 --- a/extensions/extension-editing/package.json +++ b/extensions/extension-editing/package.json @@ -19,10 +19,10 @@ "watch": "gulp watch-extension:extension-editing" }, "dependencies": { - "jsonc-parser": "^2.1.1", + "jsonc-parser": "^2.2.1", "markdown-it": "^8.3.1", "parse5": "^3.0.2", - "vscode-nls": "^4.0.0" + "vscode-nls": "^4.1.1" }, "contributes": { "jsonValidation": [ diff --git a/extensions/extension-editing/yarn.lock b/extensions/extension-editing/yarn.lock index d2f69a169d8..50adf31c099 100644 --- a/extensions/extension-editing/yarn.lock +++ b/extensions/extension-editing/yarn.lock @@ -29,10 +29,10 @@ entities@~1.1.1: resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" integrity sha1-blwtClYhtdra7O+AuQ7ftc13cvA= -jsonc-parser@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.1.1.tgz#83dc3d7a6e7186346b889b1280eefa04446c6d3e" - integrity sha512-VC0CjnWJylKB1iov4u76/W/5Ef0ydDkjtYWxoZ9t3HdWlSnZQwZL5MgFikaB/EtQ4RmMEw3tmQzuYnZA2/Ja1g== +jsonc-parser@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.2.1.tgz#db73cd59d78cce28723199466b2a03d1be1df2bc" + integrity sha512-o6/yDBYccGvTz1+QFevz6l6OBZ2+fMVu2JZ9CIhzsYRX4mjaK5IyX9eldUdCmga16zlgQxyrj5pt9kzuj2C02w== linkify-it@^2.0.0: version "2.0.3" @@ -74,7 +74,7 @@ uc.micro@^1.0.1, uc.micro@^1.0.3: resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.3.tgz#7ed50d5e0f9a9fb0a573379259f2a77458d50192" integrity sha1-ftUNXg+an7ClczeSWfKndFjVAZI= -vscode-nls@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.0.0.tgz#4001c8a6caba5cedb23a9c5ce1090395c0e44002" - integrity sha512-qCfdzcH+0LgQnBpZA53bA32kzp9rpq/f66Som577ObeuDlFIrtbEJ+A/+CCxjIh4G8dpJYNCKIsxpRAHIfsbNw== +vscode-nls@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.1.tgz#f9916b64e4947b20322defb1e676a495861f133c" + integrity sha512-4R+2UoUUU/LdnMnFjePxfLqNhBS8lrAFyX7pjb2ud/lqDkrUavFUTcG7wR0HBZFakae0Q6KLBFjMS6W93F403A== diff --git a/extensions/npm/package.json b/extensions/npm/package.json index cbd9645341a..79a95f7fc1b 100644 --- a/extensions/npm/package.json +++ b/extensions/npm/package.json @@ -18,10 +18,10 @@ "watch": "gulp watch-extension:npm" }, "dependencies": { - "jsonc-parser": "^2.1.1", + "jsonc-parser": "^2.2.1", "minimatch": "^3.0.4", "request-light": "^0.2.5", - "vscode-nls": "^4.0.0" + "vscode-nls": "^4.1.1" }, "devDependencies": { "@types/minimatch": "^3.0.3", diff --git a/extensions/npm/yarn.lock b/extensions/npm/yarn.lock index 4c6c723c3cb..9b8a23c9264 100644 --- a/extensions/npm/yarn.lock +++ b/extensions/npm/yarn.lock @@ -79,10 +79,10 @@ https-proxy-agent@^2.2.3: agent-base "^4.3.0" debug "^3.1.0" -jsonc-parser@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.1.1.tgz#83dc3d7a6e7186346b889b1280eefa04446c6d3e" - integrity sha512-VC0CjnWJylKB1iov4u76/W/5Ef0ydDkjtYWxoZ9t3HdWlSnZQwZL5MgFikaB/EtQ4RmMEw3tmQzuYnZA2/Ja1g== +jsonc-parser@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.2.1.tgz#db73cd59d78cce28723199466b2a03d1be1df2bc" + integrity sha512-o6/yDBYccGvTz1+QFevz6l6OBZ2+fMVu2JZ9CIhzsYRX4mjaK5IyX9eldUdCmga16zlgQxyrj5pt9kzuj2C02w== minimatch@^3.0.4: version "3.0.4" @@ -105,11 +105,6 @@ request-light@^0.2.5: https-proxy-agent "^2.2.3" vscode-nls "^4.1.1" -vscode-nls@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.0.0.tgz#4001c8a6caba5cedb23a9c5ce1090395c0e44002" - integrity sha512-qCfdzcH+0LgQnBpZA53bA32kzp9rpq/f66Som577ObeuDlFIrtbEJ+A/+CCxjIh4G8dpJYNCKIsxpRAHIfsbNw== - vscode-nls@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.1.tgz#f9916b64e4947b20322defb1e676a495861f133c" diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index e1f96f66c6d..7392a267a56 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -16,12 +16,12 @@ "Programming Languages" ], "dependencies": { - "jsonc-parser": "^2.1.1", + "jsonc-parser": "^2.2.1", "rimraf": "^2.6.3", "semver": "5.5.1", "typescript-vscode-sh-plugin": "^0.6.8", "vscode-extension-telemetry": "0.1.1", - "vscode-nls": "^4.0.0" + "vscode-nls": "^4.1.1" }, "devDependencies": { "@types/node": "^12.11.7", diff --git a/extensions/typescript-language-features/yarn.lock b/extensions/typescript-language-features/yarn.lock index c84c7d75280..82daf1829dd 100644 --- a/extensions/typescript-language-features/yarn.lock +++ b/extensions/typescript-language-features/yarn.lock @@ -393,10 +393,10 @@ json-stringify-safe@~5.0.1: resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= -jsonc-parser@^2.1.1: - version "2.2.0" - resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.2.0.tgz#f206f87f9d49d644b7502052c04e82dd6392e9ef" - integrity sha512-4fLQxW1j/5fWj6p78vAlAafoCKtuBm6ghv+Ij5W2DrDx0qE+ZdEl2c6Ko1mgJNF5ftX1iEWQQ4Ap7+3GlhjkOA== +jsonc-parser@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.2.1.tgz#db73cd59d78cce28723199466b2a03d1be1df2bc" + integrity sha512-o6/yDBYccGvTz1+QFevz6l6OBZ2+fMVu2JZ9CIhzsYRX4mjaK5IyX9eldUdCmga16zlgQxyrj5pt9kzuj2C02w== jsprim@^1.2.2: version "1.4.1" @@ -667,7 +667,7 @@ vscode-extension-telemetry@0.1.1: dependencies: applicationinsights "1.0.8" -vscode-nls@^4.0.0: +vscode-nls@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.1.tgz#f9916b64e4947b20322defb1e676a495861f133c" integrity sha512-4R+2UoUUU/LdnMnFjePxfLqNhBS8lrAFyX7pjb2ud/lqDkrUavFUTcG7wR0HBZFakae0Q6KLBFjMS6W93F403A== diff --git a/extensions/vscode-colorize-tests/package.json b/extensions/vscode-colorize-tests/package.json index da71634c2e7..d99e050d302 100644 --- a/extensions/vscode-colorize-tests/package.json +++ b/extensions/vscode-colorize-tests/package.json @@ -17,7 +17,7 @@ "vscode:prepublish": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:vscode-colorize-tests ./tsconfig.json" }, "dependencies": { - "jsonc-parser": "2.2.0" + "jsonc-parser": "2.2.1" }, "devDependencies": { "@types/node": "^12.11.7", diff --git a/extensions/vscode-colorize-tests/yarn.lock b/extensions/vscode-colorize-tests/yarn.lock index c6b3fdb4313..b8a66d65eff 100644 --- a/extensions/vscode-colorize-tests/yarn.lock +++ b/extensions/vscode-colorize-tests/yarn.lock @@ -1042,10 +1042,10 @@ json3@3.3.2: resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1" integrity sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE= -jsonc-parser@2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.2.0.tgz#f206f87f9d49d644b7502052c04e82dd6392e9ef" - integrity sha512-4fLQxW1j/5fWj6p78vAlAafoCKtuBm6ghv+Ij5W2DrDx0qE+ZdEl2c6Ko1mgJNF5ftX1iEWQQ4Ap7+3GlhjkOA== +jsonc-parser@2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.2.1.tgz#db73cd59d78cce28723199466b2a03d1be1df2bc" + integrity sha512-o6/yDBYccGvTz1+QFevz6l6OBZ2+fMVu2JZ9CIhzsYRX4mjaK5IyX9eldUdCmga16zlgQxyrj5pt9kzuj2C02w== jsonify@~0.0.0: version "0.0.0" From cc5dd0c98b552de951487d4ffb7ba80aa084baf1 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Fri, 21 Feb 2020 15:30:01 +0100 Subject: [PATCH 021/200] refactor build --- build/azure-pipelines/product-compile.yml | 10 +-- build/lib/compilation.js | 12 ---- build/lib/compilation.ts | 15 ----- .../build/postinstall.js | 26 ++++++++ extensions/github-authentication/package.json | 61 ++++++++++--------- 5 files changed, 62 insertions(+), 62 deletions(-) create mode 100644 extensions/github-authentication/build/postinstall.js diff --git a/build/azure-pipelines/product-compile.yml b/build/azure-pipelines/product-compile.yml index e527b8b134f..58611074190 100644 --- a/build/azure-pipelines/product-compile.yml +++ b/build/azure-pipelines/product-compile.yml @@ -77,6 +77,11 @@ steps: yarn postinstall displayName: Run postinstall scripts condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true'), eq(variables['CacheRestored'], 'true')) + env: + OSS_GITHUB_ID: "a5d3c261b032765a78de" + OSS_GITHUB_SECRET: $(oss-github-client-secret) + INSIDERS_GITHUB_ID: "31f02627809389d9f111" + INSIDERS_GITHUB_SECRET: $(insiders-github-client-secret) # Mixin must run before optimize, because the CSS loader will # inline small SVGs @@ -116,11 +121,6 @@ steps: yarn gulp minify-vscode-reh-web displayName: Compile condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) - env: - OSS_GITHUB_ID: "a5d3c261b032765a78de" - OSS_GITHUB_SECRET: $(oss-github-client-secret) - INSIDERS_GITHUB_ID: "31f02627809389d9f111" - INSIDERS_GITHUB_SECRET: $(insiders-github-client-secret) - script: | set -e diff --git a/build/lib/compilation.js b/build/lib/compilation.js index 96ef00c0657..59bf1a250f6 100644 --- a/build/lib/compilation.js +++ b/build/lib/compilation.js @@ -74,7 +74,6 @@ function compileTask(src, out, build) { if (src === 'src') { generator.execute(); } - generateGitHubAuthConfig(); return srcPipe .pipe(generator.stream) .pipe(compile()) @@ -97,17 +96,6 @@ function watchTask(out, build) { } exports.watchTask = watchTask; const REPO_SRC_FOLDER = path.join(__dirname, '../../src'); -function generateGitHubAuthConfig() { - const schemes = ['OSS', 'INSIDERS']; - let content = {}; - schemes.forEach(scheme => { - content[scheme] = { - id: process.env[`${scheme}_GITHUB_ID`], - secret: process.env[`${scheme}_GITHUB_SECRET`] - }; - }); - fs.writeFileSync(path.join(__dirname, '../../extensions/github-authentication/src/common/config.json'), JSON.stringify(content)); -} class MonacoGenerator { constructor(isWatch) { this._executeSoonTimer = null; diff --git a/build/lib/compilation.ts b/build/lib/compilation.ts index 0d225d89972..578fae31a19 100644 --- a/build/lib/compilation.ts +++ b/build/lib/compilation.ts @@ -88,8 +88,6 @@ export function compileTask(src: string, out: string, build: boolean): () => Nod generator.execute(); } - generateGitHubAuthConfig(); - return srcPipe .pipe(generator.stream) .pipe(compile()) @@ -117,19 +115,6 @@ export function watchTask(out: string, build: boolean): () => NodeJS.ReadWriteSt const REPO_SRC_FOLDER = path.join(__dirname, '../../src'); -function generateGitHubAuthConfig() { - const schemes = ['OSS', 'INSIDERS']; - let content: { [key: string]: { id?: string, secret?: string }} = {}; - schemes.forEach(scheme => { - content[scheme] = { - id: process.env[`${scheme}_GITHUB_ID`], - secret: process.env[`${scheme}_GITHUB_SECRET`] - }; - }); - - fs.writeFileSync(path.join(__dirname, '../../extensions/github-authentication/src/common/config.json'), JSON.stringify(content)); -} - class MonacoGenerator { private readonly _isWatch: boolean; public readonly stream: NodeJS.ReadWriteStream; diff --git a/extensions/github-authentication/build/postinstall.js b/extensions/github-authentication/build/postinstall.js new file mode 100644 index 00000000000..23228f45e8a --- /dev/null +++ b/extensions/github-authentication/build/postinstall.js @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +const fs = require('fs'); +const path = require('path'); + +const schemes = ['OSS', 'INSIDERS']; + +function main() { + let content = {}; + + for (const scheme of schemes) { + const id = process.env[`${scheme}_GITHUB_ID`]; + const secret = process.env[`${scheme}_GITHUB_SECRET`]; + + if (id && secret) { + content[scheme] = { id, secret }; + } + } + + fs.writeFileSync(path.join(__dirname, '../src/common/config.json'), JSON.stringify(content)); +} + +main(); diff --git a/extensions/github-authentication/package.json b/extensions/github-authentication/package.json index 8060b63f2f5..254516ee527 100644 --- a/extensions/github-authentication/package.json +++ b/extensions/github-authentication/package.json @@ -1,32 +1,33 @@ { - "name": "github-authentication", - "displayName": "%displayName%", - "description": "%description%", - "publisher": "vscode", - "version": "0.0.1", - "engines": { - "vscode": "^1.41.0" - }, - "enableProposedApi": true, - "categories": [ - "Other" - ], - "activationEvents": [ - "*" - ], - "main": "./out/extension.js", - "scripts": { - "vscode:prepublish": "npm run compile", - "compile": "gulp compile-extension:github-authentication", - "watch": "gulp watch-extension:github-authentication" - }, - "dependencies": { - "uuid": "^3.3.3" - }, - "devDependencies": { - "@types/keytar": "^4.4.2", - "@types/node": "^10.12.21", - "@types/uuid": "^3.4.6", - "typescript": "^3.7.5" - } + "name": "github-authentication", + "displayName": "%displayName%", + "description": "%description%", + "publisher": "vscode", + "version": "0.0.1", + "engines": { + "vscode": "^1.41.0" + }, + "enableProposedApi": true, + "categories": [ + "Other" + ], + "activationEvents": [ + "*" + ], + "main": "./out/extension.js", + "scripts": { + "vscode:prepublish": "npm run compile", + "compile": "gulp compile-extension:github-authentication", + "watch": "gulp watch-extension:github-authentication", + "postinstall": "node build/postinstall.js" + }, + "dependencies": { + "uuid": "^3.3.3" + }, + "devDependencies": { + "@types/keytar": "^4.4.2", + "@types/node": "^10.12.21", + "@types/uuid": "^3.4.6", + "typescript": "^3.7.5" + } } From df403aa082304c4b522a5a867fb5a4f6a525e6eb Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 21 Feb 2020 12:46:56 +0100 Subject: [PATCH 022/200] show the name of the formatter that's running long, #90851 --- src/vs/editor/contrib/format/format.ts | 3 +++ src/vs/editor/contrib/format/formatActions.ts | 3 ++- src/vs/platform/progress/common/progress.ts | 4 ++-- .../workbench/contrib/codeEditor/browser/saveParticipants.ts | 2 +- src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts | 4 ++-- src/vs/workbench/test/browser/workbenchTestServices.ts | 4 ++-- 6 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/vs/editor/contrib/format/format.ts b/src/vs/editor/contrib/format/format.ts index 84e4007bce6..83683c0043e 100644 --- a/src/vs/editor/contrib/format/format.ts +++ b/src/vs/editor/contrib/format/format.ts @@ -27,6 +27,7 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import { LinkedList } from 'vs/base/common/linkedList'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { assertType } from 'vs/base/common/types'; +import { IProgressStep, IProgress } from 'vs/platform/progress/common/progress'; export function alertFormattingEdits(edits: ISingleEditOperation[]): void { @@ -209,6 +210,7 @@ export async function formatDocumentWithSelectedProvider( accessor: ServicesAccessor, editorOrModel: ITextModel | IActiveCodeEditor, mode: FormattingMode, + progress: IProgress, token: CancellationToken ): Promise { @@ -217,6 +219,7 @@ export async function formatDocumentWithSelectedProvider( const provider = getRealAndSyntheticDocumentFormattersOrdered(model); const selected = await FormattingConflicts.select(provider, model, mode); if (selected) { + progress.report({ message: nls.localize('formattingWith', "Formatting with '{0}'", selected.displayName || selected.extensionId && selected.extensionId.value || '???') }); await instaService.invokeFunction(formatDocumentWithProvider, selected, editorOrModel, mode, token); } } diff --git a/src/vs/editor/contrib/format/formatActions.ts b/src/vs/editor/contrib/format/formatActions.ts index c169060f72a..9d1c8ec0388 100644 --- a/src/vs/editor/contrib/format/formatActions.ts +++ b/src/vs/editor/contrib/format/formatActions.ts @@ -25,6 +25,7 @@ import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegis import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { onUnexpectedError } from 'vs/base/common/errors'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { Progress } from 'vs/platform/progress/common/progress'; class FormatOnType implements IEditorContribution { @@ -230,7 +231,7 @@ class FormatDocumentAction extends EditorAction { async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise { if (editor.hasModel()) { const instaService = accessor.get(IInstantiationService); - await instaService.invokeFunction(formatDocumentWithSelectedProvider, editor, FormattingMode.Explicit, CancellationToken.None); + await instaService.invokeFunction(formatDocumentWithSelectedProvider, editor, FormattingMode.Explicit, Progress.None, CancellationToken.None); } } } diff --git a/src/vs/platform/progress/common/progress.ts b/src/vs/platform/progress/common/progress.ts index 576ae6d82fb..a0cea0a3659 100644 --- a/src/vs/platform/progress/common/progress.ts +++ b/src/vs/platform/progress/common/progress.ts @@ -86,8 +86,6 @@ export interface IProgressRunner { done(): void; } -export const emptyProgress: IProgress = { report: () => { } }; - export const emptyProgressRunner: IProgressRunner = Object.freeze({ total() { }, worked() { }, @@ -100,6 +98,8 @@ export interface IProgress { export class Progress implements IProgress { + static readonly None: IProgress = Object.freeze({ report() { } }); + private _value?: T; get value(): T | undefined { return this._value; } diff --git a/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts b/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts index ea6fc68420f..36379460260 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts @@ -215,7 +215,7 @@ class FormatOnSaveParticipant implements ITextFileSaveParticipant { progress.report({ message: localize('formatting', "Formatting") }); const editorOrModel = findEditor(model, this.codeEditorService) || model; - await this.instantiationService.invokeFunction(formatDocumentWithSelectedProvider, editorOrModel, FormattingMode.Silent, token); + await this.instantiationService.invokeFunction(formatDocumentWithSelectedProvider, editorOrModel, FormattingMode.Silent, progress, token); } } diff --git a/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts b/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts index 414a94ac93b..e5ea9a06e75 100644 --- a/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts +++ b/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts @@ -18,7 +18,7 @@ import { localize } from 'vs/nls'; import { IFileService, FileSystemProviderCapabilities } from 'vs/platform/files/common/files'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ILogService } from 'vs/platform/log/common/log'; -import { IProgress, IProgressStep, emptyProgress } from 'vs/platform/progress/common/progress'; +import { IProgress, IProgressStep, Progress } from 'vs/platform/progress/common/progress'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -242,7 +242,7 @@ class BulkEdit { @IConfigurationService private readonly _configurationService: IConfigurationService ) { this._editor = editor; - this._progress = progress || emptyProgress; + this._progress = progress || Progress.None; this._edits = edits; } diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index ec6a2da19a4..14f31ca5e1a 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -90,7 +90,7 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ import { createTextBufferFactoryFromStream } from 'vs/editor/common/model/textModel'; import { IRemotePathService } from 'vs/workbench/services/path/common/remotePathService'; import { Direction } from 'vs/base/browser/ui/grid/grid'; -import { IProgressService, IProgressOptions, IProgressWindowOptions, IProgressNotificationOptions, IProgressCompositeOptions, IProgress, IProgressStep, emptyProgress } from 'vs/platform/progress/common/progress'; +import { IProgressService, IProgressOptions, IProgressWindowOptions, IProgressNotificationOptions, IProgressCompositeOptions, IProgress, IProgressStep, Progress } from 'vs/platform/progress/common/progress'; import { IWorkingCopyFileService, WorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService'; import { UndoRedoService } from 'vs/platform/undoRedo/common/undoRedoService'; import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; @@ -260,7 +260,7 @@ export class TestProgressService implements IProgressService { task: (progress: IProgress) => Promise, onDidCancel?: ((choice?: number | undefined) => void) | undefined ): Promise { - return task(emptyProgress); + return task(Progress.None); } } From 8b955610988d28cf98ae316dc1a3d44f599a358f Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 21 Feb 2020 15:35:01 +0100 Subject: [PATCH 023/200] show names of formatting edits and code actions providers when they block save. also add links that links that open the settings editor for each feature, #90851 --- src/vs/editor/common/modes.ts | 3 + .../editor/contrib/codeAction/codeAction.ts | 6 +- .../contrib/codeAction/codeActionModel.ts | 4 +- .../codeAction/test/codeAction.test.ts | 21 +++---- src/vs/editor/contrib/format/format.ts | 6 +- .../editor/contrib/hover/modesContentHover.ts | 2 + .../api/browser/mainThreadLanguageFeatures.ts | 5 +- .../workbench/api/common/extHost.protocol.ts | 2 +- .../api/common/extHostLanguageFeatures.ts | 2 +- .../codeEditor/browser/saveParticipants.ts | 57 +++++++++++++------ .../markers/browser/markersTreeViewer.ts | 3 +- .../common/textFileSaveParticipant.ts | 2 +- .../api/extHostLanguageFeatures.test.ts | 9 +-- 13 files changed, 80 insertions(+), 42 deletions(-) diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index b7dfb8adf9b..c348245a8ec 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -635,6 +635,9 @@ export interface CodeActionList extends IDisposable { * @internal */ export interface CodeActionProvider { + + displayName?: string + /** * Provide commands for the given document and range. */ diff --git a/src/vs/editor/contrib/codeAction/codeAction.ts b/src/vs/editor/contrib/codeAction/codeAction.ts index 82ab87cacec..6f1d102c76f 100644 --- a/src/vs/editor/contrib/codeAction/codeAction.ts +++ b/src/vs/editor/contrib/codeAction/codeAction.ts @@ -16,6 +16,7 @@ import { ITextModel } from 'vs/editor/common/model'; import * as modes from 'vs/editor/common/modes'; import { IModelService } from 'vs/editor/common/services/modelService'; import { CodeActionFilter, CodeActionKind, CodeActionTrigger, filtersAction, mayIncludeActionsOfKind } from './types'; +import { IProgress, Progress } from 'vs/platform/progress/common/progress'; export const codeActionCommandId = 'editor.action.codeAction'; export const refactorCommandId = 'editor.action.refactor'; @@ -70,7 +71,8 @@ export function getCodeActions( model: ITextModel, rangeOrSelection: Range | Selection, trigger: CodeActionTrigger, - token: CancellationToken + progress: IProgress, + token: CancellationToken, ): Promise { const filter = trigger.filter || {}; @@ -85,6 +87,7 @@ export function getCodeActions( const disposables = new DisposableStore(); const promises = providers.map(async provider => { try { + progress.report(provider); const providedCodeActions = await provider.provideCodeActions(model, rangeOrSelection, codeActionContext, cts.token); if (providedCodeActions) { disposables.add(providedCodeActions); @@ -210,6 +213,7 @@ registerLanguageCommand('_executeCodeActionProvider', async function (accessor, model, validatedRangeOrSelection, { type: modes.CodeActionTriggerType.Manual, filter: { includeSourceActions: true, include: kind && kind.value ? new CodeActionKind(kind.value) : undefined } }, + Progress.None, CancellationToken.None); setTimeout(() => codeActionSet.dispose(), 100); diff --git a/src/vs/editor/contrib/codeAction/codeActionModel.ts b/src/vs/editor/contrib/codeAction/codeActionModel.ts index e8645271f35..fc35f1398ed 100644 --- a/src/vs/editor/contrib/codeAction/codeActionModel.ts +++ b/src/vs/editor/contrib/codeAction/codeActionModel.ts @@ -14,7 +14,7 @@ import { Selection } from 'vs/editor/common/core/selection'; import { CodeActionProviderRegistry, CodeActionTriggerType } from 'vs/editor/common/modes'; import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IMarkerService } from 'vs/platform/markers/common/markers'; -import { IEditorProgressService } from 'vs/platform/progress/common/progress'; +import { IEditorProgressService, Progress } from 'vs/platform/progress/common/progress'; import { getCodeActions, CodeActionSet } from './codeAction'; import { CodeActionTrigger } from './types'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; @@ -213,7 +213,7 @@ export class CodeActionModel extends Disposable { return; } - const actions = createCancelablePromise(token => getCodeActions(model, trigger.selection, trigger.trigger, token)); + const actions = createCancelablePromise(token => getCodeActions(model, trigger.selection, trigger.trigger, Progress.None, token)); if (this._progressService && trigger.trigger.type === CodeActionTriggerType.Manual) { this._progressService.showWhile(actions, 250); } diff --git a/src/vs/editor/contrib/codeAction/test/codeAction.test.ts b/src/vs/editor/contrib/codeAction/test/codeAction.test.ts index 0af6b11bb24..ad59e4ff308 100644 --- a/src/vs/editor/contrib/codeAction/test/codeAction.test.ts +++ b/src/vs/editor/contrib/codeAction/test/codeAction.test.ts @@ -12,6 +12,7 @@ import { getCodeActions } from 'vs/editor/contrib/codeAction/codeAction'; import { CodeActionKind } from 'vs/editor/contrib/codeAction/types'; import { IMarkerData, MarkerSeverity } from 'vs/platform/markers/common/markers'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { Progress } from 'vs/platform/progress/common/progress'; function staticCodeActionProvider(...actions: modes.CodeAction[]): modes.CodeActionProvider { return new class implements modes.CodeActionProvider { @@ -125,7 +126,7 @@ suite('CodeAction', () => { testData.tsLint.abc ]; - const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Manual }, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Manual }, Progress.None, CancellationToken.None); assert.equal(actions.length, 6); assert.deepEqual(actions, expected); }); @@ -140,20 +141,20 @@ suite('CodeAction', () => { disposables.add(modes.CodeActionProviderRegistry.register('fooLang', provider)); { - const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a') } }, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a') } }, Progress.None, CancellationToken.None); assert.equal(actions.length, 2); assert.strictEqual(actions[0].title, 'a'); assert.strictEqual(actions[1].title, 'a.b'); } { - const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a.b') } }, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a.b') } }, Progress.None, CancellationToken.None); assert.equal(actions.length, 1); assert.strictEqual(actions[0].title, 'a.b'); } { - const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a.b.c') } }, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a.b.c') } }, Progress.None, CancellationToken.None); assert.equal(actions.length, 0); } }); @@ -172,7 +173,7 @@ suite('CodeAction', () => { disposables.add(modes.CodeActionProviderRegistry.register('fooLang', provider)); - const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a') } }, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Auto, filter: { include: new CodeActionKind('a') } }, Progress.None, CancellationToken.None); assert.equal(actions.length, 1); assert.strictEqual(actions[0].title, 'a'); }); @@ -186,13 +187,13 @@ suite('CodeAction', () => { disposables.add(modes.CodeActionProviderRegistry.register('fooLang', provider)); { - const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Auto }, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Auto }, Progress.None, CancellationToken.None); assert.equal(actions.length, 1); assert.strictEqual(actions[0].title, 'b'); } { - const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Auto, filter: { include: CodeActionKind.Source, includeSourceActions: true } }, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: modes.CodeActionTriggerType.Auto, filter: { include: CodeActionKind.Source, includeSourceActions: true } }, Progress.None, CancellationToken.None); assert.equal(actions.length, 1); assert.strictEqual(actions[0].title, 'a'); } @@ -214,7 +215,7 @@ suite('CodeAction', () => { excludes: [CodeActionKind.Source], includeSourceActions: true, } - }, CancellationToken.None); + }, Progress.None, CancellationToken.None); assert.equal(actions.length, 1); assert.strictEqual(actions[0].title, 'b'); } @@ -250,7 +251,7 @@ suite('CodeAction', () => { include: baseType, excludes: [subType], } - }, CancellationToken.None); + }, Progress.None, CancellationToken.None); assert.strictEqual(didInvoke, false); assert.equal(actions.length, 1); assert.strictEqual(actions[0].title, 'a'); @@ -275,7 +276,7 @@ suite('CodeAction', () => { filter: { include: CodeActionKind.QuickFix } - }, CancellationToken.None); + }, Progress.None, CancellationToken.None); assert.strictEqual(actions.length, 0); assert.strictEqual(wasInvoked, false); }); diff --git a/src/vs/editor/contrib/format/format.ts b/src/vs/editor/contrib/format/format.ts index 83683c0043e..b18b6d63ad8 100644 --- a/src/vs/editor/contrib/format/format.ts +++ b/src/vs/editor/contrib/format/format.ts @@ -27,7 +27,7 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import { LinkedList } from 'vs/base/common/linkedList'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { assertType } from 'vs/base/common/types'; -import { IProgressStep, IProgress } from 'vs/platform/progress/common/progress'; +import { IProgress } from 'vs/platform/progress/common/progress'; export function alertFormattingEdits(edits: ISingleEditOperation[]): void { @@ -210,7 +210,7 @@ export async function formatDocumentWithSelectedProvider( accessor: ServicesAccessor, editorOrModel: ITextModel | IActiveCodeEditor, mode: FormattingMode, - progress: IProgress, + progress: IProgress, token: CancellationToken ): Promise { @@ -219,7 +219,7 @@ export async function formatDocumentWithSelectedProvider( const provider = getRealAndSyntheticDocumentFormattersOrdered(model); const selected = await FormattingConflicts.select(provider, model, mode); if (selected) { - progress.report({ message: nls.localize('formattingWith', "Formatting with '{0}'", selected.displayName || selected.extensionId && selected.extensionId.value || '???') }); + progress.report(selected); await instaService.invokeFunction(formatDocumentWithProvider, selected, editorOrModel, mode, token); } } diff --git a/src/vs/editor/contrib/hover/modesContentHover.ts b/src/vs/editor/contrib/hover/modesContentHover.ts index 3e41cf7b901..3f03ee3e821 100644 --- a/src/vs/editor/contrib/hover/modesContentHover.ts +++ b/src/vs/editor/contrib/hover/modesContentHover.ts @@ -40,6 +40,7 @@ import { IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { Constants } from 'vs/base/common/uint'; import { textLinkForeground } from 'vs/platform/theme/common/colorRegistry'; +import { Progress } from 'vs/platform/progress/common/progress'; const $ = dom.$; @@ -626,6 +627,7 @@ export class ModesContentHoverWidget extends ContentHoverWidget { this._editor.getModel()!, new Range(marker.startLineNumber, marker.startColumn, marker.endLineNumber, marker.endColumn), markerCodeActionTrigger, + Progress.None, cancellationToken); }); } diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index 8851128af0e..4287e53a8c0 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -273,7 +273,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha // --- quick fix - $registerQuickFixSupport(handle: number, selector: IDocumentFilterDto[], metadata: ICodeActionProviderMetadataDto): void { + $registerQuickFixSupport(handle: number, selector: IDocumentFilterDto[], metadata: ICodeActionProviderMetadataDto, displayName: string): void { this._registrations.set(handle, modes.CodeActionProviderRegistry.register(selector, { provideCodeActions: async (model: ITextModel, rangeOrSelection: EditorRange | Selection, context: modes.CodeActionContext, token: CancellationToken): Promise => { const listDto = await this._proxy.$provideCodeActions(handle, model.uri, rangeOrSelection, context, token); @@ -290,7 +290,8 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha }; }, providedCodeActionKinds: metadata.providedKinds, - documentation: metadata.documentation + documentation: metadata.documentation, + displayName })); } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index e13afc5a623..951127eec0a 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -361,7 +361,7 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable { $registerEvaluatableExpressionProvider(handle: number, selector: IDocumentFilterDto[]): void; $registerDocumentHighlightProvider(handle: number, selector: IDocumentFilterDto[]): void; $registerReferenceSupport(handle: number, selector: IDocumentFilterDto[]): void; - $registerQuickFixSupport(handle: number, selector: IDocumentFilterDto[], metadata: ICodeActionProviderMetadataDto): void; + $registerQuickFixSupport(handle: number, selector: IDocumentFilterDto[], metadata: ICodeActionProviderMetadataDto, displayName: string): void; $registerDocumentFormattingSupport(handle: number, selector: IDocumentFilterDto[], extensionId: ExtensionIdentifier, displayName: string): void; $registerRangeFormattingSupport(handle: number, selector: IDocumentFilterDto[], extensionId: ExtensionIdentifier, displayName: string): void; $registerOnTypeFormattingSupport(handle: number, selector: IDocumentFilterDto[], autoFormatTriggerCharacters: string[], extensionId: ExtensionIdentifier): void; diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index c0522477836..479a5126b3c 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -1617,7 +1617,7 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF kind: x.kind.value, command: this._commands.converter.toInternal(x.command, store), })) - }); + }, ExtHostLanguageFeatures._extLabel(extension)); store.add(this._createDisposable(handle)); return store; } diff --git a/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts b/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts index 36379460260..3386ae55dc6 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts @@ -13,7 +13,7 @@ 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 { ITextModel } from 'vs/editor/common/model'; -import { CodeAction, CodeActionTriggerType } from 'vs/editor/common/modes'; +import { CodeActionTriggerType, DocumentFormattingEditProvider, CodeActionProvider } from 'vs/editor/common/modes'; import { getCodeActions } from 'vs/editor/contrib/codeAction/codeAction'; import { applyCodeAction } from 'vs/editor/contrib/codeAction/codeActionCommands'; import { CodeActionKind } from 'vs/editor/contrib/codeAction/types'; @@ -22,7 +22,7 @@ import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2 import { localize } from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IProgressStep, IProgress } from 'vs/platform/progress/common/progress'; +import { IProgressStep, IProgress, Progress } from 'vs/platform/progress/common/progress'; import { IResolvedTextFileEditorModel, ITextFileService, ITextFileSaveParticipant } from 'vs/workbench/services/textfile/common/textfiles'; import { SaveReason } from 'vs/workbench/common/editor'; import { Disposable } from 'vs/base/common/lifecycle'; @@ -213,9 +213,17 @@ class FormatOnSaveParticipant implements ITextFileSaveParticipant { return undefined; } - progress.report({ message: localize('formatting', "Formatting") }); + const nestedProgress = new Progress(provider => { + progress.report({ + message: localize( + 'formatting', + "Running '{0}' Formatter ([configure](command:workbench.action.openSettings?%5B%22editor.formatOnSave%22%5D)).", + provider.displayName || provider.extensionId && provider.extensionId.value || '???' + ) + }); + }); const editorOrModel = findEditor(model, this.codeEditorService) || model; - await this.instantiationService.invokeFunction(formatDocumentWithSelectedProvider, editorOrModel, FormattingMode.Silent, progress, token); + await this.instantiationService.invokeFunction(formatDocumentWithSelectedProvider, editorOrModel, FormattingMode.Silent, nestedProgress, token); } } @@ -262,14 +270,37 @@ class CodeActionOnSaveParticipant implements ITextFileSaveParticipant { .map(x => new CodeActionKind(x)); progress.report({ message: localize('codeaction', "Quick Fixes") }); - await this.applyOnSaveActions(model, codeActionsOnSave, excludedActions, token); + await this.applyOnSaveActions(model, codeActionsOnSave, excludedActions, progress, token); } - private async applyOnSaveActions(model: ITextModel, codeActionsOnSave: readonly CodeActionKind[], excludes: readonly CodeActionKind[], token: CancellationToken): Promise { + private async applyOnSaveActions(model: ITextModel, codeActionsOnSave: readonly CodeActionKind[], excludes: readonly CodeActionKind[], progress: IProgress, token: CancellationToken): Promise { + + const getActionProgress = new class implements IProgress { + private _names: string[] = []; + private _report(): void { + progress.report({ + message: localize( + 'codeaction.get', + "Getting code actions from '{0}' ([configure](command:workbench.action.openSettings?%5B%22editor.codeActionsOnSave%22%5D)).", + this._names.map(name => `'${name}'`).join(', ') + ) + }); + } + report(provider: CodeActionProvider) { + if (provider.displayName) { + this._names.push(provider.displayName); + this._report(); + } + } + }; + for (const codeActionKind of codeActionsOnSave) { - const actionsToRun = await this.getActionsToRun(model, codeActionKind, excludes, token); + const actionsToRun = await this.getActionsToRun(model, codeActionKind, excludes, getActionProgress, token); try { - await this.applyCodeActions(actionsToRun.validActions); + for (const action of actionsToRun.validActions) { + progress.report({ message: localize('codeAction.apply', "Applying code action '{0}'.", action.title) }); + await this.instantiationService.invokeFunction(applyCodeAction, action); + } } catch { // Failure to apply a code action should not block other on save actions } finally { @@ -278,17 +309,11 @@ class CodeActionOnSaveParticipant implements ITextFileSaveParticipant { } } - private async applyCodeActions(actionsToRun: readonly CodeAction[]) { - for (const action of actionsToRun) { - await this.instantiationService.invokeFunction(applyCodeAction, action); - } - } - - private getActionsToRun(model: ITextModel, codeActionKind: CodeActionKind, excludes: readonly CodeActionKind[], token: CancellationToken) { + private getActionsToRun(model: ITextModel, codeActionKind: CodeActionKind, excludes: readonly CodeActionKind[], progress: IProgress, token: CancellationToken) { return getCodeActions(model, model.getFullModelRange(), { type: CodeActionTriggerType.Auto, filter: { include: codeActionKind, excludes: excludes, includeSourceActions: true }, - }, token); + }, progress, token); } } diff --git a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts index 55627367a15..abd5d1d00fe 100644 --- a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts +++ b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts @@ -51,6 +51,7 @@ import { IFileService } from 'vs/platform/files/common/files'; import { domEvent } from 'vs/base/browser/event'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; +import { Progress } from 'vs/platform/progress/common/progress'; export type TreeElement = ResourceMarkers | Marker | RelatedInformation; @@ -642,7 +643,7 @@ export class MarkerViewModel extends Disposable { this.codeActionsPromise = createCancelablePromise(cancellationToken => { return getCodeActions(model, new Range(this.marker.range.startLineNumber, this.marker.range.startColumn, this.marker.range.endLineNumber, this.marker.range.endColumn), { type: CodeActionTriggerType.Manual, filter: { include: CodeActionKind.QuickFix } - }, cancellationToken).then(actions => { + }, Progress.None, cancellationToken).then(actions => { return this._register(actions); }); }); diff --git a/src/vs/workbench/services/textfile/common/textFileSaveParticipant.ts b/src/vs/workbench/services/textfile/common/textFileSaveParticipant.ts index eb9ce782052..0c40afacfa3 100644 --- a/src/vs/workbench/services/textfile/common/textFileSaveParticipant.ts +++ b/src/vs/workbench/services/textfile/common/textFileSaveParticipant.ts @@ -33,7 +33,7 @@ export class TextFileSaveParticipant extends Disposable { const cts = new CancellationTokenSource(token); return this.progressService.withProgress({ - title: localize('saveParticipants', "Running Save Participants for '{0}'", model.name), + title: localize('saveParticipants', "Saving '{0}'", model.name), location: ProgressLocation.Notification, cancellable: true, delay: model.isDirty() ? 3000 : 5000 diff --git a/src/vs/workbench/test/browser/api/extHostLanguageFeatures.test.ts b/src/vs/workbench/test/browser/api/extHostLanguageFeatures.test.ts index 07285eb64e6..28e991cb356 100644 --- a/src/vs/workbench/test/browser/api/extHostLanguageFeatures.test.ts +++ b/src/vs/workbench/test/browser/api/extHostLanguageFeatures.test.ts @@ -48,6 +48,7 @@ import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerServ import { dispose } from 'vs/base/common/lifecycle'; import { withNullAsUndefined } from 'vs/base/common/types'; import { NullApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService'; +import { Progress } from 'vs/platform/progress/common/progress'; const defaultSelector = { scheme: 'far' }; const model: ITextModel = TextModel.createFromString( @@ -590,7 +591,7 @@ suite('ExtHostLanguageFeatures', function () { })); await rpcProtocol.sync(); - const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: modes.CodeActionTriggerType.Manual }, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: modes.CodeActionTriggerType.Manual }, Progress.None, CancellationToken.None); assert.equal(actions.length, 2); const [first, second] = actions; assert.equal(first.title, 'Testing1'); @@ -614,7 +615,7 @@ suite('ExtHostLanguageFeatures', function () { })); await rpcProtocol.sync(); - const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: modes.CodeActionTriggerType.Manual }, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: modes.CodeActionTriggerType.Manual }, Progress.None, CancellationToken.None); assert.equal(actions.length, 1); const [first] = actions; assert.equal(first.title, 'Testing1'); @@ -637,7 +638,7 @@ suite('ExtHostLanguageFeatures', function () { })); await rpcProtocol.sync(); - const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: modes.CodeActionTriggerType.Manual }, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: modes.CodeActionTriggerType.Manual }, Progress.None, CancellationToken.None); assert.equal(actions.length, 1); }); @@ -655,7 +656,7 @@ suite('ExtHostLanguageFeatures', function () { })); await rpcProtocol.sync(); - const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: modes.CodeActionTriggerType.Manual }, CancellationToken.None); + const { validActions: actions } = await getCodeActions(model, model.getFullModelRange(), { type: modes.CodeActionTriggerType.Manual }, Progress.None, CancellationToken.None); assert.equal(actions.length, 1); }); From 6cea9545d2782653bbd1e5ad1921f6c41e31102b Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 21 Feb 2020 15:57:56 +0100 Subject: [PATCH 024/200] Notifications: indicate progress notification if hidden in notification center (fix #90274) --- .../notifications/notificationsStatus.ts | 86 +++++++++++++------ .../notifications/notificationsToasts.ts | 34 ++++++-- .../notifications/notificationsViewer.ts | 2 +- src/vs/workbench/browser/workbench.ts | 6 +- src/vs/workbench/common/notifications.ts | 14 ++- 5 files changed, 98 insertions(+), 44 deletions(-) diff --git a/src/vs/workbench/browser/parts/notifications/notificationsStatus.ts b/src/vs/workbench/browser/parts/notifications/notificationsStatus.ts index f0be978d55c..71a82ef3aae 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsStatus.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsStatus.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { INotificationsModel, INotificationChangeEvent, NotificationChangeType, INotificationViewItem, IStatusMessageChangeEvent, StatusMessageChangeType, IStatusMessageViewItem } from 'vs/workbench/common/notifications'; +import { INotificationsModel, INotificationChangeEvent, NotificationChangeType, IStatusMessageChangeEvent, StatusMessageChangeType, IStatusMessageViewItem } from 'vs/workbench/common/notifications'; import { IStatusbarService, StatusbarAlignment, IStatusbarEntryAccessor, IStatusbarEntry } from 'vs/workbench/services/statusbar/common/statusbar'; import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; import { HIDE_NOTIFICATIONS_CENTER, SHOW_NOTIFICATIONS_CENTER } from 'vs/workbench/browser/parts/notifications/notificationsCommands'; @@ -12,11 +12,12 @@ import { localize } from 'vs/nls'; export class NotificationsStatus extends Disposable { private notificationsCenterStatusItem: IStatusbarEntryAccessor | undefined; - private currentNotifications = new Set(); + private newNotificationsCount = 0; private currentStatusMessage: [IStatusMessageViewItem, IDisposable] | undefined; - private isNotificationsCenterVisible: boolean | undefined; + private isNotificationsCenterVisible: boolean = false; + private isNotificationsToastsVisible: boolean = false; constructor( private model: INotificationsModel, @@ -39,28 +40,39 @@ export class NotificationsStatus extends Disposable { } private onDidChangeNotification(e: INotificationChangeEvent): void { - if (this.isNotificationsCenterVisible) { - return; // no change if notification center is visible - } - - // Notification got Added - if (e.kind === NotificationChangeType.ADD) { - this.currentNotifications.add(e.item); - } - - // Notification got Removed - else if (e.kind === NotificationChangeType.REMOVE) { - this.currentNotifications.delete(e.item); + + // Consider a notification as unread as long as it only + // appeared as toast and not in the notification center + if (!this.isNotificationsCenterVisible) { + if (e.kind === NotificationChangeType.ADD) { + this.newNotificationsCount++; + } else if (e.kind === NotificationChangeType.REMOVE) { + this.newNotificationsCount--; + } } + // Update in status bar this.updateNotificationsCenterStatusItem(); } private updateNotificationsCenterStatusItem(): void { + + // Figure out how many notifications have progress only if neither + // toasts are visible nor center is visible. In that case we still + // want to give a hint to the user that something is running. + let notificationsInProgress = 0; + if (!this.isNotificationsCenterVisible && !this.isNotificationsToastsVisible) { + for (const notification of this.model.notifications) { + if (notification.hasProgress) { + notificationsInProgress++; + } + } + } + const statusProperties: IStatusbarEntry = { - text: this.currentNotifications.size === 0 ? '$(bell)' : '$(bell-dot)', + text: `${this.newNotificationsCount === 0 ? '$(bell)' : '$(bell-dot)'}${notificationsInProgress > 0 ? ' $(sync~spin)' : ''}`, command: this.isNotificationsCenterVisible ? HIDE_NOTIFICATIONS_CENTER : SHOW_NOTIFICATIONS_CENTER, - tooltip: this.getTooltip(), + tooltip: this.getTooltip(notificationsInProgress), showBeak: this.isNotificationsCenterVisible }; @@ -77,7 +89,7 @@ export class NotificationsStatus extends Disposable { } } - private getTooltip(): string { + private getTooltip(notificationsInProgress: number): string { if (this.isNotificationsCenterVisible) { return localize('hideNotifications', "Hide Notifications"); } @@ -86,23 +98,45 @@ export class NotificationsStatus extends Disposable { return localize('zeroNotifications', "No Notifications"); } - if (this.currentNotifications.size === 0) { - return localize('noNotifications', "No New Notifications"); + if (notificationsInProgress === 0) { + if (this.newNotificationsCount === 0) { + return localize('noNotifications', "No New Notifications"); + } + + if (this.newNotificationsCount === 1) { + return localize('oneNotification', "1 New Notification"); + } + + return localize('notifications', "{0} New Notifications", this.newNotificationsCount); } - if (this.currentNotifications.size === 1) { - return localize('oneNotification', "1 New Notification"); + if (this.newNotificationsCount === 0) { + return localize('noNotificationsWithProgress', "No New Notifications ({0} in progress)", notificationsInProgress); } - return localize('notifications', "{0} New Notifications", this.currentNotifications.size); + if (this.newNotificationsCount === 1) { + return localize('oneNotificationWithProgress', "1 New Notification ({0} in progress)", notificationsInProgress); + } + + return localize('notificationsWithProgress', "{0} New Notifications ({0} in progress)", this.newNotificationsCount, notificationsInProgress); } - update(isCenterVisible: boolean): void { + update(isCenterVisible: boolean, isToastsVisible: boolean): void { + let updateNotificationsCenterStatusItem = false; + if (this.isNotificationsCenterVisible !== isCenterVisible) { this.isNotificationsCenterVisible = isCenterVisible; + this.newNotificationsCount = 0; // Showing the notification center resets the unread counter to 0 + updateNotificationsCenterStatusItem = true; + } - // Showing the notification center resets the counter to 0 - this.currentNotifications.clear(); + if (this.isNotificationsToastsVisible !== isToastsVisible) { + this.isNotificationsToastsVisible = isToastsVisible; + updateNotificationsCenterStatusItem = true; + } + + // Update in status bar as needed + if (updateNotificationsCenterStatusItem) { this.updateNotificationsCenterStatusItem(); } } diff --git a/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts b/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts index 4252392d139..95e287fbd49 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts @@ -9,7 +9,7 @@ import { IDisposable, dispose, toDisposable, DisposableStore } from 'vs/base/com import { addClass, removeClass, isAncestor, addDisposableListener, EventType, Dimension } from 'vs/base/browser/dom'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { NotificationsList } from 'vs/workbench/browser/parts/notifications/notificationsList'; -import { Event } from 'vs/base/common/event'; +import { Event, Emitter } from 'vs/base/common/event'; import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService'; import { Themable, NOTIFICATIONS_TOAST_BORDER, NOTIFICATIONS_BACKGROUND } from 'vs/workbench/common/theme'; import { IThemeService } from 'vs/platform/theme/common/themeService'; @@ -53,6 +53,12 @@ export class NotificationsToasts extends Themable implements INotificationsToast return intervals; })(); + private readonly _onDidChangeVisibility = this._register(new Emitter()); + readonly onDidChangeVisibility = this._onDidChangeVisibility.event; + + private _isVisible = false; + get isVisible(): boolean { return !!this._isVisible; } + private notificationsToastsContainer: HTMLElement | undefined; private workbenchDimensions: Dimension | undefined; private isNotificationsCenterVisible: boolean | undefined; @@ -125,11 +131,11 @@ export class NotificationsToasts extends Themable implements INotificationsToast private addToast(item: INotificationViewItem): void { if (this.isNotificationsCenterVisible) { - return; // do not show toasts while notification center is visibles + return; // do not show toasts while notification center is visible } if (item.silent) { - return; // do not show toats for silenced notifications + return; // do not show toasts for silenced notifications } // Lazily create toasts containers @@ -174,7 +180,7 @@ export class NotificationsToasts extends Themable implements INotificationsToast this.mapNotificationToToast.set(item, toast); itemDisposables.add(toDisposable(() => { - if (this.isVisible(toast) && notificationsToastsContainer) { + if (this.isToastVisible(toast) && notificationsToastsContainer) { notificationsToastsContainer.removeChild(toast.container); } })); @@ -229,6 +235,12 @@ export class NotificationsToasts extends Themable implements INotificationsToast removeClass(notificationToast, 'notification-fade-in'); addClass(notificationToast, 'notification-fade-in-done'); })); + + // Events + if (!this._isVisible) { + this._isVisible = true; + this._onDidChangeVisibility.fire(); + } } private purgeNotification(item: INotificationViewItem, notificationToastContainer: HTMLElement, notificationList: NotificationsList, disposables: DisposableStore): void { @@ -325,6 +337,12 @@ export class NotificationsToasts extends Themable implements INotificationsToast // Context Key this.notificationsToastsVisibleContextKey.set(false); + + // Events + if (this._isVisible) { + this._isVisible = false; + this._onDidChangeVisibility.fire(); + } } hide(): void { @@ -441,12 +459,12 @@ export class NotificationsToasts extends Themable implements INotificationsToast notificationToasts.push(toast); break; case ToastVisibility.HIDDEN: - if (!this.isVisible(toast)) { + if (!this.isToastVisible(toast)) { notificationToasts.push(toast); } break; case ToastVisibility.VISIBLE: - if (this.isVisible(toast)) { + if (this.isToastVisible(toast)) { notificationToasts.push(toast); } break; @@ -534,7 +552,7 @@ export class NotificationsToasts extends Themable implements INotificationsToast } private setVisibility(toast: INotificationToast, visible: boolean): void { - if (this.isVisible(toast) === visible) { + if (this.isToastVisible(toast) === visible) { return; } @@ -546,7 +564,7 @@ export class NotificationsToasts extends Themable implements INotificationsToast } } - private isVisible(toast: INotificationToast): boolean { + private isToastVisible(toast: INotificationToast): boolean { return !!toast.container.parentElement; } } diff --git a/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts b/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts index b2b8f0ce9ff..41063188c35 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts @@ -455,7 +455,7 @@ export class NotificationTemplateRenderer extends Disposable { private renderProgress(notification: INotificationViewItem): void { // Return early if the item has no progress - if (!notification.hasProgress()) { + if (!notification.hasProgress) { this.template.progress.stop().hide(); return; diff --git a/src/vs/workbench/browser/workbench.ts b/src/vs/workbench/browser/workbench.ts index a24435cd241..58afab7134e 100644 --- a/src/vs/workbench/browser/workbench.ts +++ b/src/vs/workbench/browser/workbench.ts @@ -384,10 +384,14 @@ export class Workbench extends Layout { // Visibility this._register(notificationsCenter.onDidChangeVisibility(() => { - notificationsStatus.update(notificationsCenter.isVisible); + notificationsStatus.update(notificationsCenter.isVisible, notificationsToasts.isVisible); notificationsToasts.update(notificationsCenter.isVisible); })); + this._register(notificationsToasts.onDidChangeVisibility(() => { + notificationsStatus.update(notificationsCenter.isVisible, notificationsToasts.isVisible); + })); + // Register Commands registerNotificationCommands(notificationsCenter, notificationsToasts); } diff --git a/src/vs/workbench/common/notifications.ts b/src/vs/workbench/common/notifications.ts index ef22a9c992e..353baa3bd6e 100644 --- a/src/vs/workbench/common/notifications.ts +++ b/src/vs/workbench/common/notifications.ts @@ -261,6 +261,7 @@ export interface INotificationViewItem { readonly expanded: boolean; readonly canCollapse: boolean; + readonly hasProgress: boolean; readonly onDidChangeExpansion: Event; readonly onDidClose: Event; @@ -270,9 +271,6 @@ export interface INotificationViewItem { collapse(skipEvents?: boolean): void; toggle(): void; - hasProgress(): boolean; - hasPrompt(): boolean; - updateSeverity(severity: Severity): void; updateMessage(message: NotificationMessage): void; updateActions(actions?: INotificationActions): void; @@ -495,7 +493,7 @@ export class NotificationViewItem extends Disposable implements INotificationVie } get canCollapse(): boolean { - return !this.hasPrompt(); + return !this.hasPrompt; } get expanded(): boolean { @@ -511,7 +509,7 @@ export class NotificationViewItem extends Disposable implements INotificationVie return true; // explicitly sticky } - const hasPrompt = this.hasPrompt(); + const hasPrompt = this.hasPrompt; if ( (hasPrompt && this._severity === Severity.Error) || // notification errors with actions are sticky (!hasPrompt && this._expanded) || // notifications that got expanded are sticky @@ -527,7 +525,7 @@ export class NotificationViewItem extends Disposable implements INotificationVie return !!this._silent; } - hasPrompt(): boolean { + private get hasPrompt(): boolean { if (!this._actions) { return false; } @@ -539,7 +537,7 @@ export class NotificationViewItem extends Disposable implements INotificationVie return this._actions.primary.length > 0; } - hasProgress(): boolean { + get hasProgress(): boolean { return !!this._progress; } @@ -621,7 +619,7 @@ export class NotificationViewItem extends Disposable implements INotificationVie } equals(other: INotificationViewItem): boolean { - if (this.hasProgress() || other.hasProgress()) { + if (this.hasProgress || other.hasProgress) { return false; } From 51fb67b18216ddcae216335736d80a3e86d5fe85 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 21 Feb 2020 16:06:58 +0100 Subject: [PATCH 025/200] Fix #86917 --- .../userDataSync/common/settingsMerge.ts | 8 ++-- .../userDataSync/common/settingsSync.ts | 38 +++++++++++++++---- .../userDataSync/common/userDataSync.ts | 12 +++++- .../userDataSync/common/userDataSyncIpc.ts | 5 +++ .../test/common/userDataSyncClient.ts | 6 ++- .../preferences/browser/settingsTree.ts | 34 ++++++++++++----- .../userDataSync/common/userDataSyncUtil.ts | 8 +++- 7 files changed, 85 insertions(+), 26 deletions(-) diff --git a/src/vs/platform/userDataSync/common/settingsMerge.ts b/src/vs/platform/userDataSync/common/settingsMerge.ts index 4942add903f..83e87e281d3 100644 --- a/src/vs/platform/userDataSync/common/settingsMerge.ts +++ b/src/vs/platform/userDataSync/common/settingsMerge.ts @@ -10,8 +10,8 @@ import { values } from 'vs/base/common/map'; import { IStringDictionary } from 'vs/base/common/collections'; import { FormattingOptions, Edit, getEOL } from 'vs/base/common/jsonFormatter'; import * as contentUtil from 'vs/platform/userDataSync/common/content'; -import { IConflictSetting, CONFIGURATION_SYNC_STORE_KEY } from 'vs/platform/userDataSync/common/userDataSync'; -import { firstIndex } from 'vs/base/common/arrays'; +import { IConflictSetting } from 'vs/platform/userDataSync/common/userDataSync'; +import { firstIndex, distinct } from 'vs/base/common/arrays'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { startsWith } from 'vs/base/common/strings'; @@ -22,7 +22,7 @@ export interface IMergeResult { conflictsSettings: IConflictSetting[]; } -export function getIgnoredSettings(configurationService: IConfigurationService, settingsContent?: string): string[] { +export function getIgnoredSettings(defaultIgnoredSettings: string[], configurationService: IConfigurationService, settingsContent?: string): string[] { let value: string[] = []; if (settingsContent) { const setting = parse(settingsContent); @@ -42,7 +42,7 @@ export function getIgnoredSettings(configurationService: IConfigurationService, } } } - return [CONFIGURATION_SYNC_STORE_KEY, ...added].filter(setting => removed.indexOf(setting) === -1); + return distinct([...defaultIgnoredSettings, ...added,].filter(setting => removed.indexOf(setting) === -1)); } diff --git a/src/vs/platform/userDataSync/common/settingsSync.ts b/src/vs/platform/userDataSync/common/settingsSync.ts index 7e1834fcedb..552e646cca6 100644 --- a/src/vs/platform/userDataSync/common/settingsSync.ts +++ b/src/vs/platform/userDataSync/common/settingsSync.ts @@ -21,6 +21,7 @@ import { edit } from 'vs/platform/userDataSync/common/content'; import { IFileSyncPreviewResult, AbstractJsonFileSynchroniser, IRemoteUserData } from 'vs/platform/userDataSync/common/abstractSynchronizer'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { URI } from 'vs/base/common/uri'; +import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; interface ISettingsSyncContent { settings: string; @@ -54,6 +55,7 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement @IConfigurationService configurationService: IConfigurationService, @IUserDataSyncEnablementService userDataSyncEnablementService: IUserDataSyncEnablementService, @ITelemetryService telemetryService: ITelemetryService, + @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, ) { super(environmentService.settingsResource, SyncSource.Settings, fileService, environmentService, userDataSyncStoreService, userDataSyncEnablementService, telemetryService, logService, userDataSyncUtilService, configurationService); } @@ -94,7 +96,8 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement const fileContent = await this.getLocalFileContent(); const formatUtils = await this.getFormattingOptions(); // Update ignored settings from local file content - const content = updateIgnoredSettings(remoteSettingsSyncContent.settings, fileContent ? fileContent.value.toString() : '{}', getIgnoredSettings(this.configurationService), formatUtils); + const ignoredSettings = await this.getIgnoredSettings(); + const content = updateIgnoredSettings(remoteSettingsSyncContent.settings, fileContent ? fileContent.value.toString() : '{}', ignoredSettings, formatUtils); this.syncPreviewResultPromise = createCancelablePromise(() => Promise.resolve({ fileContent, remoteUserData, @@ -136,7 +139,8 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement if (fileContent !== null) { const formatUtils = await this.getFormattingOptions(); // Remove ignored settings - const content = updateIgnoredSettings(fileContent.value.toString(), '{}', getIgnoredSettings(this.configurationService), formatUtils); + const ignoredSettings = await this.getIgnoredSettings(); + const content = updateIgnoredSettings(fileContent.value.toString(), '{}', ignoredSettings, formatUtils); const lastSyncUserData = await this.getLastSyncUserData(); const remoteUserData = await this.getRemoteUserData(lastSyncUserData); @@ -192,7 +196,8 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement if (preview && content !== null) { const formatUtils = await this.getFormattingOptions(); // remove ignored settings from the remote content for preview - content = updateIgnoredSettings(content, '{}', getIgnoredSettings(this.configurationService), formatUtils); + const ignoredSettings = await this.getIgnoredSettings(); + content = updateIgnoredSettings(content, '{}', ignoredSettings, formatUtils); } return content; } @@ -203,7 +208,8 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement this.cancel(); const formatUtils = await this.getFormattingOptions(); // Add ignored settings from local file content - content = updateIgnoredSettings(content, preview.fileContent ? preview.fileContent.value.toString() : '{}', getIgnoredSettings(this.configurationService), formatUtils); + const ignoredSettings = await this.getIgnoredSettings(); + content = updateIgnoredSettings(content, preview.fileContent ? preview.fileContent.value.toString() : '{}', ignoredSettings, formatUtils); this.syncPreviewResultPromise = createCancelablePromise(async () => ({ ...preview, content })); await this.apply(true); this.setStatus(SyncStatus.Idle); @@ -271,7 +277,8 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement const formatUtils = await this.getFormattingOptions(); // Update ignored settings from remote const remoteSettingsSyncContent = this.getSettingsSyncContent(remoteUserData); - content = updateIgnoredSettings(content, remoteSettingsSyncContent ? remoteSettingsSyncContent.settings : '{}', getIgnoredSettings(this.configurationService, content), formatUtils); + const ignoredSettings = await this.getIgnoredSettings(content); + content = updateIgnoredSettings(content, remoteSettingsSyncContent ? remoteSettingsSyncContent.settings : '{}', ignoredSettings, formatUtils); this.logService.trace('Settings: Updating remote settings...'); remoteUserData = await this.updateRemoteUserData(JSON.stringify({ settings: content }), forcePush ? null : remoteUserData.ref); this.logService.info('Settings: Updated remote settings'); @@ -317,7 +324,8 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement const localContent: string = fileContent ? fileContent.value.toString() : '{}'; this.validateContent(localContent); this.logService.trace('Settings: Merging remote settings with local settings...'); - const result = merge(localContent, remoteSettingsSyncContent.settings, lastSettingsSyncContent ? lastSettingsSyncContent.settings : null, getIgnoredSettings(this.configurationService), resolvedConflicts, formattingOptions); + const ignoredSettings = await this.getIgnoredSettings(); + const result = merge(localContent, remoteSettingsSyncContent.settings, lastSettingsSyncContent ? lastSettingsSyncContent.settings : null, ignoredSettings, resolvedConflicts, formattingOptions); content = result.localContent || result.remoteContent; hasLocalChanged = result.localContent !== null; hasRemoteChanged = result.remoteContent !== null; @@ -334,7 +342,8 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement if (content && !token.isCancellationRequested) { // Remove the ignored settings from the preview. - const previewContent = updateIgnoredSettings(content, '{}', getIgnoredSettings(this.configurationService), formattingOptions); + const ignoredSettings = await this.getIgnoredSettings(); + const previewContent = updateIgnoredSettings(content, '{}', ignoredSettings, formattingOptions); await this.fileService.writeFile(this.environmentService.settingsSyncPreviewResource, VSBuffer.fromString(previewContent)); } @@ -356,6 +365,21 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement return null; } + private _defaultIgnoredSettings: Promise | undefined = undefined; + protected async getIgnoredSettings(content?: string): Promise { + if (!this._defaultIgnoredSettings) { + this._defaultIgnoredSettings = this.userDataSyncUtilService.resolveDefaultIgnoredSettings(); + const disposable = Event.any( + Event.filter(this.extensionManagementService.onDidInstallExtension, (e => !!e.gallery)), + Event.filter(this.extensionManagementService.onDidUninstallExtension, (e => !e.error)))(() => { + disposable.dispose(); + this._defaultIgnoredSettings = undefined; + }); + } + const defaultIgnoredSettings = await this._defaultIgnoredSettings; + return getIgnoredSettings(defaultIgnoredSettings, this.configurationService, content); + } + private validateContent(content: string): void { if (this.hasErrors(content)) { throw new UserDataSyncError(localize('errorInvalidSettings', "Unable to sync settings as there are errors/warning in settings file."), UserDataSyncErrorCode.LocalInvalidContent, this.source); diff --git a/src/vs/platform/userDataSync/common/userDataSync.ts b/src/vs/platform/userDataSync/common/userDataSync.ts index 240d6ff1334..0d5dbdd3ef5 100644 --- a/src/vs/platform/userDataSync/common/userDataSync.ts +++ b/src/vs/platform/userDataSync/common/userDataSync.ts @@ -36,6 +36,12 @@ export interface ISyncConfiguration { } } +export function getDefaultIgnoredSettings(): string[] { + const allSettings = Registry.as(ConfigurationExtensions.Configuration).getConfigurationProperties(); + const machineSettings = Object.keys(allSettings).filter(setting => allSettings[setting].scope === ConfigurationScope.MACHINE || allSettings[setting].scope === ConfigurationScope.MACHINE_OVERRIDABLE); + return [CONFIGURATION_SYNC_STORE_KEY, ...machineSettings]; +} + export function registerConfiguration(): IDisposable { const ignoredSettingsSchemaId = 'vscode://schemas/ignoredSettings'; const ignoredExtensionsSchemaId = 'vscode://schemas/ignoredExtensions'; @@ -105,11 +111,12 @@ export function registerConfiguration(): IDisposable { }); const jsonRegistry = Registry.as(JSONExtensions.JSONContribution); const registerIgnoredSettingsSchema = () => { + const defaultIgnoreSettings = getDefaultIgnoredSettings().filter(s => s !== CONFIGURATION_SYNC_STORE_KEY); const ignoredSettingsSchema: IJSONSchema = { items: { type: 'string', - enum: Object.keys(allSettings.properties) - } + enum: [...Object.keys(allSettings.properties).filter(setting => defaultIgnoreSettings.indexOf(setting) === -1), ...defaultIgnoreSettings.map(setting => `-${setting}`)] + }, }; jsonRegistry.registerSchema(ignoredSettingsSchemaId, ignoredSettingsSchema); }; @@ -313,6 +320,7 @@ export interface IUserDataSyncUtilService { _serviceBrand: undefined; resolveUserBindings(userbindings: string[]): Promise>; resolveFormattingOptions(resource: URI): Promise; + resolveDefaultIgnoredSettings(): Promise; } export const IUserDataSyncLogService = createDecorator('IUserDataSyncLogService'); diff --git a/src/vs/platform/userDataSync/common/userDataSyncIpc.ts b/src/vs/platform/userDataSync/common/userDataSyncIpc.ts index 32547f516f2..08ac1ae6acf 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncIpc.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncIpc.ts @@ -101,6 +101,7 @@ export class UserDataSycnUtilServiceChannel implements IServerChannel { call(context: any, command: string, args?: any): Promise { switch (command) { + case 'resolveDefaultIgnoredSettings': return this.service.resolveDefaultIgnoredSettings(); case 'resolveUserKeybindings': return this.service.resolveUserBindings(args[0]); case 'resolveFormattingOptions': return this.service.resolveFormattingOptions(URI.revive(args[0])); } @@ -115,6 +116,10 @@ export class UserDataSyncUtilServiceClient implements IUserDataSyncUtilService { constructor(private readonly channel: IChannel) { } + async resolveDefaultIgnoredSettings(): Promise { + return this.channel.call('resolveDefaultIgnoredSettings'); + } + async resolveUserBindings(userbindings: string[]): Promise> { return this.channel.call('resolveUserKeybindings', [userbindings]); } diff --git a/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts b/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts index 396fc07139e..a050f896b78 100644 --- a/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts +++ b/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts @@ -6,7 +6,7 @@ import { IRequestService } from 'vs/platform/request/common/request'; import { IRequestOptions, IRequestContext, IHeaders } from 'vs/base/parts/request/common/request'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { IUserData, ResourceKey, IUserDataManifest, ALL_RESOURCE_KEYS, IUserDataSyncLogService, IUserDataSyncStoreService, IUserDataSyncUtilService, IUserDataSyncEnablementService, ISettingsSyncService, IUserDataSyncService } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserData, ResourceKey, IUserDataManifest, ALL_RESOURCE_KEYS, IUserDataSyncLogService, IUserDataSyncStoreService, IUserDataSyncUtilService, IUserDataSyncEnablementService, ISettingsSyncService, IUserDataSyncService, getDefaultIgnoredSettings } from 'vs/platform/userDataSync/common/userDataSync'; import { bufferToStream, VSBuffer } from 'vs/base/common/buffer'; import { generateUuid } from 'vs/base/common/uuid'; import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService'; @@ -231,6 +231,10 @@ export class TestUserDataSyncUtilService implements IUserDataSyncUtilService { _serviceBrand: any; + async resolveDefaultIgnoredSettings(): Promise { + return getDefaultIgnoredSettings(); + } + async resolveUserBindings(userbindings: string[]): Promise> { const keys: IStringDictionary = {}; for (const keybinding of userbindings) { diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index 8d1f6e2ab07..c1a674828f9 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -49,7 +49,7 @@ import { ExcludeSettingWidget, IListChangeEvent, IListDataItem, ListSettingWidge import { SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU } from 'vs/workbench/contrib/preferences/common/preferences'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { ISetting, ISettingsGroup, SettingValueType } from 'vs/workbench/services/preferences/common/preferences'; -import { IUserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataSyncEnablementService, getDefaultIgnoredSettings } from 'vs/platform/userDataSync/common/userDataSync'; const $ = DOM.$; @@ -1238,7 +1238,7 @@ export class SettingTreeRenderers { private getActionsForSetting(setting: ISetting): IAction[] { const enableSync = this._userDataSyncEnablementService.isEnabled(); return enableSync && !setting.disallowSyncIgnore ? - [this._instantiationService.createInstance(StopSyncingSettingAction, setting)] : + [this._instantiationService.createInstance(SyncSettingAction, setting)] : []; } @@ -1622,7 +1622,7 @@ class CopySettingAsJSONAction extends Action { } } -class StopSyncingSettingAction extends Action { +class SyncSettingAction extends Action { static readonly ID = 'settings.stopSyncingSetting'; static readonly LABEL = localize('stopSyncingSetting', "Sync This Setting"); @@ -1630,24 +1630,38 @@ class StopSyncingSettingAction extends Action { private readonly setting: ISetting, @IConfigurationService private readonly configService: IConfigurationService, ) { - super(StopSyncingSettingAction.ID, StopSyncingSettingAction.LABEL); + super(SyncSettingAction.ID, SyncSettingAction.LABEL); + this._register(Event.filter(configService.onDidChangeConfiguration, e => e.affectsConfiguration('sync.ignoredSettings'))(() => this.update())); this.update(); } - update() { - const ignoredSettings = getIgnoredSettings(this.configService); + async update() { + const ignoredSettings = getIgnoredSettings(getDefaultIgnoredSettings(), this.configService); this.checked = !ignoredSettings.includes(this.setting.key); } async run(): Promise { + // first remove the current setting completely from ignored settings let currentValue = [...this.configService.getValue('sync.ignoredSettings')]; - if (this.checked) { - currentValue.push(this.setting.key); - } else { - currentValue = currentValue.filter(v => v !== this.setting.key); + currentValue = currentValue.filter(v => v !== this.setting.key && v !== `-${this.setting.key}`); + + const defaultIgnoredSettings = getDefaultIgnoredSettings(); + const isDefaultIgnored = defaultIgnoredSettings.includes(this.setting.key); + const askedToSync = !this.checked; + + // If asked to sync, then add only if it is ignored by default + if (askedToSync && isDefaultIgnored) { + currentValue.push(`-${this.setting.key}`); } + + // If asked not to sync, then add only if it is not ignored by default + if (!askedToSync && !isDefaultIgnored) { + currentValue.push(this.setting.key); + } + this.configService.updateValue('sync.ignoredSettings', currentValue.length ? currentValue : undefined, ConfigurationTarget.USER); return Promise.resolve(undefined); } + } diff --git a/src/vs/workbench/services/userDataSync/common/userDataSyncUtil.ts b/src/vs/workbench/services/userDataSync/common/userDataSyncUtil.ts index c168d05c76a..0fbeb2a56c8 100644 --- a/src/vs/workbench/services/userDataSync/common/userDataSyncUtil.ts +++ b/src/vs/workbench/services/userDataSync/common/userDataSyncUtil.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { IUserDataSyncUtilService } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataSyncUtilService, getDefaultIgnoredSettings } from 'vs/platform/userDataSync/common/userDataSync'; import { IStringDictionary } from 'vs/base/common/collections'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { FormattingOptions } from 'vs/base/common/jsonFormatter'; @@ -23,7 +23,11 @@ class UserDataSyncUtilService implements IUserDataSyncUtilService { @ITextResourceConfigurationService private readonly textResourceConfigurationService: ITextResourceConfigurationService, ) { } - public async resolveUserBindings(userBindings: string[]): Promise> { + async resolveDefaultIgnoredSettings(): Promise { + return getDefaultIgnoredSettings(); + } + + async resolveUserBindings(userBindings: string[]): Promise> { const keys: IStringDictionary = {}; for (const userbinding of userBindings) { keys[userbinding] = this.keybindingsService.resolveUserBinding(userbinding).map(part => part.getUserSettingsLabel()).join(' '); From 487db98febd7fc6f8c9e1e4980cfc5b1b7239f4e Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 21 Feb 2020 16:21:55 +0100 Subject: [PATCH 026/200] tweaks to message --- src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index df54d4357c9..27f8e31504a 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -476,7 +476,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo } await this.handleFirstTimeSync(); this.userDataSyncEnablementService.setEnablement(true); - this.notificationService.info(localize('sync turned on', "Sync is turned on and from now on sycing will be done automatically.")); + this.notificationService.info(localize('sync turned on', "Sync will happen automatically from now on.")); } private getConfigureSyncQuickPickItems(): ConfigureSyncQuickPickItem[] { From eb36a76298b9fca0c63e62f4ebf754fd1eba6ec3 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 21 Feb 2020 16:35:22 +0100 Subject: [PATCH 027/200] List items should have role 'listitem' fixes #90876 --- src/vs/base/browser/ui/list/listView.ts | 2 +- src/vs/base/browser/ui/tree/abstractTree.ts | 2 +- src/vs/base/browser/ui/tree/asyncDataTree.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 7b520501582..4209457c0f0 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -554,7 +554,7 @@ export class ListView implements ISpliceable, IDisposable { if (!item.row) { item.row = this.cache.alloc(item.templateId); - const role = this.ariaProvider.getRole ? this.ariaProvider.getRole(item.element) : 'treeitem'; + const role = this.ariaProvider.getRole ? this.ariaProvider.getRole(item.element) : 'listitem'; item.row!.domNode!.setAttribute('role', role); const checked = this.ariaProvider.isChecked ? this.ariaProvider.isChecked(item.element) : undefined; if (typeof checked !== 'undefined') { diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 9e584efcc56..fc78cfe1c28 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -196,7 +196,7 @@ function asListOptions(modelProvider: () => ITreeModel { return options.ariaProvider!.getRole!(node.element); - } : undefined + } : () => 'treeitem' } }; } diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index 6ddb3fe5cb8..0cd13de8079 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -267,7 +267,7 @@ function asObjectTreeOptions(options?: IAsyncDataTreeOpt }, getRole: options.ariaProvider!.getRole ? (el) => { return options.ariaProvider!.getRole!(el.element as T); - } : undefined, + } : () => 'treeitem', isChecked: options.ariaProvider!.isChecked ? (e) => { return options.ariaProvider?.isChecked!(e.element as T); } : undefined From a1f61fd0fec2e3dbe159264f4b69148554fcbfd9 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Fri, 21 Feb 2020 16:38:19 +0100 Subject: [PATCH 028/200] welcome views: telemetry events for buttons --- src/vs/workbench/browser/parts/views/customView.ts | 4 +++- .../workbench/browser/parts/views/viewPaneContainer.ts | 10 +++++++++- .../workbench/contrib/bulkEdit/browser/bulkEditPane.ts | 4 +++- .../workbench/contrib/comments/browser/commentsView.ts | 6 ++++-- .../workbench/contrib/debug/browser/breakpointsView.ts | 4 +++- .../workbench/contrib/debug/browser/callStackView.ts | 4 +++- .../contrib/debug/browser/loadedScriptsView.ts | 4 +++- src/vs/workbench/contrib/debug/browser/repl.ts | 4 +++- src/vs/workbench/contrib/debug/browser/startView.ts | 6 ++++-- .../workbench/contrib/debug/browser/variablesView.ts | 4 +++- .../contrib/debug/browser/watchExpressionsView.ts | 4 +++- .../contrib/extensions/browser/extensionsViews.ts | 4 ++-- .../workbench/contrib/files/browser/views/emptyView.ts | 6 ++++-- .../contrib/files/browser/views/explorerView.ts | 4 ++-- .../contrib/files/browser/views/openEditorsView.ts | 4 ++-- .../workbench/contrib/markers/browser/markersView.ts | 4 ++-- .../workbench/contrib/outline/browser/outlinePane.ts | 4 +++- src/vs/workbench/contrib/output/browser/outputView.ts | 3 ++- src/vs/workbench/contrib/remote/browser/remote.ts | 3 ++- src/vs/workbench/contrib/remote/browser/tunnelView.ts | 6 ++++-- src/vs/workbench/contrib/scm/browser/mainPane.ts | 4 +++- src/vs/workbench/contrib/scm/browser/repositoryPane.ts | 4 +++- src/vs/workbench/contrib/scm/browser/scmViewlet.ts | 3 ++- src/vs/workbench/contrib/search/browser/searchView.ts | 4 +++- .../workbench/contrib/terminal/browser/terminalView.ts | 2 +- .../workbench/contrib/timeline/browser/timelinePane.ts | 4 +++- 26 files changed, 79 insertions(+), 34 deletions(-) diff --git a/src/vs/workbench/browser/parts/views/customView.ts b/src/vs/workbench/browser/parts/views/customView.ts index 32ccfff21ee..e4cfff2a63c 100644 --- a/src/vs/workbench/browser/parts/views/customView.ts +++ b/src/vs/workbench/browser/parts/views/customView.ts @@ -43,6 +43,7 @@ import { CollapseAllAction } from 'vs/base/browser/ui/tree/treeDefaults'; import { isFalsyOrWhitespace } from 'vs/base/common/strings'; import { SIDE_BAR_BACKGROUND, PANEL_BACKGROUND } from 'vs/workbench/common/theme'; import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; export class CustomTreeViewPane extends ViewPane { @@ -59,8 +60,9 @@ export class CustomTreeViewPane extends ViewPane { @IInstantiationService instantiationService: IInstantiationService, @IOpenerService openerService: IOpenerService, @IThemeService themeService: IThemeService, + @ITelemetryService telemetryService: ITelemetryService, ) { - super({ ...(options as IViewPaneOptions), ariaHeaderLabel: options.title, titleMenuId: MenuId.ViewTitle }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService); + super({ ...(options as IViewPaneOptions), ariaHeaderLabel: options.title, titleMenuId: MenuId.ViewTitle }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); const { treeView } = (Registry.as(Extensions.ViewsRegistry).getView(options.id)); this.treeView = treeView; this._register(this.treeView.onDidChangeActions(() => this.updateActions(), this)); diff --git a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts index ec2e7f8df87..595cc112e62 100644 --- a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts +++ b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts @@ -66,6 +66,10 @@ export class DraggedViewIdentifier { } } +type WelcomeActionClassification = { + viewId: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; + uri: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; +}; const viewsRegistry = Registry.as(ViewContainerExtensions.ViewsRegistry); @@ -199,6 +203,7 @@ export abstract class ViewPane extends Pane implements IView { @IInstantiationService protected instantiationService: IInstantiationService, @IOpenerService protected openerService: IOpenerService, @IThemeService protected themeService: IThemeService, + @ITelemetryService protected telemetryService: ITelemetryService, ) { super(options); @@ -416,7 +421,10 @@ export abstract class ViewPane extends Pane implements IView { } else if (linkedText.nodes.length === 1) { const button = new Button(p, { title: node.title }); button.label = node.label; - button.onDidClick(_ => this.openerService.open(node.href), null, disposables); + button.onDidClick(_ => { + this.telemetryService.publicLog2<{ viewId: string, uri: string }, WelcomeActionClassification>('views.welcomeAction', { viewId: this.id, uri: node.href }); + this.openerService.open(node.href); + }, null, disposables); disposables.add(button); disposables.add(attachButtonStyler(button, this.themeService)); } else { diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts index 0970caecdbb..808c009ed03 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditPane.ts @@ -38,6 +38,7 @@ import type { IAsyncDataTreeViewState } from 'vs/base/browser/ui/tree/asyncDataT import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IViewDescriptorService } from 'vs/workbench/common/views'; import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; const enum State { Data = 'data', @@ -86,10 +87,11 @@ export class BulkEditPane extends ViewPane { @IConfigurationService configurationService: IConfigurationService, @IOpenerService openerService: IOpenerService, @IThemeService themeService: IThemeService, + @ITelemetryService telemetryService: ITelemetryService, ) { super( { ...options, titleMenuId: MenuId.BulkEditTitle }, - keybindingService, contextMenuService, configurationService, _contextKeyService, viewDescriptorService, _instaService, openerService, themeService + keybindingService, contextMenuService, configurationService, _contextKeyService, viewDescriptorService, _instaService, openerService, themeService, telemetryService ); this.element.classList.add('bulk-edit-panel', 'show-file-icons'); diff --git a/src/vs/workbench/contrib/comments/browser/commentsView.ts b/src/vs/workbench/contrib/comments/browser/commentsView.ts index bd76b690c66..700a75d9d5d 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsView.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsView.ts @@ -27,6 +27,7 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; export class CommentsPanel extends ViewPane { @@ -51,9 +52,10 @@ export class CommentsPanel extends ViewPane { @IKeybindingService keybindingService: IKeybindingService, @IOpenerService openerService: IOpenerService, @IThemeService themeService: IThemeService, - @ICommentService private readonly commentService: ICommentService + @ICommentService private readonly commentService: ICommentService, + @ITelemetryService telemetryService: ITelemetryService, ) { - super({ ...(options as IViewPaneOptions), id: COMMENTS_VIEW_ID, ariaHeaderLabel: COMMENTS_VIEW_TITLE }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService); + super({ ...(options as IViewPaneOptions), id: COMMENTS_VIEW_ID, ariaHeaderLabel: COMMENTS_VIEW_TITLE }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); } public renderBody(container: HTMLElement): void { diff --git a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts index d64f9b7134e..da287aae45d 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts @@ -35,6 +35,7 @@ import { Gesture } from 'vs/base/browser/touch'; import { IViewDescriptorService } from 'vs/workbench/common/views'; import { TextEditorSelectionRevealType } from 'vs/platform/editor/common/editor'; import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; const $ = dom.$; @@ -71,8 +72,9 @@ export class BreakpointsView extends ViewPane { @IViewDescriptorService viewDescriptorService: IViewDescriptorService, @IContextKeyService contextKeyService: IContextKeyService, @IOpenerService openerService: IOpenerService, + @ITelemetryService telemetryService: ITelemetryService, ) { - super({ ...(options as IViewPaneOptions), ariaHeaderLabel: nls.localize('breakpointsSection', "Breakpoints Section") }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService); + super({ ...(options as IViewPaneOptions), ariaHeaderLabel: nls.localize('breakpointsSection', "Breakpoints Section") }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); this.minimumBodySize = this.maximumBodySize = getExpandedBodySize(this.debugService.getModel()); this._register(this.debugService.getModel().onDidChangeBreakpoints(() => this.onBreakpointsChange())); diff --git a/src/vs/workbench/contrib/debug/browser/callStackView.ts b/src/vs/workbench/contrib/debug/browser/callStackView.ts index 5ae2c952efe..377041b25f9 100644 --- a/src/vs/workbench/contrib/debug/browser/callStackView.ts +++ b/src/vs/workbench/contrib/debug/browser/callStackView.ts @@ -37,6 +37,7 @@ import { CollapseAction } from 'vs/workbench/browser/viewlet'; import { IViewDescriptorService } from 'vs/workbench/common/views'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; const $ = dom.$; @@ -101,8 +102,9 @@ export class CallStackView extends ViewPane { @IContextKeyService readonly contextKeyService: IContextKeyService, @IOpenerService openerService: IOpenerService, @IThemeService themeService: IThemeService, + @ITelemetryService telemetryService: ITelemetryService, ) { - super({ ...(options as IViewPaneOptions), ariaHeaderLabel: nls.localize('callstackSection', "Call Stack Section") }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService); + super({ ...(options as IViewPaneOptions), ariaHeaderLabel: nls.localize('callstackSection', "Call Stack Section") }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); this.callStackItemType = CONTEXT_CALLSTACK_ITEM_TYPE.bindTo(contextKeyService); this.contributedContextMenu = menuService.createMenu(MenuId.DebugCallStackContext, contextKeyService); diff --git a/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts b/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts index ceb03abfcc9..af223d0a938 100644 --- a/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts +++ b/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts @@ -39,6 +39,7 @@ import type { ICompressibleTreeRenderer } from 'vs/base/browser/ui/tree/objectTr import { IViewDescriptorService } from 'vs/workbench/common/views'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; const NEW_STYLE_COMPRESS = true; @@ -427,8 +428,9 @@ export class LoadedScriptsView extends ViewPane { @ILabelService private readonly labelService: ILabelService, @IOpenerService openerService: IOpenerService, @IThemeService themeService: IThemeService, + @ITelemetryService telemetryService: ITelemetryService, ) { - super({ ...(options as IViewPaneOptions), ariaHeaderLabel: nls.localize('loadedScriptsSection', "Loaded Scripts Section") }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService); + super({ ...(options as IViewPaneOptions), ariaHeaderLabel: nls.localize('loadedScriptsSection', "Loaded Scripts Section") }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); this.loadedScriptsItemType = CONTEXT_LOADED_SCRIPTS_ITEM_TYPE.bindTo(contextKeyService); } diff --git a/src/vs/workbench/contrib/debug/browser/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts index 338d74cc951..6ad3398983d 100644 --- a/src/vs/workbench/contrib/debug/browser/repl.ts +++ b/src/vs/workbench/contrib/debug/browser/repl.ts @@ -58,6 +58,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IViewsService, IViewDescriptorService } from 'vs/workbench/common/views'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { ReplGroup } from 'vs/workbench/contrib/debug/common/replModel'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; const $ = dom.$; @@ -110,8 +111,9 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { @IEditorService private readonly editorService: IEditorService, @IKeybindingService keybindingService: IKeybindingService, @IOpenerService openerService: IOpenerService, + @ITelemetryService telemetryService: ITelemetryService, ) { - super({ ...(options as IViewPaneOptions), id: REPL_VIEW_ID, ariaHeaderLabel: localize('debugConsole', "Debug Console") }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService); + super({ ...(options as IViewPaneOptions), id: REPL_VIEW_ID, ariaHeaderLabel: localize('debugConsole', "Debug Console") }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); this.history = new HistoryNavigator(JSON.parse(this.storageService.get(HISTORY_STORAGE_KEY, StorageScope.WORKSPACE, '[]')), 50); codeEditorService.registerDecorationType(DECORATION_KEY, {}); diff --git a/src/vs/workbench/contrib/debug/browser/startView.ts b/src/vs/workbench/contrib/debug/browser/startView.ts index ac466684def..0a501ebf5bf 100644 --- a/src/vs/workbench/contrib/debug/browser/startView.ts +++ b/src/vs/workbench/contrib/debug/browser/startView.ts @@ -23,6 +23,7 @@ import { OpenFolderAction, OpenFileAction, OpenFileFolderAction } from 'vs/workb import { isMacintosh } from 'vs/base/common/platform'; import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; const debugStartLanguageKey = 'debugStartLanguage'; const CONTEXT_DEBUG_START_LANGUAGE = new RawContextKey(debugStartLanguageKey, undefined); @@ -48,9 +49,10 @@ export class StartView extends ViewPane { @IInstantiationService instantiationService: IInstantiationService, @IViewDescriptorService viewDescriptorService: IViewDescriptorService, @IOpenerService openerService: IOpenerService, - @IStorageService storageSevice: IStorageService + @IStorageService storageSevice: IStorageService, + @ITelemetryService telemetryService: ITelemetryService, ) { - super({ ...(options as IViewPaneOptions), ariaHeaderLabel: localize('debugStart', "Debug Start Section") }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService); + super({ ...(options as IViewPaneOptions), ariaHeaderLabel: localize('debugStart', "Debug Start Section") }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); this.debugStartLanguageContext = CONTEXT_DEBUG_START_LANGUAGE.bindTo(contextKeyService); this.debuggerInterestedContext = CONTEXT_DEBUGGER_INTERESTED_IN_ACTIVE_EDITOR.bindTo(contextKeyService); diff --git a/src/vs/workbench/contrib/debug/browser/variablesView.ts b/src/vs/workbench/contrib/debug/browser/variablesView.ts index 4dd9c4bfac0..2c40b45a4e2 100644 --- a/src/vs/workbench/contrib/debug/browser/variablesView.ts +++ b/src/vs/workbench/contrib/debug/browser/variablesView.ts @@ -33,6 +33,7 @@ import { dispose } from 'vs/base/common/lifecycle'; import { IViewDescriptorService } from 'vs/workbench/common/views'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; const $ = dom.$; let forgetScopes = true; @@ -58,8 +59,9 @@ export class VariablesView extends ViewPane { @IContextKeyService contextKeyService: IContextKeyService, @IOpenerService openerService: IOpenerService, @IThemeService themeService: IThemeService, + @ITelemetryService telemetryService: ITelemetryService, ) { - super({ ...(options as IViewPaneOptions), ariaHeaderLabel: nls.localize('variablesSection', "Variables Section") }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService); + super({ ...(options as IViewPaneOptions), ariaHeaderLabel: nls.localize('variablesSection', "Variables Section") }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); // Use scheduler to prevent unnecessary flashing this.onFocusStackFrameScheduler = new RunOnceScheduler(async () => { diff --git a/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts b/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts index 99b9cc6d32e..1be23a91b3e 100644 --- a/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts +++ b/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts @@ -33,6 +33,7 @@ import { dispose } from 'vs/base/common/lifecycle'; import { IViewDescriptorService } from 'vs/workbench/common/views'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; const MAX_VALUE_RENDER_LENGTH_IN_VIEWLET = 1024; let ignoreVariableSetEmitter = false; @@ -55,8 +56,9 @@ export class WatchExpressionsView extends ViewPane { @IContextKeyService contextKeyService: IContextKeyService, @IOpenerService openerService: IOpenerService, @IThemeService themeService: IThemeService, + @ITelemetryService telemetryService: ITelemetryService, ) { - super({ ...(options as IViewPaneOptions), ariaHeaderLabel: nls.localize('watchExpressionsSection', "Watch Expressions Section") }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService); + super({ ...(options as IViewPaneOptions), ariaHeaderLabel: nls.localize('watchExpressionsSection', "Watch Expressions Section") }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); this.onWatchExpressionsUpdatedScheduler = new RunOnceScheduler(() => { this.needsRefresh = false; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts index 0821aeea684..9055d5f8b6e 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts @@ -102,7 +102,7 @@ export class ExtensionsListView extends ViewPane { @IExtensionsWorkbenchService protected extensionsWorkbenchService: IExtensionsWorkbenchService, @IEditorService private readonly editorService: IEditorService, @IExtensionTipsService protected tipsService: IExtensionTipsService, - @ITelemetryService private readonly telemetryService: ITelemetryService, + @ITelemetryService telemetryService: ITelemetryService, @IConfigurationService configurationService: IConfigurationService, @IWorkspaceContextService protected contextService: IWorkspaceContextService, @IExperimentService private readonly experimentService: IExperimentService, @@ -114,7 +114,7 @@ export class ExtensionsListView extends ViewPane { @IMenuService private readonly menuService: IMenuService, @IOpenerService openerService: IOpenerService, ) { - super({ ...(options as IViewPaneOptions), ariaHeaderLabel: options.title, showActionsAlways: true }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService); + super({ ...(options as IViewPaneOptions), ariaHeaderLabel: options.title, showActionsAlways: true }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); this.server = options.server; } diff --git a/src/vs/workbench/contrib/files/browser/views/emptyView.ts b/src/vs/workbench/contrib/files/browser/views/emptyView.ts index e9296281846..212fe31a042 100644 --- a/src/vs/workbench/contrib/files/browser/views/emptyView.ts +++ b/src/vs/workbench/contrib/files/browser/views/emptyView.ts @@ -19,6 +19,7 @@ import { ILabelService } from 'vs/platform/label/common/label'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IViewDescriptorService } from 'vs/workbench/common/views'; import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; export class EmptyView extends ViewPane { @@ -36,9 +37,10 @@ export class EmptyView extends ViewPane { @IConfigurationService configurationService: IConfigurationService, @ILabelService private labelService: ILabelService, @IContextKeyService contextKeyService: IContextKeyService, - @IOpenerService openerService: IOpenerService + @IOpenerService openerService: IOpenerService, + @ITelemetryService telemetryService: ITelemetryService, ) { - super({ ...(options as IViewPaneOptions), ariaHeaderLabel: nls.localize('explorerSection', "Explorer Section: No Folder Opened") }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService); + super({ ...(options as IViewPaneOptions), ariaHeaderLabel: nls.localize('explorerSection', "Explorer Section: No Folder Opened") }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); this._register(this.contextService.onDidChangeWorkbenchState(() => this.refreshTitle())); this._register(this.labelService.onDidChangeFormatters(() => this.refreshTitle())); diff --git a/src/vs/workbench/contrib/files/browser/views/explorerView.ts b/src/vs/workbench/contrib/files/browser/views/explorerView.ts index 1af95f4a23c..d6e128f90f0 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerView.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerView.ts @@ -165,14 +165,14 @@ export class ExplorerView extends ViewPane { @ILabelService private readonly labelService: ILabelService, @IThemeService protected themeService: IWorkbenchThemeService, @IMenuService private readonly menuService: IMenuService, - @ITelemetryService private readonly telemetryService: ITelemetryService, + @ITelemetryService telemetryService: ITelemetryService, @IExplorerService private readonly explorerService: IExplorerService, @IStorageService private readonly storageService: IStorageService, @IClipboardService private clipboardService: IClipboardService, @IFileService private readonly fileService: IFileService, @IOpenerService openerService: IOpenerService, ) { - super({ ...(options as IViewPaneOptions), id: ExplorerView.ID, ariaHeaderLabel: nls.localize('explorerSection', "Explorer Section: {0}", labelService.getWorkspaceLabel(contextService.getWorkspace())) }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService); + super({ ...(options as IViewPaneOptions), id: ExplorerView.ID, ariaHeaderLabel: nls.localize('explorerSection', "Explorer Section: {0}", labelService.getWorkspaceLabel(contextService.getWorkspace())) }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); this.resourceContext = instantiationService.createInstance(ResourceContextKey); this._register(this.resourceContext); diff --git a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts index da798f2d1a8..35cd7738074 100644 --- a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts +++ b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts @@ -78,7 +78,7 @@ export class OpenEditorsView extends ViewPane { @IKeybindingService keybindingService: IKeybindingService, @IContextKeyService private readonly contextKeyService: IContextKeyService, @IThemeService themeService: IThemeService, - @ITelemetryService private readonly telemetryService: ITelemetryService, + @ITelemetryService telemetryService: ITelemetryService, @IMenuService private readonly menuService: IMenuService, @IWorkingCopyService private readonly workingCopyService: IWorkingCopyService, @IFilesConfigurationService private readonly filesConfigurationService: IFilesConfigurationService, @@ -87,7 +87,7 @@ export class OpenEditorsView extends ViewPane { super({ ...(options as IViewPaneOptions), ariaHeaderLabel: nls.localize({ key: 'openEditosrSection', comment: ['Open is an adjective'] }, "Open Editors Section"), - }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService); + }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); this.structuralRefreshDelay = 0; this.listRefreshScheduler = new RunOnceScheduler(() => { diff --git a/src/vs/workbench/contrib/markers/browser/markersView.ts b/src/vs/workbench/contrib/markers/browser/markersView.ts index 008b211f282..9b4453be046 100644 --- a/src/vs/workbench/contrib/markers/browser/markersView.ts +++ b/src/vs/workbench/contrib/markers/browser/markersView.ts @@ -97,7 +97,7 @@ export class MarkersView extends ViewPane implements IMarkerFilterController { @IViewDescriptorService viewDescriptorService: IViewDescriptorService, @IEditorService private readonly editorService: IEditorService, @IConfigurationService configurationService: IConfigurationService, - @ITelemetryService private readonly telemetryService: ITelemetryService, + @ITelemetryService telemetryService: ITelemetryService, @IMarkersWorkbenchService private readonly markersWorkbenchService: IMarkersWorkbenchService, @IContextKeyService contextKeyService: IContextKeyService, @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, @@ -108,7 +108,7 @@ export class MarkersView extends ViewPane implements IMarkerFilterController { @IOpenerService openerService: IOpenerService, @IThemeService themeService: IThemeService, ) { - super({ ...(options as IViewPaneOptions), id: Constants.MARKERS_VIEW_ID, ariaHeaderLabel: Messages.MARKERS_PANEL_TITLE_PROBLEMS }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService); + super({ ...(options as IViewPaneOptions), id: Constants.MARKERS_VIEW_ID, ariaHeaderLabel: Messages.MARKERS_PANEL_TITLE_PROBLEMS }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); this.panelFoucusContextKey = Constants.MarkerViewFocusContextKey.bindTo(contextKeyService); this.panelState = new Memento(Constants.MARKERS_VIEW_STORAGE_ID, storageService).getMemento(StorageScope.WORKSPACE); this.markersViewModel = this._register(instantiationService.createInstance(MarkersViewModel, this.panelState['multiline'])); diff --git a/src/vs/workbench/contrib/outline/browser/outlinePane.ts b/src/vs/workbench/contrib/outline/browser/outlinePane.ts index 148a64dda65..906dc9572d8 100644 --- a/src/vs/workbench/contrib/outline/browser/outlinePane.ts +++ b/src/vs/workbench/contrib/outline/browser/outlinePane.ts @@ -50,6 +50,7 @@ import { MarkerSeverity } from 'vs/platform/markers/common/markers'; import { IViewDescriptorService } from 'vs/workbench/common/views'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; class RequestState { @@ -270,8 +271,9 @@ export class OutlinePane extends ViewPane { @IContextMenuService contextMenuService: IContextMenuService, @IOpenerService openerService: IOpenerService, @IThemeService themeService: IThemeService, + @ITelemetryService telemetryService: ITelemetryService, ) { - super(options, keybindingService, contextMenuService, _configurationService, contextKeyService, viewDescriptorService, _instantiationService, openerService, themeService); + super(options, keybindingService, contextMenuService, _configurationService, contextKeyService, viewDescriptorService, _instantiationService, openerService, themeService, telemetryService); this._outlineViewState.restore(this._storageService); this._contextKeyFocused = OutlineViewFocused.bindTo(contextKeyService); this._contextKeyFiltered = OutlineViewFiltered.bindTo(contextKeyService); diff --git a/src/vs/workbench/contrib/output/browser/outputView.ts b/src/vs/workbench/contrib/output/browser/outputView.ts index f5cc76f8b4d..c6b09aee6a1 100644 --- a/src/vs/workbench/contrib/output/browser/outputView.ts +++ b/src/vs/workbench/contrib/output/browser/outputView.ts @@ -49,8 +49,9 @@ export class OutputViewPane extends ViewPane { @IOutputService private readonly outputService: IOutputService, @IOpenerService openerService: IOpenerService, @IThemeService themeService: IThemeService, + @ITelemetryService telemetryService: ITelemetryService, ) { - super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService); + super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); this.editor = instantiationService.createInstance(OutputEditor); this._register(this.editor.onTitleAreaUpdate(() => { this.updateTitle(this.editor.getTitle()); diff --git a/src/vs/workbench/contrib/remote/browser/remote.ts b/src/vs/workbench/contrib/remote/browser/remote.ts index 76b780adca8..47dd085867b 100644 --- a/src/vs/workbench/contrib/remote/browser/remote.ts +++ b/src/vs/workbench/contrib/remote/browser/remote.ts @@ -374,8 +374,9 @@ class HelpPanel extends ViewPane { @IRemoteExplorerService protected readonly remoteExplorerService: IRemoteExplorerService, @IWorkbenchEnvironmentService protected readonly workbenchEnvironmentService: IWorkbenchEnvironmentService, @IThemeService themeService: IThemeService, + @ITelemetryService telemetryService: ITelemetryService, ) { - super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService); + super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); } protected renderBody(container: HTMLElement): void { diff --git a/src/vs/workbench/contrib/remote/browser/tunnelView.ts b/src/vs/workbench/contrib/remote/browser/tunnelView.ts index 664565a92cf..f4768fd7a15 100644 --- a/src/vs/workbench/contrib/remote/browser/tunnelView.ts +++ b/src/vs/workbench/contrib/remote/browser/tunnelView.ts @@ -40,6 +40,7 @@ import { URI } from 'vs/base/common/uri'; import { RemoteTunnel } from 'vs/platform/remote/common/tunnel'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; export const forwardedPortsViewEnabled = new RawContextKey('forwardedPortsViewEnabled', false); @@ -457,9 +458,10 @@ export class TunnelPanel extends ViewPane { @INotificationService private readonly notificationService: INotificationService, @IContextViewService private readonly contextViewService: IContextViewService, @IThemeService themeService: IThemeService, - @IRemoteExplorerService private readonly remoteExplorerService: IRemoteExplorerService + @IRemoteExplorerService private readonly remoteExplorerService: IRemoteExplorerService, + @ITelemetryService telemetryService: ITelemetryService, ) { - super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService); + super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); this.tunnelTypeContext = TunnelTypeContextKey.bindTo(contextKeyService); this.tunnelCloseableContext = TunnelCloseableContextKey.bindTo(contextKeyService); this.tunnelViewFocusContext = TunnelViewFocusContextKey.bindTo(contextKeyService); diff --git a/src/vs/workbench/contrib/scm/browser/mainPane.ts b/src/vs/workbench/contrib/scm/browser/mainPane.ts index a1219d0d8c2..e642e3f9f41 100644 --- a/src/vs/workbench/contrib/scm/browser/mainPane.ts +++ b/src/vs/workbench/contrib/scm/browser/mainPane.ts @@ -33,6 +33,7 @@ import { IViewDescriptor, IViewDescriptorService } from 'vs/workbench/common/vie import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; export interface ISpliceEvent { index: number; @@ -190,8 +191,9 @@ export class MainPane extends ViewPane { @IConfigurationService configurationService: IConfigurationService, @IOpenerService openerService: IOpenerService, @IThemeService themeService: IThemeService, + @ITelemetryService telemetryService: ITelemetryService, ) { - super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService); + super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); } protected renderBody(container: HTMLElement): void { diff --git a/src/vs/workbench/contrib/scm/browser/repositoryPane.ts b/src/vs/workbench/contrib/scm/browser/repositoryPane.ts index c5b5c59c295..69b9619c90a 100644 --- a/src/vs/workbench/contrib/scm/browser/repositoryPane.ts +++ b/src/vs/workbench/contrib/scm/browser/repositoryPane.ts @@ -68,6 +68,7 @@ import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2 import { Schemas } from 'vs/base/common/network'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; type TreeElement = ISCMResourceGroup | IResourceNode | ISCMResource; @@ -640,8 +641,9 @@ export class RepositoryPane extends ViewPane { @IStorageService private storageService: IStorageService, @IModelService private modelService: IModelService, @IOpenerService openerService: IOpenerService, + @ITelemetryService telemetryService: ITelemetryService, ) { - super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService); + super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); this.menus = instantiationService.createInstance(SCMMenus, this.repository.provider); this._register(this.menus); diff --git a/src/vs/workbench/contrib/scm/browser/scmViewlet.ts b/src/vs/workbench/contrib/scm/browser/scmViewlet.ts index 035f4617e54..b78b35610e7 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewlet.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewlet.ts @@ -56,8 +56,9 @@ export class EmptyPane extends ViewPane { @IInstantiationService instantiationService: IInstantiationService, @IOpenerService openerService: IOpenerService, @IThemeService themeService: IThemeService, + @ITelemetryService telemetryService: ITelemetryService, ) { - super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService); + super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); } shouldShowWelcome(): boolean { diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 94530fb3543..3b8ebb0eaa2 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -67,6 +67,7 @@ import { Color, RGBA } from 'vs/base/common/color'; import { IViewDescriptorService } from 'vs/workbench/common/views'; import { OpenSearchEditorAction, createEditorFromSearchResult } from 'vs/workbench/contrib/searchEditor/browser/searchEditorActions'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; const $ = dom.$; @@ -173,9 +174,10 @@ export class SearchView extends ViewPane { @IKeybindingService keybindingService: IKeybindingService, @IStorageService storageService: IStorageService, @IOpenerService openerService: IOpenerService, + @ITelemetryService telemetryService: ITelemetryService, ) { - super({ ...options, id: VIEW_ID, ariaHeaderLabel: nls.localize('searchView', "Search") }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService); + super({ ...options, id: VIEW_ID, ariaHeaderLabel: nls.localize('searchView', "Search") }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); this.container = dom.$('.search-view'); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalView.ts b/src/vs/workbench/contrib/terminal/browser/terminalView.ts index a45eaa00771..c0d8a8bc8f9 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalView.ts @@ -57,7 +57,7 @@ export class TerminalViewPane extends ViewPane { @IStorageService storageService: IStorageService, @IOpenerService openerService: IOpenerService, ) { - super(options, keybindingService, _contextMenuService, configurationService, contextKeyService, viewDescriptorService, _instantiationService, openerService, themeService); + super(options, keybindingService, _contextMenuService, configurationService, contextKeyService, viewDescriptorService, _instantiationService, openerService, themeService, telemetryService); } protected renderBody(container: HTMLElement): void { diff --git a/src/vs/workbench/contrib/timeline/browser/timelinePane.ts b/src/vs/workbench/contrib/timeline/browser/timelinePane.ts index 1afbedc6563..d15f9ab2a42 100644 --- a/src/vs/workbench/contrib/timeline/browser/timelinePane.ts +++ b/src/vs/workbench/contrib/timeline/browser/timelinePane.ts @@ -36,6 +36,7 @@ import { IAction, ActionRunner } from 'vs/base/common/actions'; import { ContextAwareMenuEntryActionViewItem, createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { MenuItemAction, IMenuService, MenuId } from 'vs/platform/actions/common/actions'; import { fromNow } from 'vs/base/common/date'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; // TODO[ECA]: Localize all the strings @@ -78,8 +79,9 @@ export class TimelinePane extends ViewPane { @ITimelineService protected timelineService: ITimelineService, @IOpenerService openerService: IOpenerService, @IThemeService themeService: IThemeService, + @ITelemetryService telemetryService: ITelemetryService, ) { - super({ ...options, titleMenuId: MenuId.TimelineTitle }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService); + super({ ...options, titleMenuId: MenuId.TimelineTitle }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); this._menus = this._register(this.instantiationService.createInstance(TimelineMenus, this.id)); From 56cd2882a1ce084fd30fb07bd5a1d375ebec76db Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Fri, 21 Feb 2020 16:39:59 +0100 Subject: [PATCH 029/200] fix env in postinstall --- build/npm/postinstall.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/build/npm/postinstall.js b/build/npm/postinstall.js index 5a937a80bd8..7a2320d8289 100644 --- a/build/npm/postinstall.js +++ b/build/npm/postinstall.js @@ -13,7 +13,7 @@ const yarn = process.platform === 'win32' ? 'yarn.cmd' : 'yarn'; * @param {*} [opts] */ function yarnInstall(location, opts) { - opts = opts || {}; + opts = opts || { env: process.env }; opts.cwd = location; opts.stdio = 'inherit'; @@ -52,8 +52,6 @@ extensions.forEach(extension => yarnInstall(`extensions/${extension}`)); function yarnInstallBuildDependencies() { // make sure we install the deps of build/lib/watch for the system installed // node, since that is the driver of gulp - //@ts-ignore - const env = Object.assign({}, process.env); const watchPath = path.join(path.dirname(__dirname), 'lib', 'watch'); const yarnrcPath = path.join(watchPath, '.yarnrc'); @@ -66,7 +64,7 @@ target "${target}" runtime "${runtime}"`; fs.writeFileSync(yarnrcPath, yarnrc, 'utf8'); - yarnInstall(watchPath, { env }); + yarnInstall(watchPath); } yarnInstall(`build`); // node modules required for build From 86abd4e95dde7122a1984fe19dd3b6df5501a7b2 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 21 Feb 2020 16:38:57 +0100 Subject: [PATCH 030/200] Show account identity instead of activity --- .../userDataSync/browser/userDataSync.ts | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index 27f8e31504a..ba05a309407 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -86,9 +86,12 @@ const getActivityTitle = (label: string, userDataSyncService: IUserDataSyncServi } return label; }; +const getIdentityTitle = (label: string, account?: AuthenticationSession): string => { + return account ? `${label} (${account.accountName})` : label; +}; const turnOnSyncCommand = { id: 'workbench.userData.actions.syncStart', title: localize('turn on sync with category', "Sync: Turn on Sync") }; const signInCommand = { id: 'workbench.userData.actions.signin', title: localize('sign in', "Sync: Sign in to sync") }; -const stopSyncCommand = { id: 'workbench.userData.actions.stopSync', title: localize('stop sync', "Sync: Turn off Sync") }; +const stopSyncCommand = { id: 'workbench.userData.actions.stopSync', title(account?: AuthenticationSession) { return getIdentityTitle(localize('stop sync', "Sync: Turn off Sync"), account); } }; const resolveSettingsConflictsCommand = { id: 'workbench.userData.actions.resolveSettingsConflicts', title: localize('showConflicts', "Sync: Show Settings Conflicts") }; const resolveKeybindingsConflictsCommand = { id: 'workbench.userData.actions.resolveKeybindingsConflicts', title: localize('showKeybindingsConflicts', "Sync: Show Keybindings Conflicts") }; const configureSyncCommand = { id: 'workbench.userData.actions.configureSync', title: localize('configure sync', "Sync: Configure") }; @@ -111,8 +114,6 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo private readonly signInNotificationDisposable = this._register(new MutableDisposable()); private _activeAccount: AuthenticationSession | undefined; - private readonly syncStatusAction = this._register(new MutableDisposable()); - constructor( @IUserDataSyncEnablementService private readonly userDataSyncEnablementService: IUserDataSyncEnablementService, @IUserDataSyncService private readonly userDataSyncService: IUserDataSyncService, @@ -233,6 +234,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo } this.updateBadge(); + this.registerSyncStatusAction(); } private async onDidChangeSessions(providerId: string): Promise { @@ -778,17 +780,16 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo } + private readonly _syncStatusActionDisposable = this._register(new MutableDisposable()); private registerSyncStatusAction(): void { const that = this; - const id = 'workbench.userData.actions.syncStatus'; - const title = localize('sync is on', "Sync is on"); const when = ContextKeyExpr.and(CONTEXT_SYNC_ENABLEMENT, CONTEXT_AUTH_TOKEN_STATE.isEqualTo(AuthStatus.SignedIn), CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized)); - this.syncStatusAction.value = registerAction2(class SyncStatusAction extends Action2 { + this._syncStatusActionDisposable.value = registerAction2(class SyncStatusAction extends Action2 { constructor() { super({ - id, + id: 'workbench.userData.actions.syncStatus', get title() { - return getActivityTitle(localize('sync is on', "Sync is on"), that.userDataSyncService); + return getIdentityTitle(localize('sync is on', "Sync is on"), that.activeAccount); }, menu: [ { @@ -796,6 +797,12 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo group: '5_sync', when, order: 3 + }, + { + id: MenuId.MenubarPreferencesMenu, + group: '5_sync', + when, + order: 3, } ], }); @@ -823,7 +830,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo items.push({ id: showSyncSettingsCommand.id, label: showSyncSettingsCommand.title }); items.push({ id: showSyncActivityCommand.id, label: showSyncActivityCommand.title(that.userDataSyncService) }); items.push({ type: 'separator' }); - items.push({ id: stopSyncCommand.id, label: stopSyncCommand.title }); + items.push({ id: stopSyncCommand.id, label: stopSyncCommand.title(that.activeAccount), }); quickPick.items = items; const disposables = new DisposableStore(); disposables.add(quickPick.onDidAccept(() => { @@ -840,12 +847,6 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo }); } }); - MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { - group: '5_sync', - command: { id, title }, - when, - order: 3 - }); } private registerTurnOffSyncAction(): void { @@ -854,7 +855,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo constructor() { super({ id: stopSyncCommand.id, - title: stopSyncCommand.title, + title: stopSyncCommand.title(that.activeAccount), menu: { id: MenuId.CommandPalette, when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), CONTEXT_SYNC_ENABLEMENT), @@ -896,7 +897,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo constructor() { super({ id: showSyncActivityCommand.id, - title: showSyncActivityCommand.title(that.userDataSyncService), + get title() { return showSyncActivityCommand.title(that.userDataSyncService); }, menu: { id: MenuId.CommandPalette, when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized)), From 66cd1d8e5b20b1760c656c47835ad39628e73110 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 21 Feb 2020 16:54:09 +0100 Subject: [PATCH 031/200] Fix #90624 --- .../common/abstractSynchronizer.ts | 18 +++++++++++++++--- .../userDataSync/common/extensionsSync.ts | 7 +------ .../userDataSync/common/globalStateSync.ts | 7 +------ .../userDataSync/common/keybindingsSync.ts | 4 ---- .../userDataSync/common/settingsSync.ts | 4 ---- 5 files changed, 17 insertions(+), 23 deletions(-) diff --git a/src/vs/platform/userDataSync/common/abstractSynchronizer.ts b/src/vs/platform/userDataSync/common/abstractSynchronizer.ts index 45e1e01f6f7..20b9eabe1c1 100644 --- a/src/vs/platform/userDataSync/common/abstractSynchronizer.ts +++ b/src/vs/platform/userDataSync/common/abstractSynchronizer.ts @@ -91,7 +91,7 @@ export abstract class AbstractSynchroniser extends Disposable { protected get enabled(): boolean { return this.userDataSyncEnablementService.isResourceEnabled(this.resourceKey); } - async sync(ref?: string): Promise { + async sync(ref?: string, donotUseLastSyncUserData?: boolean): Promise { if (!this.enabled) { this.logService.info(`${this.source}: Skipped synchronizing ${this.source.toLowerCase()} as it is disabled.`); return; @@ -108,7 +108,7 @@ export abstract class AbstractSynchroniser extends Disposable { this.logService.trace(`${this.source}: Started synchronizing ${this.source.toLowerCase()}...`); this.setStatus(SyncStatus.Syncing); - const lastSyncUserData = await this.getLastSyncUserData(); + const lastSyncUserData = donotUseLastSyncUserData ? null : await this.getLastSyncUserData(); const remoteUserData = ref && lastSyncUserData && lastSyncUserData.ref === ref ? lastSyncUserData : await this.getRemoteUserData(lastSyncUserData); if (remoteUserData.syncData && remoteUserData.syncData.version > this.version) { @@ -117,7 +117,19 @@ export abstract class AbstractSynchroniser extends Disposable { throw new UserDataSyncError(localize('incompatible', "Cannot sync {0} as its version {1} is not compatible with cloud {2}", this.source, this.version, remoteUserData.syncData.version), UserDataSyncErrorCode.Incompatible, this.source); } - return this.doSync(remoteUserData, lastSyncUserData); + try { + await this.doSync(remoteUserData, lastSyncUserData); + } catch (e) { + if (e instanceof UserDataSyncError) { + switch (e.code) { + case UserDataSyncErrorCode.RemotePreconditionFailed: + // Rejected as there is a new remote version. Syncing again, + this.logService.info(`${this.source}: Failed to synchronize as there is a new remote version available. Synchronizing again...`); + return this.sync(undefined, true); + } + } + throw e; + } } async hasPreviouslySynced(): Promise { diff --git a/src/vs/platform/userDataSync/common/extensionsSync.ts b/src/vs/platform/userDataSync/common/extensionsSync.ts index d9a082cfb6f..b932d1ecd10 100644 --- a/src/vs/platform/userDataSync/common/extensionsSync.ts +++ b/src/vs/platform/userDataSync/common/extensionsSync.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { UserDataSyncError, UserDataSyncErrorCode, SyncStatus, IUserDataSyncStoreService, ISyncExtension, IUserDataSyncLogService, IUserDataSynchroniser, SyncSource, ResourceKey, IUserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync'; +import { SyncStatus, IUserDataSyncStoreService, ISyncExtension, IUserDataSyncLogService, IUserDataSynchroniser, SyncSource, ResourceKey, IUserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync'; import { Event } from 'vs/base/common/event'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IExtensionManagementService, IExtensionGalleryService, IGlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement'; @@ -154,11 +154,6 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse await this.apply(previewResult); } catch (e) { this.setStatus(SyncStatus.Idle); - if (e instanceof UserDataSyncError && e.code === UserDataSyncErrorCode.RemotePreconditionFailed) { - // Rejected as there is a new remote version. Syncing again, - this.logService.info('Extensions: Failed to synchronize extensions as there is a new remote version available. Synchronizing again...'); - return this.sync(); - } throw e; } diff --git a/src/vs/platform/userDataSync/common/globalStateSync.ts b/src/vs/platform/userDataSync/common/globalStateSync.ts index 4cae464d938..9e6b1d16a43 100644 --- a/src/vs/platform/userDataSync/common/globalStateSync.ts +++ b/src/vs/platform/userDataSync/common/globalStateSync.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { UserDataSyncError, UserDataSyncErrorCode, SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IGlobalState, SyncSource, IUserDataSynchroniser, ResourceKey, IUserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync'; +import { SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IGlobalState, SyncSource, IUserDataSynchroniser, ResourceKey, IUserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync'; import { VSBuffer } from 'vs/base/common/buffer'; import { Event } from 'vs/base/common/event'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; @@ -131,11 +131,6 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs this.logService.trace('UI State: Finished synchronizing ui state.'); } catch (e) { this.setStatus(SyncStatus.Idle); - if (e instanceof UserDataSyncError && e.code === UserDataSyncErrorCode.RemotePreconditionFailed) { - // Rejected as there is a new remote version. Syncing again, - this.logService.info('UI State: Failed to synchronize ui state as there is a new remote version available. Synchronizing again...'); - return this.sync(); - } throw e; } finally { this.setStatus(SyncStatus.Idle); diff --git a/src/vs/platform/userDataSync/common/keybindingsSync.ts b/src/vs/platform/userDataSync/common/keybindingsSync.ts index 4d1134dfbf4..8d1d78409fd 100644 --- a/src/vs/platform/userDataSync/common/keybindingsSync.ts +++ b/src/vs/platform/userDataSync/common/keybindingsSync.ts @@ -180,10 +180,6 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem this.setStatus(SyncStatus.Idle); if (e instanceof UserDataSyncError) { switch (e.code) { - case UserDataSyncErrorCode.RemotePreconditionFailed: - // Rejected as there is a new remote version. Syncing again, - this.logService.info('Keybindings: Failed to synchronize keybindings as there is a new remote version available. Synchronizing again...'); - return this.sync(); case UserDataSyncErrorCode.LocalPreconditionFailed: // Rejected as there is a new local version. Syncing again. this.logService.info('Keybindings: Failed to synchronize keybindings as there is a new local version available. Synchronizing again...'); diff --git a/src/vs/platform/userDataSync/common/settingsSync.ts b/src/vs/platform/userDataSync/common/settingsSync.ts index 552e646cca6..4dcce8bb0ee 100644 --- a/src/vs/platform/userDataSync/common/settingsSync.ts +++ b/src/vs/platform/userDataSync/common/settingsSync.ts @@ -243,10 +243,6 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement this.setStatus(SyncStatus.Idle); if (e instanceof UserDataSyncError) { switch (e.code) { - case UserDataSyncErrorCode.RemotePreconditionFailed: - // Rejected as there is a new remote version. Syncing again, - this.logService.info('Settings: Failed to synchronize settings as there is a new remote version available. Synchronizing again...'); - return this.sync(); case UserDataSyncErrorCode.LocalPreconditionFailed: // Rejected as there is a new local version. Syncing again. this.logService.info('Settings: Failed to synchronize settings as there is a new local version available. Synchronizing again...'); From f9a90e4ec8269dadee644cb6733a0c079fd2028f Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 21 Feb 2020 16:57:55 +0100 Subject: [PATCH 032/200] fix error handling in tests --- .../common/abstractSynchronizer.ts | 48 ++++++++++--------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/src/vs/platform/userDataSync/common/abstractSynchronizer.ts b/src/vs/platform/userDataSync/common/abstractSynchronizer.ts index 20b9eabe1c1..a82acd2b357 100644 --- a/src/vs/platform/userDataSync/common/abstractSynchronizer.ts +++ b/src/vs/platform/userDataSync/common/abstractSynchronizer.ts @@ -212,29 +212,33 @@ export abstract class AbstractSynchroniser extends Disposable { } private async cleanUpBackup(): Promise { - const stat = await this.fileService.resolve(this.syncFolder); - if (stat.children) { - const all = stat.children.filter(stat => stat.isFile && /^\d{8}T\d{6}$/.test(stat.name)).sort(); - const backUpMaxAge = 1000 * 60 * 60 * 24 * (this.configurationService.getValue('sync.localBackupDuration') || 30 /* Default 30 days */); - let toDelete = all.filter(stat => { - const ctime = stat.ctime || new Date( - parseInt(stat.name.substring(0, 4)), - parseInt(stat.name.substring(4, 6)) - 1, - parseInt(stat.name.substring(6, 8)), - parseInt(stat.name.substring(9, 11)), - parseInt(stat.name.substring(11, 13)), - parseInt(stat.name.substring(13, 15)) - ).getTime(); - return Date.now() - ctime > backUpMaxAge; - }); - const remaining = all.length - toDelete.length; - if (remaining < 10) { - toDelete = toDelete.slice(10 - remaining); + try { + const stat = await this.fileService.resolve(this.syncFolder); + if (stat.children) { + const all = stat.children.filter(stat => stat.isFile && /^\d{8}T\d{6}$/.test(stat.name)).sort(); + const backUpMaxAge = 1000 * 60 * 60 * 24 * (this.configurationService.getValue('sync.localBackupDuration') || 30 /* Default 30 days */); + let toDelete = all.filter(stat => { + const ctime = stat.ctime || new Date( + parseInt(stat.name.substring(0, 4)), + parseInt(stat.name.substring(4, 6)) - 1, + parseInt(stat.name.substring(6, 8)), + parseInt(stat.name.substring(9, 11)), + parseInt(stat.name.substring(11, 13)), + parseInt(stat.name.substring(13, 15)) + ).getTime(); + return Date.now() - ctime > backUpMaxAge; + }); + const remaining = all.length - toDelete.length; + if (remaining < 10) { + toDelete = toDelete.slice(10 - remaining); + } + await Promise.all(toDelete.map(stat => { + this.logService.info('Deleting from backup', stat.resource.path); + this.fileService.del(stat.resource); + })); } - await Promise.all(toDelete.map(stat => { - this.logService.info('Deleting from backup', stat.resource.path); - this.fileService.del(stat.resource); - })); + } catch (e) { + this.logService.error(e); } } From 0c1c8f2760967f4a950af36e0482b78ea2916a34 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Fri, 21 Feb 2020 17:03:57 +0100 Subject: [PATCH 033/200] welcome view: add internal precondition --- .../browser/parts/views/viewPaneContainer.ts | 22 +++++++++++++++++-- src/vs/workbench/common/views.ts | 5 +++++ .../workbench/contrib/debug/browser/repl.ts | 2 +- .../extensions/browser/extensionsViews.ts | 2 +- .../files/browser/views/explorerView.ts | 2 +- .../files/browser/views/openEditorsView.ts | 2 +- .../contrib/output/browser/outputView.ts | 2 +- .../workbench/contrib/scm/browser/mainPane.ts | 2 +- 8 files changed, 31 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts index 595cc112e62..90554ca9351 100644 --- a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts +++ b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts @@ -198,7 +198,7 @@ export abstract class ViewPane extends Pane implements IView { @IKeybindingService protected keybindingService: IKeybindingService, @IContextMenuService protected contextMenuService: IContextMenuService, @IConfigurationService protected readonly configurationService: IConfigurationService, - @IContextKeyService contextKeyService: IContextKeyService, + @IContextKeyService protected contextKeyService: IContextKeyService, @IViewDescriptorService protected viewDescriptorService: IViewDescriptorService, @IInstantiationService protected instantiationService: IInstantiationService, @IOpenerService protected openerService: IOpenerService, @@ -402,7 +402,9 @@ export abstract class ViewPane extends Pane implements IView { addClass(this.bodyContainer, 'welcome'); this.viewWelcomeContainer.innerHTML = ''; - for (const { content } of contents) { + let buttonIndex = 0; + + for (const { content, preconditions } of contents) { const lines = content.split('\n'); for (let line of lines) { @@ -427,6 +429,22 @@ export abstract class ViewPane extends Pane implements IView { }, null, disposables); disposables.add(button); disposables.add(attachButtonStyler(button, this.themeService)); + + if (preconditions) { + const precondition = preconditions[buttonIndex]; + + if (precondition) { + const updateEnablement = () => button.enabled = this.contextKeyService.contextMatchesRules(precondition); + updateEnablement(); + + const keys = new Set(); + precondition.keys().forEach(key => keys.add(key)); + const onDidChangeContext = Event.filter(this.contextKeyService.onDidChangeContext, e => e.affectsSome(keys)); + onDidChangeContext(updateEnablement, null, disposables); + } + } + + buttonIndex++; } else { const link = this.instantiationService.createInstance(Link, node); append(p, link.el); diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index 0cae0da27f6..1e141807590 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -214,6 +214,11 @@ export interface IViewDescriptorCollection extends IDisposable { export interface IViewContentDescriptor { readonly content: string; readonly when?: ContextKeyExpr | 'default'; + + /** + * ordered preconditions for each button in the content + */ + readonly preconditions?: (ContextKeyExpr | undefined)[]; } export interface IViewsRegistry { diff --git a/src/vs/workbench/contrib/debug/browser/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts index 6ad3398983d..b803f4ce468 100644 --- a/src/vs/workbench/contrib/debug/browser/repl.ts +++ b/src/vs/workbench/contrib/debug/browser/repl.ts @@ -101,7 +101,7 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { @IStorageService private readonly storageService: IStorageService, @IThemeService themeService: IThemeService, @IModelService private readonly modelService: IModelService, - @IContextKeyService private readonly contextKeyService: IContextKeyService, + @IContextKeyService contextKeyService: IContextKeyService, @ICodeEditorService codeEditorService: ICodeEditorService, @IViewDescriptorService viewDescriptorService: IViewDescriptorService, @IContextMenuService contextMenuService: IContextMenuService, diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts index 9055d5f8b6e..52378063e9c 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts @@ -109,7 +109,7 @@ export class ExtensionsListView extends ViewPane { @IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService, @IExtensionManagementServerService protected readonly extensionManagementServerService: IExtensionManagementServerService, @IProductService protected readonly productService: IProductService, - @IContextKeyService private readonly contextKeyService: IContextKeyService, + @IContextKeyService contextKeyService: IContextKeyService, @IViewDescriptorService viewDescriptorService: IViewDescriptorService, @IMenuService private readonly menuService: IMenuService, @IOpenerService openerService: IOpenerService, diff --git a/src/vs/workbench/contrib/files/browser/views/explorerView.ts b/src/vs/workbench/contrib/files/browser/views/explorerView.ts index d6e128f90f0..3e5fc89ec88 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerView.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerView.ts @@ -159,7 +159,7 @@ export class ExplorerView extends ViewPane { @IEditorService private readonly editorService: IEditorService, @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService, @IKeybindingService keybindingService: IKeybindingService, - @IContextKeyService private readonly contextKeyService: IContextKeyService, + @IContextKeyService contextKeyService: IContextKeyService, @IConfigurationService configurationService: IConfigurationService, @IDecorationsService private readonly decorationService: IDecorationsService, @ILabelService private readonly labelService: ILabelService, diff --git a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts index 35cd7738074..c388727aba4 100644 --- a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts +++ b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts @@ -76,7 +76,7 @@ export class OpenEditorsView extends ViewPane { @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService, @IConfigurationService configurationService: IConfigurationService, @IKeybindingService keybindingService: IKeybindingService, - @IContextKeyService private readonly contextKeyService: IContextKeyService, + @IContextKeyService contextKeyService: IContextKeyService, @IThemeService themeService: IThemeService, @ITelemetryService telemetryService: ITelemetryService, @IMenuService private readonly menuService: IMenuService, diff --git a/src/vs/workbench/contrib/output/browser/outputView.ts b/src/vs/workbench/contrib/output/browser/outputView.ts index c6b09aee6a1..aebdb85e8b8 100644 --- a/src/vs/workbench/contrib/output/browser/outputView.ts +++ b/src/vs/workbench/contrib/output/browser/outputView.ts @@ -43,7 +43,7 @@ export class OutputViewPane extends ViewPane { @IKeybindingService keybindingService: IKeybindingService, @IContextMenuService contextMenuService: IContextMenuService, @IConfigurationService configurationService: IConfigurationService, - @IContextKeyService private readonly contextKeyService: IContextKeyService, + @IContextKeyService contextKeyService: IContextKeyService, @IViewDescriptorService viewDescriptorService: IViewDescriptorService, @IInstantiationService instantiationService: IInstantiationService, @IOutputService private readonly outputService: IOutputService, diff --git a/src/vs/workbench/contrib/scm/browser/mainPane.ts b/src/vs/workbench/contrib/scm/browser/mainPane.ts index e642e3f9f41..60d2d14c614 100644 --- a/src/vs/workbench/contrib/scm/browser/mainPane.ts +++ b/src/vs/workbench/contrib/scm/browser/mainPane.ts @@ -186,7 +186,7 @@ export class MainPane extends ViewPane { @IContextMenuService protected contextMenuService: IContextMenuService, @IInstantiationService instantiationService: IInstantiationService, @IViewDescriptorService viewDescriptorService: IViewDescriptorService, - @IContextKeyService private readonly contextKeyService: IContextKeyService, + @IContextKeyService contextKeyService: IContextKeyService, @IMenuService private readonly menuService: IMenuService, @IConfigurationService configurationService: IConfigurationService, @IOpenerService openerService: IOpenerService, From 194dae557cf71ceb01fea39c0d56bd1b440e673c Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 21 Feb 2020 17:10:25 +0100 Subject: [PATCH 034/200] start view: use preconditions for button enablement fixes #91141 --- src/vs/workbench/contrib/debug/browser/startView.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/startView.ts b/src/vs/workbench/contrib/debug/browser/startView.ts index 0a501ebf5bf..f6b3133dc28 100644 --- a/src/vs/workbench/contrib/debug/browser/startView.ts +++ b/src/vs/workbench/contrib/debug/browser/startView.ts @@ -86,13 +86,14 @@ export class StartView extends ViewPane { const viewsRegistry = Registry.as(Extensions.ViewsRegistry); viewsRegistry.registerViewWelcomeContent(StartView.ID, { content: localize('openAFileWhichCanBeDebugged', "[Open a file](command:{0}) which can be debugged or run.", isMacintosh ? OpenFileFolderAction.ID : OpenFileAction.ID), - when: CONTEXT_DEBUGGER_INTERESTED_IN_ACTIVE_EDITOR + when: CONTEXT_DEBUGGER_INTERESTED_IN_ACTIVE_EDITOR.toNegated() }); const debugKeybinding = this.keybindingService.lookupKeybinding(StartAction.ID); const debugKeybindingLabel = debugKeybinding ? ` (${debugKeybinding.getLabel()})` : ''; viewsRegistry.registerViewWelcomeContent(StartView.ID, { - content: localize('runAndDebugAction', "[Run and Debug{0}](command:{1})", debugKeybindingLabel, StartAction.ID) + content: localize('runAndDebugAction', "[Run and Debug{0}](command:{1})", debugKeybindingLabel, StartAction.ID), + preconditions: [CONTEXT_DEBUGGER_INTERESTED_IN_ACTIVE_EDITOR] }); viewsRegistry.registerViewWelcomeContent(StartView.ID, { From 52259152785ba4ef4efef2f806830452e8e0a22e Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 21 Feb 2020 18:10:49 +0100 Subject: [PATCH 035/200] #90218 Handle local errors --- .../userDataSync/common/userDataSync.ts | 2 + .../userDataSync/common/userDataSyncIpc.ts | 1 + .../common/userDataSyncService.ts | 7 +++ .../browser/userDataSync.contribution.ts | 10 ++-- .../userDataSync/browser/userDataSync.ts | 47 ++++++++++++++++++- .../electron-browser/userDataSyncService.ts | 4 ++ 6 files changed, 64 insertions(+), 7 deletions(-) diff --git a/src/vs/platform/userDataSync/common/userDataSync.ts b/src/vs/platform/userDataSync/common/userDataSync.ts index 0d5dbdd3ef5..69585e9d79f 100644 --- a/src/vs/platform/userDataSync/common/userDataSync.ts +++ b/src/vs/platform/userDataSync/common/userDataSync.ts @@ -187,6 +187,7 @@ export enum UserDataSyncErrorCode { // Local Errors LocalPreconditionFailed = 'LocalPreconditionFailed', LocalInvalidContent = 'LocalInvalidContent', + LocalError = 'LocalError', Incompatible = 'Incompatible', Unknown = 'Unknown', @@ -293,6 +294,7 @@ export interface IUserDataSyncService { readonly onDidChangeConflicts: Event; readonly onDidChangeLocal: Event; + readonly onSyncErrors: Event<[SyncSource, UserDataSyncError][]>; readonly lastSyncTime: number | undefined; readonly onDidChangeLastSyncTime: Event; diff --git a/src/vs/platform/userDataSync/common/userDataSyncIpc.ts b/src/vs/platform/userDataSync/common/userDataSyncIpc.ts index 08ac1ae6acf..04c8649a703 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncIpc.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncIpc.ts @@ -20,6 +20,7 @@ export class UserDataSyncChannel implements IServerChannel { case 'onDidChangeConflicts': return this.service.onDidChangeConflicts; case 'onDidChangeLocal': return this.service.onDidChangeLocal; case 'onDidChangeLastSyncTime': return this.service.onDidChangeLastSyncTime; + case 'onSyncErrors': return this.service.onSyncErrors; } throw new Error(`Event not found: ${event}`); } diff --git a/src/vs/platform/userDataSync/common/userDataSyncService.ts b/src/vs/platform/userDataSync/common/userDataSyncService.ts index 94e476f67f2..07203267160 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncService.ts @@ -41,6 +41,10 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ private _onDidChangeConflicts: Emitter = this._register(new Emitter()); readonly onDidChangeConflicts: Event = this._onDidChangeConflicts.event; + private _syncErrors: [SyncSource, UserDataSyncError][] = []; + private _onSyncErrors: Emitter<[SyncSource, UserDataSyncError][]> = this._register(new Emitter<[SyncSource, UserDataSyncError][]>()); + readonly onSyncErrors: Event<[SyncSource, UserDataSyncError][]> = this._onSyncErrors.event; + private _lastSyncTime: number | undefined = undefined; get lastSyncTime(): number | undefined { return this._lastSyncTime; } private _onDidChangeLastSyncTime: Emitter = this._register(new Emitter()); @@ -101,6 +105,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ await this.checkEnablement(); const startTime = new Date().getTime(); + this._syncErrors = []; try { this.logService.trace('Sync started.'); if (this.status !== SyncStatus.HasConflicts) { @@ -126,6 +131,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ await synchroniser.sync(manifest && manifest.latest ? manifest.latest[synchroniser.resourceKey] : undefined); } catch (e) { this.handleSyncError(e, synchroniser.source); + this._syncErrors.push([synchroniser.source, UserDataSyncError.toUserDataSyncError(e)]); } } @@ -144,6 +150,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ } finally { this.updateStatus(); + this._onSyncErrors.fire(this._syncErrors); } } diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.contribution.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.contribution.ts index 5bd817b28e6..36c2f28c7a1 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.contribution.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.contribution.ts @@ -37,11 +37,11 @@ class UserDataSyncSettingsMigrationContribution implements IWorkbenchContributio } private async removeFromConfiguration(): Promise { - await this.configurationService.updateValue('sync.enable', undefined, ConfigurationTarget.USER); - await this.configurationService.updateValue('sync.enableSettings', undefined, ConfigurationTarget.USER); - await this.configurationService.updateValue('sync.enableKeybindings', undefined, ConfigurationTarget.USER); - await this.configurationService.updateValue('sync.enableUIState', undefined, ConfigurationTarget.USER); - await this.configurationService.updateValue('sync.enableExtensions', undefined, ConfigurationTarget.USER); + await this.configurationService.updateValue('sync.enable', undefined, {}, ConfigurationTarget.USER, true); + await this.configurationService.updateValue('sync.enableSettings', undefined, {}, ConfigurationTarget.USER, true); + await this.configurationService.updateValue('sync.enableKeybindings', undefined, {}, ConfigurationTarget.USER, true); + await this.configurationService.updateValue('sync.enableUIState', undefined, {}, ConfigurationTarget.USER, true); + await this.configurationService.updateValue('sync.enableExtensions', undefined, {}, ConfigurationTarget.USER, true); } } diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index ba05a309407..a2c70433f6c 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -109,7 +109,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo private readonly syncStatusContext: IContextKey; private readonly authenticationState: IContextKey; private readonly conflictsSources: IContextKey; - private readonly conflictsDisposables = new Map(); + private readonly badgeDisposable = this._register(new MutableDisposable()); private readonly signInNotificationDisposable = this._register(new MutableDisposable()); private _activeAccount: AuthenticationSession | undefined; @@ -149,6 +149,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo this.onDidChangeEnablement(this.userDataSyncEnablementService.isEnabled()); this._register(Event.debounce(userDataSyncService.onDidChangeStatus, () => undefined, 500)(() => this.onDidChangeSyncStatus(this.userDataSyncService.status))); this._register(userDataSyncService.onDidChangeConflicts(() => this.onDidChangeConflicts(this.userDataSyncService.conflictsSources))); + this._register(userDataSyncService.onSyncErrors(errors => this.onSyncErrors(errors))); this._register(this.authTokenService.onTokenFailed(_ => this.authenticationService.getSessions(this.userDataSyncStore!.authenticationProviderId))); this._register(this.userDataSyncEnablementService.onDidChangeEnablement(enabled => this.onDidChangeEnablement(enabled))); this._register(this.authenticationService.onDidRegisterAuthenticationProvider(e => this.onDidRegisterAuthenticationProvider(e))); @@ -268,6 +269,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo this.updateBadge(); } + private readonly conflictsDisposables = new Map(); private onDidChangeConflicts(conflicts: SyncSource[]) { this.updateBadge(); if (conflicts.length) { @@ -405,7 +407,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo severity: Severity.Error, message: localize('too large', "Disabled sync {0} because size of the {1} file to sync is larger than {2}. Please open the file and reduce the size and enable sync", sourceArea, sourceArea, '100kb'), actions: { - primary: [new Action('open sync file', localize('open file', "Show {0} file", sourceArea), undefined, true, + primary: [new Action('open sync file', localize('open file', "Open {0} file", sourceArea), undefined, true, () => error.source === SyncSource.Settings ? this.preferencesService.openGlobalSettings(true) : this.preferencesService.openGlobalKeybindingSettings(true))] } }); @@ -421,6 +423,47 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo } } + private readonly invalidContentErrorDisposables = new Map(); + private onSyncErrors(errors: [SyncSource, UserDataSyncError][]): void { + if (errors.length) { + for (const [source, error] of errors) { + switch (error.code) { + case UserDataSyncErrorCode.LocalInvalidContent: + this.handleInvalidContentError(source); + break; + default: + const disposable = this.invalidContentErrorDisposables.get(source); + if (disposable) { + disposable.dispose(); + this.invalidContentErrorDisposables.delete(source); + } + } + } + } else { + this.invalidContentErrorDisposables.forEach(disposable => disposable.dispose()); + this.invalidContentErrorDisposables.clear(); + } + } + + private handleInvalidContentError(source: SyncSource): void { + if (!this.invalidContentErrorDisposables.has(source)) { + const errorArea = getSyncAreaLabel(source); + const handle = this.notificationService.notify({ + severity: Severity.Error, + message: localize('errorInvalidConfiguration', "Unable to sync {0} because there are some errors/warnings in the file. Please open the file to correct errors/warnings in it.", errorArea), + actions: { + primary: [new Action('open sync file', localize('open file', "Open {0} file", errorArea), undefined, true, + () => source === SyncSource.Settings ? this.preferencesService.openGlobalSettings(true) : this.preferencesService.openGlobalKeybindingSettings(true))] + } + }); + this.invalidContentErrorDisposables.set(source, toDisposable(() => { + // close the error warning notification + handle.close(); + this.invalidContentErrorDisposables.delete(source); + })); + } + } + private async updateBadge(): Promise { this.badgeDisposable.clear(); diff --git a/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncService.ts b/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncService.ts index 46537301337..2b495a43dc9 100644 --- a/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncService.ts +++ b/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncService.ts @@ -34,6 +34,9 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ private _onDidChangeLastSyncTime: Emitter = this._register(new Emitter()); readonly onDidChangeLastSyncTime: Event = this._onDidChangeLastSyncTime.event; + private _onSyncErrors: Emitter<[SyncSource, UserDataSyncError][]> = this._register(new Emitter<[SyncSource, UserDataSyncError][]>()); + readonly onSyncErrors: Event<[SyncSource, UserDataSyncError][]> = this._onSyncErrors.event; + constructor( @ISharedProcessService sharedProcessService: ISharedProcessService ) { @@ -58,6 +61,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ this._register(this.channel.listen('onDidChangeLastSyncTime')(lastSyncTime => this.updateLastSyncTime(lastSyncTime))); }); this._register(this.channel.listen('onDidChangeConflicts')(conflicts => this.updateConflicts(conflicts))); + this._register(this.channel.listen<[SyncSource, Error][]>('onSyncErrors')(errors => this._onSyncErrors.fire(errors.map(([source, error]) => ([source, UserDataSyncError.toUserDataSyncError(error)]))))); } pull(): Promise { From 6874ecc18985ee21ff2b0ba85acaa62d7af84957 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 21 Feb 2020 18:22:04 +0100 Subject: [PATCH 036/200] fix #90218 --- src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index a2c70433f6c..fc5a1fea149 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -402,10 +402,11 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo return; case UserDataSyncErrorCode.TooLarge: if (error.source === SyncSource.Keybindings || error.source === SyncSource.Settings) { + this.disableSync(error.source); const sourceArea = getSyncAreaLabel(error.source); this.notificationService.notify({ severity: Severity.Error, - message: localize('too large', "Disabled sync {0} because size of the {1} file to sync is larger than {2}. Please open the file and reduce the size and enable sync", sourceArea, sourceArea, '100kb'), + message: localize('too large', "Disabled syncing {0} because size of the {1} file to sync is larger than {2}. Please open the file and reduce the size and enable sync", sourceArea, sourceArea, '100kb'), actions: { primary: [new Action('open sync file', localize('open file', "Open {0} file", sourceArea), undefined, true, () => error.source === SyncSource.Settings ? this.preferencesService.openGlobalSettings(true) : this.preferencesService.openGlobalKeybindingSettings(true))] From 0b02e61f4296909658593658ca3e1e13c0547706 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 21 Feb 2020 18:25:27 +0100 Subject: [PATCH 037/200] :lipstick: --- src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index fc5a1fea149..1ec84ca0d66 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -394,9 +394,9 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo case UserDataSyncErrorCode.SessionExpired: this.notificationService.notify({ severity: Severity.Info, - message: localize('turned off', "Turned off sync because it was turned off from other device."), + message: localize('turned off', "Sync was turned off from another device."), actions: { - primary: [new Action('turn on sync', localize('Turn on sync', "Turn on Sync"), undefined, true, () => this.turnOn())] + primary: [new Action('turn on sync', localize('Turn sync back on', "Turn Sync Back On"), undefined, true, () => this.turnOn())] } }); return; From 7d715621403d24d8359d79469ef8da7c75352115 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 21 Feb 2020 18:52:59 +0100 Subject: [PATCH 038/200] untitled - fix title for labels (#89921) --- src/vs/workbench/browser/labels.ts | 17 +++++++++-------- .../parts/editor/editorDiffModel.test.ts | 1 + .../parts/editor/resourceEditorInput.test.ts | 1 + 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/browser/labels.ts b/src/vs/workbench/browser/labels.ts index 895d41d509e..b92199f1f86 100644 --- a/src/vs/workbench/browser/labels.ts +++ b/src/vs/workbench/browser/labels.ts @@ -353,7 +353,7 @@ class ResourceLabelWidget extends IconLabel { this.setResource({ resource, name, description }, options); } - setResource(label: IResourceLabelProps, options?: IResourceLabelOptions): void { + setResource(label: IResourceLabelProps, options: IResourceLabelOptions = Object.create(null)): void { if (label.resource?.scheme === Schemas.untitled) { // Untitled labels are very dynamic because they may change // whenever the content changes (unless a path is associated). @@ -369,19 +369,20 @@ class ResourceLabelWidget extends IconLabel { } if (typeof label.description === 'string') { - let untitledDescription: string; - if (untitledModel.hasAssociatedFilePath) { - untitledDescription = this.labelService.getUriLabel(dirname(untitledModel.resource), { relative: true }); - } else { - untitledDescription = untitledModel.resource.path; - } - + let untitledDescription = untitledModel.resource.path; if (label.name !== untitledDescription) { label.description = untitledDescription; } else if (label.description === posix.sep) { label.description = undefined; // unset showing just "/" for untitled without associated resource } } + + let untitledTitle = untitledModel.resource.path; + if (untitledModel.name !== untitledTitle) { + options.title = `${untitledModel.name} • ${untitledTitle}`; + } else { + options.title = untitledTitle; + } } } diff --git a/src/vs/workbench/test/browser/parts/editor/editorDiffModel.test.ts b/src/vs/workbench/test/browser/parts/editor/editorDiffModel.test.ts index 9de4ac90c2e..37bfc6dc478 100644 --- a/src/vs/workbench/test/browser/parts/editor/editorDiffModel.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/editorDiffModel.test.ts @@ -13,6 +13,7 @@ import { ITextModel } from 'vs/editor/common/model'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; suite('Workbench editor model', () => { + let instantiationService: IInstantiationService; let accessor: TestServiceAccessor; diff --git a/src/vs/workbench/test/browser/parts/editor/resourceEditorInput.test.ts b/src/vs/workbench/test/browser/parts/editor/resourceEditorInput.test.ts index 0593d4a5fd2..7634e777a07 100644 --- a/src/vs/workbench/test/browser/parts/editor/resourceEditorInput.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/resourceEditorInput.test.ts @@ -13,6 +13,7 @@ import { snapshotToString } from 'vs/workbench/services/textfile/common/textfile import { ModesRegistry, PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry'; suite('Resource text editors', () => { + let instantiationService: IInstantiationService; let accessor: TestServiceAccessor; From ed698aa9b1e24db25eafad5b2e9b939118cd12c7 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 21 Feb 2020 11:06:02 +0100 Subject: [PATCH 039/200] Scaffold MultiEditStackElement --- src/vs/editor/common/model/editStack.ts | 121 ++++++++++++++++++------ 1 file changed, 91 insertions(+), 30 deletions(-) diff --git a/src/vs/editor/common/model/editStack.ts b/src/vs/editor/common/model/editStack.ts index 1e6c83563d8..b1094a21ca8 100644 --- a/src/vs/editor/common/model/editStack.ts +++ b/src/vs/editor/common/model/editStack.ts @@ -10,12 +10,13 @@ import { EndOfLineSequence, ICursorStateComputer, IIdentifiedSingleEditOperation import { TextModel } from 'vs/editor/common/model/textModel'; import { IUndoRedoService, IUndoRedoElement, IUndoRedoContext } from 'vs/platform/undoRedo/common/undoRedo'; import { URI } from 'vs/base/common/uri'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; -class EditStackElement implements IUndoRedoElement { +export class EditStackElement implements IUndoRedoElement { public readonly label: string; private _isOpen: boolean; - private readonly _model: TextModel; + public readonly model: ITextModel; private readonly _beforeVersionId: number; private readonly _beforeCursorState: Selection[]; private _afterVersionId: number; @@ -23,25 +24,25 @@ class EditStackElement implements IUndoRedoElement { private _edits: IValidEditOperations[]; public get resources(): readonly URI[] { - return [this._model.uri]; + return [this.model.uri]; } - constructor(model: TextModel, beforeVersionId: number, beforeCursorState: Selection[], afterVersionId: number, afterCursorState: Selection[] | null, operations: IValidEditOperation[]) { + constructor(model: ITextModel, beforeCursorState: Selection[]) { this.label = nls.localize('edit', "Typing"); this._isOpen = true; - this._model = model; - this._beforeVersionId = beforeVersionId; + this.model = model; + this._beforeVersionId = model.getAlternativeVersionId(); this._beforeCursorState = beforeCursorState; - this._afterVersionId = afterVersionId; - this._afterCursorState = afterCursorState; - this._edits = [{ operations: operations }]; + this._afterVersionId = this._beforeVersionId; + this._afterCursorState = this._beforeCursorState; + this._edits = []; } - public isOpen(): boolean { - return this._isOpen; + public canAppend(model: ITextModel): boolean { + return (this._isOpen && this.model === model); } - public append(operations: IValidEditOperation[], afterVersionId: number, afterCursorState: Selection[] | null): void { + public append(model: ITextModel, operations: IValidEditOperation[], afterVersionId: number, afterCursorState: Selection[] | null): void { this._edits.push({ operations: operations }); this._afterVersionId = afterVersionId; this._afterCursorState = afterCursorState; @@ -54,13 +55,12 @@ class EditStackElement implements IUndoRedoElement { undo(ctx: IUndoRedoContext): void { this._isOpen = false; this._edits.reverse(); - this._edits = this._model._applyEdits(this._edits, true, false, this._beforeVersionId, this._beforeCursorState); + this._edits = this.model._applyEdits(this._edits, true, false, this._beforeVersionId, this._beforeCursorState); } redo(ctx: IUndoRedoContext): void { - this._isOpen = false; this._edits.reverse(); - this._edits = this._model._applyEdits(this._edits, false, true, this._afterVersionId, this._afterCursorState); + this._edits = this.model._applyEdits(this._edits, false, true, this._afterVersionId, this._afterCursorState); } invalidate(resource: URI): void { @@ -68,6 +68,70 @@ class EditStackElement implements IUndoRedoElement { } } +export class MultiEditStackElement implements IUndoRedoElement { + + public readonly label: string; + private _isOpen: boolean; + + private readonly _editStackElementsArr: EditStackElement[]; + private readonly _editStackElementsMap: Map; + + public get resources(): readonly URI[] { + return this._editStackElementsArr.map(editStackElement => editStackElement.model.uri); + } + + constructor( + label: string, + editStackElements: EditStackElement[], + @IDialogService dialogService: IDialogService + ) { + this.label = label; + this._isOpen = true; + this._editStackElementsArr = editStackElements.slice(0); + this._editStackElementsMap = new Map(); + for (const editStackElement of this._editStackElementsArr) { + this._editStackElementsMap.set(editStackElement.model.id, editStackElement); + } + } + + public canAppend(model: ITextModel): boolean { + if (!this._isOpen) { + return false; + } + if (this._editStackElementsMap.has(model.id)) { + const editStackElement = this._editStackElementsMap.get(model.id)!; + return editStackElement.canAppend(model); + } + return false; + } + + public append(model: ITextModel, operations: IValidEditOperation[], afterVersionId: number, afterCursorState: Selection[] | null): void { + const editStackElement = this._editStackElementsMap.get(model.id)!; + editStackElement.append(model, operations, afterVersionId, afterCursorState); + } + + public close(): void { + this._isOpen = false; + } + + undo(ctx: IUndoRedoContext): void { + this._isOpen = false; + for (const editStackElement of this._editStackElementsArr) { + editStackElement.undo(ctx); + } + } + + redo(ctx: IUndoRedoContext): void { + for (const editStackElement of this._editStackElementsArr) { + editStackElement.redo(ctx); + } + } + + invalidate(resource: URI): void { + console.log(`MULTI INVALIDATE: ${resource}`); + } +} + function getModelEOL(model: ITextModel): EndOfLineSequence { const eol = model.getEOL(); if (eol === '\n') { @@ -114,11 +178,6 @@ class EOLStackElement implements IUndoRedoElement { } } -export interface IUndoRedoResult { - selections: Selection[] | null; - recordedVersionId: number; -} - export class EditStack { private readonly _model: TextModel; @@ -153,19 +212,21 @@ export class EditStack { this._undoRedoService.pushElement(new EOLStackElement(this._model, inverseEOL, beforeVersionId, afterVersionId)); } - public pushEditOperation(beforeCursorState: Selection[], editOperations: IIdentifiedSingleEditOperation[], cursorStateComputer: ICursorStateComputer | null): Selection[] | null { - const beforeVersionId = this._model.getAlternativeVersionId(); - const inverseEditOperations = this._model.applyEdits(editOperations); - const afterVersionId = this._model.getAlternativeVersionId(); - const afterCursorState = EditStack._computeCursorState(cursorStateComputer, inverseEditOperations); - + private _getOrCreateEditStackElement(beforeCursorState: Selection[]): EditStackElement { const lastElement = this._undoRedoService.getLastElement(this._model.uri); - if (lastElement && lastElement instanceof EditStackElement && lastElement.isOpen()) { - lastElement.append(inverseEditOperations, afterVersionId, afterCursorState); - } else { - this._undoRedoService.pushElement(new EditStackElement(this._model, beforeVersionId, beforeCursorState, afterVersionId, afterCursorState, inverseEditOperations)); + if (lastElement && lastElement instanceof EditStackElement && lastElement.canAppend(this._model)) { + return lastElement; } + const newElement = new EditStackElement(this._model, beforeCursorState); + this._undoRedoService.pushElement(newElement); + return newElement; + } + public pushEditOperation(beforeCursorState: Selection[], editOperations: IIdentifiedSingleEditOperation[], cursorStateComputer: ICursorStateComputer | null): Selection[] | null { + const editStackElement = this._getOrCreateEditStackElement(beforeCursorState); + const inverseEditOperations = this._model.applyEdits(editOperations); + const afterCursorState = EditStack._computeCursorState(cursorStateComputer, inverseEditOperations); + editStackElement.append(this._model, inverseEditOperations, this._model.getAlternativeVersionId(), afterCursorState); return afterCursorState; } From 94fe174275097aa35b996b7ad1e174471efb222c Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 21 Feb 2020 11:51:24 +0100 Subject: [PATCH 040/200] Allow EOL changes to be mergeable with other edits --- src/vs/editor/common/model.ts | 7 +- src/vs/editor/common/model/editStack.ts | 85 ++++++------------- src/vs/editor/common/model/textModel.ts | 19 +---- .../test/browser/controller/cursor.test.ts | 24 ++++++ .../browser/parts/editor/editorStatus.ts | 2 + 5 files changed, 54 insertions(+), 83 deletions(-) diff --git a/src/vs/editor/common/model.ts b/src/vs/editor/common/model.ts index 644a224686f..df8ad4833db 100644 --- a/src/vs/editor/common/model.ts +++ b/src/vs/editor/common/model.ts @@ -1093,11 +1093,6 @@ export interface ITextModel { */ applyEdits(operations: IIdentifiedSingleEditOperation[]): IValidEditOperation[]; - /** - * @internal - */ - _applyEdits(edits: IValidEditOperations[], isUndoing: boolean, isRedoing: boolean, resultingAlternativeVersionId: number, resultingSelection: Selection[] | null): IValidEditOperations[]; - /** * Change the end of line sequence without recording in the undo stack. * This can have dire consequences on the undo stack! See @pushEOL for the preferred way. @@ -1107,7 +1102,7 @@ export interface ITextModel { /** * @internal */ - _setEOL(eol: EndOfLineSequence, isUndoing: boolean, isRedoing: boolean, resultingAlternativeVersionId: number, resultingSelection: Selection[] | null): void; + _applyUndoRedoEdits(edits: IValidEditOperations[], eol: EndOfLineSequence, isUndoing: boolean, isRedoing: boolean, resultingAlternativeVersionId: number, resultingSelection: Selection[] | null): IValidEditOperations[]; /** * Undo edit operations until the first previous stop point created by `pushStackElement`. diff --git a/src/vs/editor/common/model/editStack.ts b/src/vs/editor/common/model/editStack.ts index b1094a21ca8..92bab6cd80e 100644 --- a/src/vs/editor/common/model/editStack.ts +++ b/src/vs/editor/common/model/editStack.ts @@ -18,8 +18,10 @@ export class EditStackElement implements IUndoRedoElement { private _isOpen: boolean; public readonly model: ITextModel; private readonly _beforeVersionId: number; - private readonly _beforeCursorState: Selection[]; + private readonly _beforeEOL: EndOfLineSequence; + private readonly _beforeCursorState: Selection[] | null; private _afterVersionId: number; + private _afterEOL: EndOfLineSequence; private _afterCursorState: Selection[] | null; private _edits: IValidEditOperations[]; @@ -27,13 +29,15 @@ export class EditStackElement implements IUndoRedoElement { return [this.model.uri]; } - constructor(model: ITextModel, beforeCursorState: Selection[]) { + constructor(model: ITextModel, beforeCursorState: Selection[] | null) { this.label = nls.localize('edit', "Typing"); this._isOpen = true; this.model = model; - this._beforeVersionId = model.getAlternativeVersionId(); + this._beforeVersionId = this.model.getAlternativeVersionId(); + this._beforeEOL = getModelEOL(this.model); this._beforeCursorState = beforeCursorState; this._afterVersionId = this._beforeVersionId; + this._afterEOL = this._beforeEOL; this._afterCursorState = this._beforeCursorState; this._edits = []; } @@ -42,8 +46,11 @@ export class EditStackElement implements IUndoRedoElement { return (this._isOpen && this.model === model); } - public append(model: ITextModel, operations: IValidEditOperation[], afterVersionId: number, afterCursorState: Selection[] | null): void { - this._edits.push({ operations: operations }); + public append(model: ITextModel, operations: IValidEditOperation[], afterEOL: EndOfLineSequence, afterVersionId: number, afterCursorState: Selection[] | null): void { + if (operations.length > 0) { + this._edits.push({ operations: operations }); + } + this._afterEOL = afterEOL; this._afterVersionId = afterVersionId; this._afterCursorState = afterCursorState; } @@ -55,12 +62,12 @@ export class EditStackElement implements IUndoRedoElement { undo(ctx: IUndoRedoContext): void { this._isOpen = false; this._edits.reverse(); - this._edits = this.model._applyEdits(this._edits, true, false, this._beforeVersionId, this._beforeCursorState); + this._edits = this.model._applyUndoRedoEdits(this._edits, this._beforeEOL, true, false, this._beforeVersionId, this._beforeCursorState); } redo(ctx: IUndoRedoContext): void { this._edits.reverse(); - this._edits = this.model._applyEdits(this._edits, false, true, this._afterVersionId, this._afterCursorState); + this._edits = this.model._applyUndoRedoEdits(this._edits, this._afterEOL, false, true, this._afterVersionId, this._afterCursorState); } invalidate(resource: URI): void { @@ -105,9 +112,9 @@ export class MultiEditStackElement implements IUndoRedoElement { return false; } - public append(model: ITextModel, operations: IValidEditOperation[], afterVersionId: number, afterCursorState: Selection[] | null): void { + public append(model: ITextModel, operations: IValidEditOperation[], afterEOL: EndOfLineSequence, afterVersionId: number, afterCursorState: Selection[] | null): void { const editStackElement = this._editStackElementsMap.get(model.id)!; - editStackElement.append(model, operations, afterVersionId, afterCursorState); + editStackElement.append(model, operations, afterEOL, afterVersionId, afterCursorState); } public close(): void { @@ -141,43 +148,6 @@ function getModelEOL(model: ITextModel): EndOfLineSequence { } } -class EOLStackElement implements IUndoRedoElement { - - public readonly label: string; - private readonly _model: TextModel; - private readonly _beforeVersionId: number; - private readonly _afterVersionId: number; - private _eol: EndOfLineSequence; - - public get resources(): readonly URI[] { - return [this._model.uri]; - } - - constructor(model: TextModel, beforeVersionId: number, afterVersionId: number, eol: EndOfLineSequence) { - this.label = nls.localize('eol', "Change End Of Line Sequence"); - this._model = model; - this._beforeVersionId = beforeVersionId; - this._afterVersionId = afterVersionId; - this._eol = eol; - } - - undo(ctx: IUndoRedoContext): void { - const redoEOL = getModelEOL(this._model); - this._model._setEOL(this._eol, true, false, this._beforeVersionId, null); - this._eol = redoEOL; - } - - redo(ctx: IUndoRedoContext): void { - const undoEOL = getModelEOL(this._model); - this._model._setEOL(this._eol, false, true, this._afterVersionId, null); - this._eol = undoEOL; - } - - invalidate(resource: URI): void { - // nothing to do - } -} - export class EditStack { private readonly _model: TextModel; @@ -199,20 +169,7 @@ export class EditStack { this._undoRedoService.removeElements(this._model.uri); } - public pushEOL(eol: EndOfLineSequence): void { - const beforeVersionId = this._model.getAlternativeVersionId(); - const inverseEOL = getModelEOL(this._model); - this._model.setEOL(eol); - const afterVersionId = this._model.getAlternativeVersionId(); - - const lastElement = this._undoRedoService.getLastElement(this._model.uri); - if (lastElement && lastElement instanceof EditStackElement) { - lastElement.close(); - } - this._undoRedoService.pushElement(new EOLStackElement(this._model, inverseEOL, beforeVersionId, afterVersionId)); - } - - private _getOrCreateEditStackElement(beforeCursorState: Selection[]): EditStackElement { + private _getOrCreateEditStackElement(beforeCursorState: Selection[] | null): EditStackElement { const lastElement = this._undoRedoService.getLastElement(this._model.uri); if (lastElement && lastElement instanceof EditStackElement && lastElement.canAppend(this._model)) { return lastElement; @@ -222,11 +179,17 @@ export class EditStack { return newElement; } + public pushEOL(eol: EndOfLineSequence): void { + const editStackElement = this._getOrCreateEditStackElement(null); + this._model.setEOL(eol); + editStackElement.append(this._model, [], getModelEOL(this._model), this._model.getAlternativeVersionId(), null); + } + public pushEditOperation(beforeCursorState: Selection[], editOperations: IIdentifiedSingleEditOperation[], cursorStateComputer: ICursorStateComputer | null): Selection[] | null { const editStackElement = this._getOrCreateEditStackElement(beforeCursorState); const inverseEditOperations = this._model.applyEdits(editOperations); const afterCursorState = EditStack._computeCursorState(cursorStateComputer, inverseEditOperations); - editStackElement.append(this._model, inverseEditOperations, this._model.getAlternativeVersionId(), afterCursorState); + editStackElement.append(this._model, inverseEditOperations, getModelEOL(this._model), this._model.getAlternativeVersionId(), afterCursorState); return afterCursorState; } diff --git a/src/vs/editor/common/model/textModel.ts b/src/vs/editor/common/model/textModel.ts index 9c999e23ce5..a535afe069f 100644 --- a/src/vs/editor/common/model/textModel.ts +++ b/src/vs/editor/common/model/textModel.ts @@ -494,21 +494,6 @@ export class TextModel extends Disposable implements model.ITextModel { ); } - _setEOL(eol: model.EndOfLineSequence, isUndoing: boolean, isRedoing: boolean, resultingAlternativeVersionId: number, resultingSelection: Selection[] | null): void { - try { - this._onDidChangeDecorations.beginDeferredEmit(); - this._eventEmitter.beginDeferredEmit(); - this._isUndoing = isUndoing; - this._isRedoing = isRedoing; - this.setEOL(eol); - this._overwriteAlternativeVersionId(resultingAlternativeVersionId); - } finally { - this._isUndoing = false; - this._eventEmitter.endDeferredEmit(resultingSelection); - this._onDidChangeDecorations.endDeferredEmit(); - } - } - private _onBeforeEOLChange(): void { // Ensure all decorations get their `range` set. const versionId = this.getVersionId(); @@ -1298,7 +1283,7 @@ export class TextModel extends Disposable implements model.ITextModel { return this._commandManager.pushEditOperation(beforeCursorState, editOperations, cursorStateComputer); } - _applyEdits(edits: model.IValidEditOperations[], isUndoing: boolean, isRedoing: boolean, resultingAlternativeVersionId: number, resultingSelection: Selection[] | null): model.IValidEditOperations[] { + _applyUndoRedoEdits(edits: model.IValidEditOperations[], eol: model.EndOfLineSequence, isUndoing: boolean, isRedoing: boolean, resultingAlternativeVersionId: number, resultingSelection: Selection[] | null): model.IValidEditOperations[] { try { this._onDidChangeDecorations.beginDeferredEmit(); this._eventEmitter.beginDeferredEmit(); @@ -1308,10 +1293,12 @@ export class TextModel extends Disposable implements model.ITextModel { for (let i = 0, len = edits.length; i < len; i++) { reverseEdits[i] = { operations: this.applyEdits(edits[i].operations) }; } + this.setEOL(eol); this._overwriteAlternativeVersionId(resultingAlternativeVersionId); return reverseEdits; } finally { this._isUndoing = false; + this._isRedoing = false; this._eventEmitter.endDeferredEmit(resultingSelection); this._onDidChangeDecorations.endDeferredEmit(); } diff --git a/src/vs/editor/test/browser/controller/cursor.test.ts b/src/vs/editor/test/browser/controller/cursor.test.ts index fa60d5989ec..b6984401643 100644 --- a/src/vs/editor/test/browser/controller/cursor.test.ts +++ b/src/vs/editor/test/browser/controller/cursor.test.ts @@ -5551,4 +5551,28 @@ suite('Undo stops', () => { }); }); + test('can undo typing and EOL change in one undo stop', () => { + let model = createTextModel( + [ + 'A line', + 'Another line', + ].join('\n') + ); + + withTestCodeEditor(null, { model: model }, (editor, cursor) => { + cursor.setSelections('test', [new Selection(1, 3, 1, 3)]); + cursorCommand(cursor, H.Type, { text: 'first' }, 'keyboard'); + assert.equal(model.getValue(), 'A first line\nAnother line'); + assertCursor(cursor, new Selection(1, 8, 1, 8)); + + model.pushEOL(EndOfLineSequence.CRLF); + assert.equal(model.getValue(), 'A first line\r\nAnother line'); + assertCursor(cursor, new Selection(1, 8, 1, 8)); + + CoreEditingCommands.Undo.runEditorCommand(null, editor, null); + assert.equal(model.getValue(), 'A line\nAnother line'); + assertCursor(cursor, new Selection(1, 3, 1, 3)); + }); + }); + }); diff --git a/src/vs/workbench/browser/parts/editor/editorStatus.ts b/src/vs/workbench/browser/parts/editor/editorStatus.ts index 64e258c60b0..18ba6d61dcb 100644 --- a/src/vs/workbench/browser/parts/editor/editorStatus.ts +++ b/src/vs/workbench/browser/parts/editor/editorStatus.ts @@ -1224,7 +1224,9 @@ export class ChangeEOLAction extends Action { const activeCodeEditor = getCodeEditor(this.editorService.activeTextEditorWidget); if (activeCodeEditor?.hasModel() && !this.editorService.activeEditor?.isReadonly()) { textModel = activeCodeEditor.getModel(); + textModel.pushStackElement(); textModel.pushEOL(eol.eol); + textModel.pushStackElement(); } } } From 985eea10b1ca1fb6c19c7b2dd950e585915a207d Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 21 Feb 2020 12:18:53 +0100 Subject: [PATCH 041/200] Relax method signature --- src/vs/editor/common/model.ts | 2 +- src/vs/editor/common/model/editStack.ts | 4 ++-- src/vs/editor/common/model/textModel.ts | 32 +++++++++++++------------ src/vs/monaco.d.ts | 2 +- 4 files changed, 21 insertions(+), 19 deletions(-) diff --git a/src/vs/editor/common/model.ts b/src/vs/editor/common/model.ts index df8ad4833db..2bf83cca827 100644 --- a/src/vs/editor/common/model.ts +++ b/src/vs/editor/common/model.ts @@ -1077,7 +1077,7 @@ export interface ITextModel { * @param cursorStateComputer A callback that can compute the resulting cursors state after the edit operations have been executed. * @return The cursor state returned by the `cursorStateComputer`. */ - pushEditOperations(beforeCursorState: Selection[], editOperations: IIdentifiedSingleEditOperation[], cursorStateComputer: ICursorStateComputer): Selection[] | null; + pushEditOperations(beforeCursorState: Selection[] | null, editOperations: IIdentifiedSingleEditOperation[], cursorStateComputer: ICursorStateComputer): Selection[] | null; /** * Change the end of line sequence. This is the preferred way of diff --git a/src/vs/editor/common/model/editStack.ts b/src/vs/editor/common/model/editStack.ts index 92bab6cd80e..9b5d53c6bda 100644 --- a/src/vs/editor/common/model/editStack.ts +++ b/src/vs/editor/common/model/editStack.ts @@ -75,7 +75,7 @@ export class EditStackElement implements IUndoRedoElement { } } -export class MultiEditStackElement implements IUndoRedoElement { +export class MultiModelEditStackElement implements IUndoRedoElement { public readonly label: string; private _isOpen: boolean; @@ -185,7 +185,7 @@ export class EditStack { editStackElement.append(this._model, [], getModelEOL(this._model), this._model.getAlternativeVersionId(), null); } - public pushEditOperation(beforeCursorState: Selection[], editOperations: IIdentifiedSingleEditOperation[], cursorStateComputer: ICursorStateComputer | null): Selection[] | null { + public pushEditOperation(beforeCursorState: Selection[] | null, editOperations: IIdentifiedSingleEditOperation[], cursorStateComputer: ICursorStateComputer | null): Selection[] | null { const editStackElement = this._getOrCreateEditStackElement(beforeCursorState); const inverseEditOperations = this._model.applyEdits(editOperations); const afterCursorState = EditStack._computeCursorState(cursorStateComputer, inverseEditOperations); diff --git a/src/vs/editor/common/model/textModel.ts b/src/vs/editor/common/model/textModel.ts index a535afe069f..bba23d05d11 100644 --- a/src/vs/editor/common/model/textModel.ts +++ b/src/vs/editor/common/model/textModel.ts @@ -1187,7 +1187,7 @@ export class TextModel extends Disposable implements model.ITextModel { return result; } - public pushEditOperations(beforeCursorState: Selection[], editOperations: model.IIdentifiedSingleEditOperation[], cursorStateComputer: model.ICursorStateComputer | null): Selection[] | null { + public pushEditOperations(beforeCursorState: Selection[] | null, editOperations: model.IIdentifiedSingleEditOperation[], cursorStateComputer: model.ICursorStateComputer | null): Selection[] | null { try { this._onDidChangeDecorations.beginDeferredEmit(); this._eventEmitter.beginDeferredEmit(); @@ -1198,7 +1198,7 @@ export class TextModel extends Disposable implements model.ITextModel { } } - private _pushEditOperations(beforeCursorState: Selection[], editOperations: model.ValidAnnotatedEditOperation[], cursorStateComputer: model.ICursorStateComputer | null): Selection[] | null { + private _pushEditOperations(beforeCursorState: Selection[] | null, editOperations: model.ValidAnnotatedEditOperation[], cursorStateComputer: model.ICursorStateComputer | null): Selection[] | null { if (this._options.trimAutoWhitespace && this._trimAutoWhitespaceLines) { // Go through each saved line number and insert a trim whitespace edit // if it is safe to do so (no conflicts with other edits). @@ -1213,22 +1213,24 @@ export class TextModel extends Disposable implements model.ITextModel { // Sometimes, auto-formatters change ranges automatically which can cause undesired auto whitespace trimming near the cursor // We'll use the following heuristic: if the edits occur near the cursor, then it's ok to trim auto whitespace let editsAreNearCursors = true; - for (let i = 0, len = beforeCursorState.length; i < len; i++) { - let sel = beforeCursorState[i]; - let foundEditNearSel = false; - for (let j = 0, lenJ = incomingEdits.length; j < lenJ; j++) { - let editRange = incomingEdits[j].range; - let selIsAbove = editRange.startLineNumber > sel.endLineNumber; - let selIsBelow = sel.startLineNumber > editRange.endLineNumber; - if (!selIsAbove && !selIsBelow) { - foundEditNearSel = true; + if (beforeCursorState) { + for (let i = 0, len = beforeCursorState.length; i < len; i++) { + let sel = beforeCursorState[i]; + let foundEditNearSel = false; + for (let j = 0, lenJ = incomingEdits.length; j < lenJ; j++) { + let editRange = incomingEdits[j].range; + let selIsAbove = editRange.startLineNumber > sel.endLineNumber; + let selIsBelow = sel.startLineNumber > editRange.endLineNumber; + if (!selIsAbove && !selIsBelow) { + foundEditNearSel = true; + break; + } + } + if (!foundEditNearSel) { + editsAreNearCursors = false; break; } } - if (!foundEditNearSel) { - editsAreNearCursors = false; - break; - } } if (editsAreNearCursors) { diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 628fea221a7..9fb1bf00712 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -1884,7 +1884,7 @@ declare namespace monaco.editor { * @param cursorStateComputer A callback that can compute the resulting cursors state after the edit operations have been executed. * @return The cursor state returned by the `cursorStateComputer`. */ - pushEditOperations(beforeCursorState: Selection[], editOperations: IIdentifiedSingleEditOperation[], cursorStateComputer: ICursorStateComputer): Selection[] | null; + pushEditOperations(beforeCursorState: Selection[] | null, editOperations: IIdentifiedSingleEditOperation[], cursorStateComputer: ICursorStateComputer): Selection[] | null; /** * Change the end of line sequence. This is the preferred way of * changing the eol sequence. This will land on the undo stack. From 4ae83d0a4b4269e242cb41bf594ad05fee331ebe Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 21 Feb 2020 15:51:23 +0100 Subject: [PATCH 042/200] First cut at supporting undo across files --- src/vs/editor/common/model/editStack.ts | 113 +++++++++++++++--- .../undoRedo/common/undoRedoService.ts | 41 +++++-- .../bulkEdit/browser/bulkEditService.ts | 61 +++++++--- 3 files changed, 173 insertions(+), 42 deletions(-) diff --git a/src/vs/editor/common/model/editStack.ts b/src/vs/editor/common/model/editStack.ts index 9b5d53c6bda..f14bcfa2e35 100644 --- a/src/vs/editor/common/model/editStack.ts +++ b/src/vs/editor/common/model/editStack.ts @@ -11,11 +11,14 @@ import { TextModel } from 'vs/editor/common/model/textModel'; import { IUndoRedoService, IUndoRedoElement, IUndoRedoContext } from 'vs/platform/undoRedo/common/undoRedo'; import { URI } from 'vs/base/common/uri'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { getComparisonKey as uriGetComparisonKey } from 'vs/base/common/resources'; +import { Severity } from 'vs/platform/notification/common/notification'; export class EditStackElement implements IUndoRedoElement { public readonly label: string; private _isOpen: boolean; + private _isValid: boolean; public readonly model: ITextModel; private readonly _beforeVersionId: number; private readonly _beforeEOL: EndOfLineSequence; @@ -32,6 +35,7 @@ export class EditStackElement implements IUndoRedoElement { constructor(model: ITextModel, beforeCursorState: Selection[] | null) { this.label = nls.localize('edit', "Typing"); this._isOpen = true; + this._isValid = true; this.model = model; this._beforeVersionId = this.model.getAlternativeVersionId(); this._beforeEOL = getModelEOL(this.model); @@ -42,6 +46,10 @@ export class EditStackElement implements IUndoRedoElement { this._edits = []; } + public isValid(): boolean { + return this._isValid; + } + public canAppend(model: ITextModel): boolean { return (this._isOpen && this.model === model); } @@ -59,19 +67,33 @@ export class EditStackElement implements IUndoRedoElement { this._isOpen = false; } - undo(ctx: IUndoRedoContext): void { + public canUndo(): boolean { + if (!this._isValid) { + return false; + } + return (this._afterVersionId === this.model.getAlternativeVersionId()); + } + + public undo(ctx: IUndoRedoContext): void { this._isOpen = false; this._edits.reverse(); this._edits = this.model._applyUndoRedoEdits(this._edits, this._beforeEOL, true, false, this._beforeVersionId, this._beforeCursorState); } - redo(ctx: IUndoRedoContext): void { + public canRedo(): boolean { + if (!this._isValid) { + return false; + } + return (this._beforeVersionId === this.model.getAlternativeVersionId()); + } + + public redo(ctx: IUndoRedoContext): void { this._edits.reverse(); this._edits = this.model._applyUndoRedoEdits(this._edits, this._afterEOL, false, true, this._afterVersionId, this._afterCursorState); } - invalidate(resource: URI): void { - // nothing to do + public invalidate(resource: URI): void { + this._isValid = false; } } @@ -82,6 +104,7 @@ export class MultiModelEditStackElement implements IUndoRedoElement { private readonly _editStackElementsArr: EditStackElement[]; private readonly _editStackElementsMap: Map; + private _isValid: boolean; public get resources(): readonly URI[] { return this._editStackElementsArr.map(editStackElement => editStackElement.model.uri); @@ -90,30 +113,34 @@ export class MultiModelEditStackElement implements IUndoRedoElement { constructor( label: string, editStackElements: EditStackElement[], - @IDialogService dialogService: IDialogService + @IDialogService private readonly _dialogService: IDialogService ) { this.label = label; this._isOpen = true; this._editStackElementsArr = editStackElements.slice(0); this._editStackElementsMap = new Map(); for (const editStackElement of this._editStackElementsArr) { - this._editStackElementsMap.set(editStackElement.model.id, editStackElement); + const key = uriGetComparisonKey(editStackElement.model.uri); + this._editStackElementsMap.set(key, editStackElement); } + this._isValid = true; } public canAppend(model: ITextModel): boolean { if (!this._isOpen) { return false; } - if (this._editStackElementsMap.has(model.id)) { - const editStackElement = this._editStackElementsMap.get(model.id)!; + const key = uriGetComparisonKey(model.uri); + if (this._editStackElementsMap.has(key)) { + const editStackElement = this._editStackElementsMap.get(key)!; return editStackElement.canAppend(model); } return false; } public append(model: ITextModel, operations: IValidEditOperation[], afterEOL: EndOfLineSequence, afterVersionId: number, afterCursorState: Selection[] | null): void { - const editStackElement = this._editStackElementsMap.get(model.id)!; + const key = uriGetComparisonKey(model.uri); + const editStackElement = this._editStackElementsMap.get(key)!; editStackElement.append(model, operations, afterEOL, afterVersionId, afterCursorState); } @@ -121,21 +148,66 @@ export class MultiModelEditStackElement implements IUndoRedoElement { this._isOpen = false; } + private _canUndo(): boolean { + if (!this._isValid) { + return false; + } + for (const editStackElement of this._editStackElementsArr) { + if (!editStackElement.canUndo()) { + return false; + } + } + return true; + } + undo(ctx: IUndoRedoContext): void { this._isOpen = false; - for (const editStackElement of this._editStackElementsArr) { - editStackElement.undo(ctx); + + if (this._canUndo()) { + for (const editStackElement of this._editStackElementsArr) { + editStackElement.undo(ctx); + } + } else { + // cannot apply! + const validStackElements = this._editStackElementsArr.filter(stackElement => stackElement.isValid()); + ctx.replaceCurrentElement(validStackElements); + this._dialogService.show(Severity.Info, nls.localize('workspace', "Could not apply the edit in all the impacted files."), []); } } - redo(ctx: IUndoRedoContext): void { + private _canRedo(): boolean { + if (!this._isValid) { + return false; + } for (const editStackElement of this._editStackElementsArr) { - editStackElement.redo(ctx); + if (!editStackElement.canRedo()) { + return false; + } + } + return true; + } + + redo(ctx: IUndoRedoContext): void { + if (this._canRedo()) { + for (const editStackElement of this._editStackElementsArr) { + editStackElement.redo(ctx); + } + } else { + // cannot apply! + const validStackElements = this._editStackElementsArr.filter(stackElement => stackElement.isValid()); + ctx.replaceCurrentElement(validStackElements); + this._dialogService.show(Severity.Info, nls.localize('workspace', "Could not apply the edit in all the impacted files."), []); } } invalidate(resource: URI): void { - console.log(`MULTI INVALIDATE: ${resource}`); + const key = uriGetComparisonKey(resource); + if (!this._editStackElementsMap.has(key)) { + return; + } + this._isValid = false; + const stackElement = this._editStackElementsMap.get(key)!; + stackElement.invalidate(resource); } } @@ -148,6 +220,13 @@ function getModelEOL(model: ITextModel): EndOfLineSequence { } } +function isKnownStackElement(element: IUndoRedoElement | null): element is EditStackElement | MultiModelEditStackElement { + if (!element) { + return false; + } + return ((element instanceof EditStackElement) || (element instanceof MultiModelEditStackElement)); +} + export class EditStack { private readonly _model: TextModel; @@ -160,7 +239,7 @@ export class EditStack { public pushStackElement(): void { const lastElement = this._undoRedoService.getLastElement(this._model.uri); - if (lastElement && lastElement instanceof EditStackElement) { + if (isKnownStackElement(lastElement)) { lastElement.close(); } } @@ -169,9 +248,9 @@ export class EditStack { this._undoRedoService.removeElements(this._model.uri); } - private _getOrCreateEditStackElement(beforeCursorState: Selection[] | null): EditStackElement { + private _getOrCreateEditStackElement(beforeCursorState: Selection[] | null): EditStackElement | MultiModelEditStackElement { const lastElement = this._undoRedoService.getLastElement(this._model.uri); - if (lastElement && lastElement instanceof EditStackElement && lastElement.canAppend(this._model)) { + if (isKnownStackElement(lastElement) && lastElement.canAppend(this._model)) { return lastElement; } const newElement = new EditStackElement(this._model, beforeCursorState); diff --git a/src/vs/platform/undoRedo/common/undoRedoService.ts b/src/vs/platform/undoRedo/common/undoRedoService.ts index dfa7fb4cea2..78f42f43b9d 100644 --- a/src/vs/platform/undoRedo/common/undoRedoService.ts +++ b/src/vs/platform/undoRedo/common/undoRedoService.ts @@ -12,20 +12,27 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; class StackElement { public readonly actual: IUndoRedoElement; public readonly label: string; - public readonly resources: readonly URI[]; - public readonly strResources: string[]; + + public resources: URI[]; + public strResources: string[]; constructor(actual: IUndoRedoElement) { this.actual = actual; this.label = actual.label; - this.resources = actual.resources; + this.resources = actual.resources.slice(0); this.strResources = this.resources.map(resource => uriGetComparisonKey(resource)); } public invalidate(resource: URI): void { - if (this.resources.length > 1) { - this.actual.invalidate(resource); + const strResource = uriGetComparisonKey(resource); + for (let i = 0, len = this.strResources.length; i < len; i++) { + if (this.strResources[i] === strResource) { + this.resources.splice(i, 1); + this.strResources.splice(i, 1); + break; + } } + this.actual.invalidate(resource); } } @@ -146,10 +153,14 @@ export class UndoRedoService implements IUndoRedoService { } const replaceCurrentElementMap = new Map(); + let foundReplacementForThisResource = false; for (const _replace of replaceCurrentElement) { const replace = new StackElement(_replace); - for (const strResource of replace.strResources) { - replaceCurrentElementMap.set(strResource, replace); + for (const strReplaceResource of replace.strResources) { + replaceCurrentElementMap.set(strReplaceResource, replace); + if (strReplaceResource === strResource) { + foundReplacementForThisResource = true; + } } } @@ -169,6 +180,10 @@ export class UndoRedoService implements IUndoRedoService { } } } + + if (foundReplacementForThisResource) { + this.undo(resource); + } } public canRedo(resource: URI): boolean { @@ -214,11 +229,15 @@ export class UndoRedoService implements IUndoRedoService { return; } + let foundReplacementForThisResource = false; const replaceCurrentElementMap = new Map(); for (const _replace of replaceCurrentElement) { const replace = new StackElement(_replace); - for (const strResource of replace.strResources) { - replaceCurrentElementMap.set(strResource, replace); + for (const strReplaceResource of replace.strResources) { + replaceCurrentElementMap.set(strReplaceResource, replace); + if (strReplaceResource === strResource) { + foundReplacementForThisResource = true; + } } } @@ -238,6 +257,10 @@ export class UndoRedoService implements IUndoRedoService { } } } + + if (foundReplacementForThisResource) { + this.redo(resource); + } } } diff --git a/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts b/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts index e5ea9a06e75..9bb5819cc21 100644 --- a/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts +++ b/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts @@ -10,6 +10,7 @@ import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { IBulkEditOptions, IBulkEditResult, IBulkEditService, IBulkEditPreviewHandler } from 'vs/editor/browser/services/bulkEditService'; import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; import { EndOfLineSequence, IIdentifiedSingleEditOperation, ITextModel } from 'vs/editor/common/model'; import { WorkspaceFileEdit, WorkspaceTextEdit, WorkspaceEdit } from 'vs/editor/common/modes'; import { IModelService } from 'vs/editor/common/services/modelService'; @@ -26,20 +27,24 @@ import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService'; +import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; +import { EditStackElement, MultiModelEditStackElement } from 'vs/editor/common/model/editStack'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; +const USE_MULTI_MODEL_EDIT_STACK_ELEMENT = false; type ValidationResult = { canApply: true } | { canApply: false, reason: URI }; class ModelEditTask implements IDisposable { - private readonly _model: ITextModel; + public readonly model: ITextModel; protected _edits: IIdentifiedSingleEditOperation[]; private _expectedModelVersionId: number | undefined; protected _newEol: EndOfLineSequence | undefined; constructor(private readonly _modelReference: IReference) { - this._model = this._modelReference.object.textEditorModel; + this.model = this._modelReference.object.textEditorModel; this._edits = []; } @@ -67,7 +72,7 @@ class ModelEditTask implements IDisposable { // create edit operation let range: Range; if (!edit.range) { - range = this._model.getFullModelRange(); + range = this.model.getFullModelRange(); } else { range = Range.lift(edit.range); } @@ -75,23 +80,23 @@ class ModelEditTask implements IDisposable { } validate(): ValidationResult { - if (typeof this._expectedModelVersionId === 'undefined' || this._model.getVersionId() === this._expectedModelVersionId) { + if (typeof this._expectedModelVersionId === 'undefined' || this.model.getVersionId() === this._expectedModelVersionId) { return { canApply: true }; } - return { canApply: false, reason: this._model.uri }; + return { canApply: false, reason: this.model.uri }; + } + + getBeforeCursorState(): Selection[] | null { + return null; } apply(): void { if (this._edits.length > 0) { this._edits = mergeSort(this._edits, (a, b) => Range.compareRangesUsingStarts(a.range, b.range)); - this._model.pushStackElement(); - this._model.pushEditOperations([], this._edits, () => []); - this._model.pushStackElement(); + this.model.pushEditOperations(null, this._edits, () => null); } if (this._newEol !== undefined) { - this._model.pushStackElement(); - this._model.pushEOL(this._newEol); - this._model.pushStackElement(); + this.model.pushEOL(this._newEol); } } } @@ -105,18 +110,18 @@ class EditorEditTask extends ModelEditTask { this._editor = editor; } + getBeforeCursorState(): Selection[] | null { + return this._editor.getSelections(); + } + apply(): void { if (this._edits.length > 0) { this._edits = mergeSort(this._edits, (a, b) => Range.compareRangesUsingStarts(a.range, b.range)); - this._editor.pushUndoStop(); this._editor.executeEdits('', this._edits); - this._editor.pushUndoStop(); } if (this._newEol !== undefined) { if (this._editor.hasModel()) { - this._editor.pushUndoStop(); this._editor.getModel().pushEOL(this._newEol); - this._editor.pushUndoStop(); } } } @@ -133,6 +138,8 @@ class BulkEditModel implements IDisposable { edits: WorkspaceTextEdit[], @IEditorWorkerService private readonly _editorWorker: IEditorWorkerService, @ITextModelService private readonly _textModelResolverService: ITextModelService, + @IUndoRedoService private readonly _undoRedoService: IUndoRedoService, + @IDialogService private readonly _dialogService: IDialogService ) { edits.forEach(this._addEdit, this); } @@ -215,10 +222,32 @@ class BulkEditModel implements IDisposable { } apply(): void { - for (const task of this._tasks!) { + const tasks = this._tasks!; + + if (tasks.length === 1 || !USE_MULTI_MODEL_EDIT_STACK_ELEMENT) { + // This edit touches a single model => keep things simple + for (const task of tasks) { + task.model.pushStackElement(); + task.apply(); + task.model.pushStackElement(); + this._progress.report(undefined); + } + return; + } + + const multiModelEditStackElement = new MultiModelEditStackElement( + localize('workspaceEdit', "Workspace Edit"), + tasks.map(t => new EditStackElement(t.model, t.getBeforeCursorState())), + this._dialogService + ); + this._undoRedoService.pushElement(multiModelEditStackElement); + + for (const task of tasks) { task.apply(); this._progress.report(undefined); } + + multiModelEditStackElement.close(); } } From 691c4b15918c53664a290a7401a7c3ab9f81122e Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 21 Feb 2020 21:36:27 +0100 Subject: [PATCH 043/200] Cross-file undo/redo --- src/vs/editor/common/model/editStack.ts | 111 +----- src/vs/editor/common/model/textModel.ts | 21 +- .../contrib/find/test/findModel.test.ts | 4 +- .../smartSelect/test/smartSelect.test.ts | 4 +- .../standalone/browser/standaloneServices.ts | 2 +- .../test/common/services/modelService.test.ts | 4 +- .../dialogs/test/common/testDialogService.ts | 16 + .../common/instantiationService.ts | 2 +- src/vs/platform/undoRedo/common/undoRedo.ts | 56 +-- .../undoRedo/common/undoRedoService.ts | 353 +++++++++++------- .../bulkEdit/browser/bulkEditService.ts | 11 +- .../api/mainThreadDocumentsAndEditors.test.ts | 7 +- .../browser/api/mainThreadEditors.test.ts | 13 +- .../browser/parts/editor/editorModel.test.ts | 11 +- .../test/browser/workbenchTestServices.ts | 11 +- .../quickopen.perf.integrationTest.ts | 13 +- .../textsearch.perf.integrationTest.ts | 13 +- 17 files changed, 364 insertions(+), 288 deletions(-) create mode 100644 src/vs/platform/dialogs/test/common/testDialogService.ts diff --git a/src/vs/editor/common/model/editStack.ts b/src/vs/editor/common/model/editStack.ts index f14bcfa2e35..c0aee2abe10 100644 --- a/src/vs/editor/common/model/editStack.ts +++ b/src/vs/editor/common/model/editStack.ts @@ -8,17 +8,15 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { Selection } from 'vs/editor/common/core/selection'; import { EndOfLineSequence, ICursorStateComputer, IIdentifiedSingleEditOperation, IValidEditOperation, ITextModel, IValidEditOperations } from 'vs/editor/common/model'; import { TextModel } from 'vs/editor/common/model/textModel'; -import { IUndoRedoService, IUndoRedoElement, IUndoRedoContext } from 'vs/platform/undoRedo/common/undoRedo'; +import { IUndoRedoService, IResourceUndoRedoElement, UndoRedoElementType, IWorkspaceUndoRedoElement } from 'vs/platform/undoRedo/common/undoRedo'; import { URI } from 'vs/base/common/uri'; -import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { getComparisonKey as uriGetComparisonKey } from 'vs/base/common/resources'; -import { Severity } from 'vs/platform/notification/common/notification'; -export class EditStackElement implements IUndoRedoElement { +export class EditStackElement implements IResourceUndoRedoElement { + public readonly type = UndoRedoElementType.Resource; public readonly label: string; private _isOpen: boolean; - private _isValid: boolean; public readonly model: ITextModel; private readonly _beforeVersionId: number; private readonly _beforeEOL: EndOfLineSequence; @@ -28,14 +26,13 @@ export class EditStackElement implements IUndoRedoElement { private _afterCursorState: Selection[] | null; private _edits: IValidEditOperations[]; - public get resources(): readonly URI[] { - return [this.model.uri]; + public get resource(): URI { + return this.model.uri; } constructor(model: ITextModel, beforeCursorState: Selection[] | null) { this.label = nls.localize('edit', "Typing"); this._isOpen = true; - this._isValid = true; this.model = model; this._beforeVersionId = this.model.getAlternativeVersionId(); this._beforeEOL = getModelEOL(this.model); @@ -46,10 +43,6 @@ export class EditStackElement implements IUndoRedoElement { this._edits = []; } - public isValid(): boolean { - return this._isValid; - } - public canAppend(model: ITextModel): boolean { return (this._isOpen && this.model === model); } @@ -67,44 +60,26 @@ export class EditStackElement implements IUndoRedoElement { this._isOpen = false; } - public canUndo(): boolean { - if (!this._isValid) { - return false; - } - return (this._afterVersionId === this.model.getAlternativeVersionId()); - } - - public undo(ctx: IUndoRedoContext): void { + public undo(): void { this._isOpen = false; this._edits.reverse(); this._edits = this.model._applyUndoRedoEdits(this._edits, this._beforeEOL, true, false, this._beforeVersionId, this._beforeCursorState); } - public canRedo(): boolean { - if (!this._isValid) { - return false; - } - return (this._beforeVersionId === this.model.getAlternativeVersionId()); - } - - public redo(ctx: IUndoRedoContext): void { + public redo(): void { this._edits.reverse(); this._edits = this.model._applyUndoRedoEdits(this._edits, this._afterEOL, false, true, this._afterVersionId, this._afterCursorState); } - - public invalidate(resource: URI): void { - this._isValid = false; - } } -export class MultiModelEditStackElement implements IUndoRedoElement { +export class MultiModelEditStackElement implements IWorkspaceUndoRedoElement { + public readonly type = UndoRedoElementType.Workspace; public readonly label: string; private _isOpen: boolean; private readonly _editStackElementsArr: EditStackElement[]; private readonly _editStackElementsMap: Map; - private _isValid: boolean; public get resources(): readonly URI[] { return this._editStackElementsArr.map(editStackElement => editStackElement.model.uri); @@ -112,8 +87,7 @@ export class MultiModelEditStackElement implements IUndoRedoElement { constructor( label: string, - editStackElements: EditStackElement[], - @IDialogService private readonly _dialogService: IDialogService + editStackElements: EditStackElement[] ) { this.label = label; this._isOpen = true; @@ -123,7 +97,6 @@ export class MultiModelEditStackElement implements IUndoRedoElement { const key = uriGetComparisonKey(editStackElement.model.uri); this._editStackElementsMap.set(key, editStackElement); } - this._isValid = true; } public canAppend(model: ITextModel): boolean { @@ -148,66 +121,22 @@ export class MultiModelEditStackElement implements IUndoRedoElement { this._isOpen = false; } - private _canUndo(): boolean { - if (!this._isValid) { - return false; - } - for (const editStackElement of this._editStackElementsArr) { - if (!editStackElement.canUndo()) { - return false; - } - } - return true; - } - - undo(ctx: IUndoRedoContext): void { + public undo(): void { this._isOpen = false; - if (this._canUndo()) { - for (const editStackElement of this._editStackElementsArr) { - editStackElement.undo(ctx); - } - } else { - // cannot apply! - const validStackElements = this._editStackElementsArr.filter(stackElement => stackElement.isValid()); - ctx.replaceCurrentElement(validStackElements); - this._dialogService.show(Severity.Info, nls.localize('workspace', "Could not apply the edit in all the impacted files."), []); - } - } - - private _canRedo(): boolean { - if (!this._isValid) { - return false; - } for (const editStackElement of this._editStackElementsArr) { - if (!editStackElement.canRedo()) { - return false; - } - } - return true; - } - - redo(ctx: IUndoRedoContext): void { - if (this._canRedo()) { - for (const editStackElement of this._editStackElementsArr) { - editStackElement.redo(ctx); - } - } else { - // cannot apply! - const validStackElements = this._editStackElementsArr.filter(stackElement => stackElement.isValid()); - ctx.replaceCurrentElement(validStackElements); - this._dialogService.show(Severity.Info, nls.localize('workspace', "Could not apply the edit in all the impacted files."), []); + editStackElement.undo(); } } - invalidate(resource: URI): void { - const key = uriGetComparisonKey(resource); - if (!this._editStackElementsMap.has(key)) { - return; + public redo(): void { + for (const editStackElement of this._editStackElementsArr) { + editStackElement.redo(); } - this._isValid = false; - const stackElement = this._editStackElementsMap.get(key)!; - stackElement.invalidate(resource); + } + + public split(): IResourceUndoRedoElement[] { + return this._editStackElementsArr; } } @@ -220,7 +149,7 @@ function getModelEOL(model: ITextModel): EndOfLineSequence { } } -function isKnownStackElement(element: IUndoRedoElement | null): element is EditStackElement | MultiModelEditStackElement { +function isKnownStackElement(element: IResourceUndoRedoElement | IWorkspaceUndoRedoElement | null): element is EditStackElement | MultiModelEditStackElement { if (!element) { return false; } diff --git a/src/vs/editor/common/model/textModel.ts b/src/vs/editor/common/model/textModel.ts index bba23d05d11..612d22bc66f 100644 --- a/src/vs/editor/common/model/textModel.ts +++ b/src/vs/editor/common/model/textModel.ts @@ -38,6 +38,8 @@ import { Constants } from 'vs/base/common/uint'; import { EditorTheme } from 'vs/editor/common/view/viewContext'; import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; import { UndoRedoService } from 'vs/platform/undoRedo/common/undoRedoService'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { NoOpNotification, INotificationService } from 'vs/platform/notification/common/notification'; function createTextBufferBuilder() { return new PieceTreeTextBufferBuilder(); @@ -190,7 +192,24 @@ export class TextModel extends Disposable implements model.ITextModel { }; public static createFromString(text: string, options: model.ITextModelCreationOptions = TextModel.DEFAULT_CREATION_OPTIONS, languageIdentifier: LanguageIdentifier | null = null, uri: URI | null = null): TextModel { - return new TextModel(text, options, languageIdentifier, uri, new UndoRedoService()); + const dialogService = new class implements IDialogService { + _serviceBrand: undefined; + confirm() { return Promise.resolve({ confirmed: false }); } + show() { return Promise.resolve({ choice: 0 }); } + about() { return Promise.resolve(); } + }; + const noop = new NoOpNotification(); + const notificationService = new class implements INotificationService { + _serviceBrand: undefined; + info() { return noop; } + warn() { return noop; } + error() { return noop; } + notify() { return noop; } + prompt() { return noop; } + status() { return Disposable.None; } + setFilter() { } + }; + return new TextModel(text, options, languageIdentifier, uri, new UndoRedoService(dialogService, notificationService)); } public static resolveOptions(textBuffer: model.ITextBuffer, options: model.ITextModelCreationOptions): model.TextModelResolvedOptions { diff --git a/src/vs/editor/contrib/find/test/findModel.test.ts b/src/vs/editor/contrib/find/test/findModel.test.ts index 2e05ead3e79..f82c0d542ae 100644 --- a/src/vs/editor/contrib/find/test/findModel.test.ts +++ b/src/vs/editor/contrib/find/test/findModel.test.ts @@ -16,6 +16,8 @@ import { FindModelBoundToEditorModel } from 'vs/editor/contrib/find/findModel'; import { FindReplaceState } from 'vs/editor/contrib/find/findState'; import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; import { UndoRedoService } from 'vs/platform/undoRedo/common/undoRedoService'; +import { TestDialogService } from 'vs/platform/dialogs/test/common/testDialogService'; +import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService'; suite('FindModel', () => { @@ -45,7 +47,7 @@ suite('FindModel', () => { const factory = ptBuilder.finish(); withTestCodeEditor([], { - model: new TextModel(factory, TextModel.DEFAULT_CREATION_OPTIONS, null, null, new UndoRedoService()) + model: new TextModel(factory, TextModel.DEFAULT_CREATION_OPTIONS, null, null, new UndoRedoService(new TestDialogService(), new TestNotificationService())) }, (editor, cursor) => callback(editor as unknown as IActiveCodeEditor, cursor) ); diff --git a/src/vs/editor/contrib/smartSelect/test/smartSelect.test.ts b/src/vs/editor/contrib/smartSelect/test/smartSelect.test.ts index 4ebcba775cc..e56cb2bb72f 100644 --- a/src/vs/editor/contrib/smartSelect/test/smartSelect.test.ts +++ b/src/vs/editor/contrib/smartSelect/test/smartSelect.test.ts @@ -20,6 +20,8 @@ import { TestTextResourcePropertiesService } from 'vs/editor/test/common/service import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; import { NullLogService } from 'vs/platform/log/common/log'; import { UndoRedoService } from 'vs/platform/undoRedo/common/undoRedoService'; +import { TestDialogService } from 'vs/platform/dialogs/test/common/testDialogService'; +import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService'; class MockJSMode extends MockMode { @@ -48,7 +50,7 @@ suite('SmartSelect', () => { setup(() => { const configurationService = new TestConfigurationService(); - modelService = new ModelServiceImpl(configurationService, new TestTextResourcePropertiesService(configurationService), new TestThemeService(), new NullLogService(), new UndoRedoService()); + modelService = new ModelServiceImpl(configurationService, new TestTextResourcePropertiesService(configurationService), new TestThemeService(), new NullLogService(), new UndoRedoService(new TestDialogService(), new TestNotificationService())); mode = new MockJSMode(); }); diff --git a/src/vs/editor/standalone/browser/standaloneServices.ts b/src/vs/editor/standalone/browser/standaloneServices.ts index 1dc78d68e40..b92c8ae812c 100644 --- a/src/vs/editor/standalone/browser/standaloneServices.ts +++ b/src/vs/editor/standalone/browser/standaloneServices.ts @@ -152,7 +152,7 @@ export module StaticServices { export const logService = define(ILogService, () => new NullLogService()); - export const undoRedoService = define(IUndoRedoService, () => new UndoRedoService()); + export const undoRedoService = define(IUndoRedoService, (o) => new UndoRedoService(dialogService.get(o), notificationService.get(o))); export const modelService = define(IModelService, (o) => new ModelServiceImpl(configurationService.get(o), resourcePropertiesService.get(o), standaloneThemeService.get(o), logService.get(o), undoRedoService.get(o))); diff --git a/src/vs/editor/test/common/services/modelService.test.ts b/src/vs/editor/test/common/services/modelService.test.ts index cd2df2cb74e..72ec78849d8 100644 --- a/src/vs/editor/test/common/services/modelService.test.ts +++ b/src/vs/editor/test/common/services/modelService.test.ts @@ -19,6 +19,8 @@ import { TestConfigurationService } from 'vs/platform/configuration/test/common/ import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; import { NullLogService } from 'vs/platform/log/common/log'; import { UndoRedoService } from 'vs/platform/undoRedo/common/undoRedoService'; +import { TestDialogService } from 'vs/platform/dialogs/test/common/testDialogService'; +import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService'; const GENERATE_TESTS = false; @@ -30,7 +32,7 @@ suite('ModelService', () => { configService.setUserConfiguration('files', { 'eol': '\n' }); configService.setUserConfiguration('files', { 'eol': '\r\n' }, URI.file(platform.isWindows ? 'c:\\myroot' : '/myroot')); - modelService = new ModelServiceImpl(configService, new TestTextResourcePropertiesService(configService), new TestThemeService(), new NullLogService(), new UndoRedoService()); + modelService = new ModelServiceImpl(configService, new TestTextResourcePropertiesService(configService), new TestThemeService(), new NullLogService(), new UndoRedoService(new TestDialogService(), new TestNotificationService())); }); teardown(() => { diff --git a/src/vs/platform/dialogs/test/common/testDialogService.ts b/src/vs/platform/dialogs/test/common/testDialogService.ts new file mode 100644 index 00000000000..0f1cbc0f992 --- /dev/null +++ b/src/vs/platform/dialogs/test/common/testDialogService.ts @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import Severity from 'vs/base/common/severity'; +import { IConfirmation, IConfirmationResult, IDialogService, IDialogOptions, IShowResult } from 'vs/platform/dialogs/common/dialogs'; + +export class TestDialogService implements IDialogService { + + _serviceBrand: undefined; + + confirm(_confirmation: IConfirmation): Promise { return Promise.resolve({ confirmed: false }); } + show(_severity: Severity, _message: string, _buttons: string[], _options?: IDialogOptions): Promise { return Promise.resolve({ choice: 0 }); } + about(): Promise { return Promise.resolve(); } +} diff --git a/src/vs/platform/instantiation/common/instantiationService.ts b/src/vs/platform/instantiation/common/instantiationService.ts index 0b010882458..bb33f2e1195 100644 --- a/src/vs/platform/instantiation/common/instantiationService.ts +++ b/src/vs/platform/instantiation/common/instantiationService.ts @@ -157,7 +157,7 @@ export class InstantiationService implements IInstantiationService { graph.lookupOrInsertNode(item); // a weak but working heuristic for cycle checks - if (cycleCount++ > 150) { + if (cycleCount++ > 200) { throw new CyclicDependencyError(graph); } diff --git a/src/vs/platform/undoRedo/common/undoRedo.ts b/src/vs/platform/undoRedo/common/undoRedo.ts index 75e08d09dcc..b8fef5dd2f6 100644 --- a/src/vs/platform/undoRedo/common/undoRedo.ts +++ b/src/vs/platform/undoRedo/common/undoRedo.ts @@ -8,42 +8,26 @@ import { URI } from 'vs/base/common/uri'; export const IUndoRedoService = createDecorator('undoRedoService'); -export interface IUndoRedoContext { - replaceCurrentElement(others: IUndoRedoElement[]): void; +export const enum UndoRedoElementType { + Resource, + Workspace } -export interface IUndoRedoElement { - /** - * None, one or multiple resources that this undo/redo element impacts. - */ - readonly resources: readonly URI[]; - - /** - * The label of the undo/redo element. - */ +export interface IResourceUndoRedoElement { + readonly type: UndoRedoElementType.Resource; + readonly resource: URI; readonly label: string; + undo(): Promise | void; + redo(): Promise | void; +} - /** - * Undo. - * Will always be called before `redo`. - * Can be called multiple times. - * e.g. `undo` -> `redo` -> `undo` -> `redo` - */ - undo(ctx: IUndoRedoContext): void; - - /** - * Redo. - * Will always be called after `undo`. - * Can be called multiple times. - * e.g. `undo` -> `redo` -> `undo` -> `redo` - */ - redo(ctx: IUndoRedoContext): void; - - /** - * Invalidate the edits concerning `resource`. - * i.e. the undo/redo stack for that particular resource has been destroyed. - */ - invalidate(resource: URI): void; +export interface IWorkspaceUndoRedoElement { + readonly type: UndoRedoElementType.Workspace; + readonly resources: readonly URI[]; + readonly label: string; + undo(): Promise | void; + redo(): Promise | void; + split(): IResourceUndoRedoElement[]; } export interface IUndoRedoService { @@ -53,12 +37,12 @@ export interface IUndoRedoService { * Add a new element to the `undo` stack. * This will destroy the `redo` stack. */ - pushElement(element: IUndoRedoElement): void; + pushElement(element: IResourceUndoRedoElement | IWorkspaceUndoRedoElement): void; /** * Get the last pushed element. If the last pushed element has been undone, returns null. */ - getLastElement(resource: URI): IUndoRedoElement | null; + getLastElement(resource: URI): IResourceUndoRedoElement | IWorkspaceUndoRedoElement | null; /** * Remove elements that target `resource`. @@ -66,8 +50,8 @@ export interface IUndoRedoService { removeElements(resource: URI): void; canUndo(resource: URI): boolean; - undo(resource: URI): void; + undo(resource: URI): Promise | void; - redo(resource: URI): void; canRedo(resource: URI): boolean; + redo(resource: URI): Promise | void; } diff --git a/src/vs/platform/undoRedo/common/undoRedoService.ts b/src/vs/platform/undoRedo/common/undoRedoService.ts index 78f42f43b9d..8b68917e466 100644 --- a/src/vs/platform/undoRedo/common/undoRedoService.ts +++ b/src/vs/platform/undoRedo/common/undoRedoService.ts @@ -3,38 +3,52 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IUndoRedoService, IUndoRedoElement } from 'vs/platform/undoRedo/common/undoRedo'; +import * as nls from 'vs/nls'; +import { IUndoRedoService, IResourceUndoRedoElement, IWorkspaceUndoRedoElement, UndoRedoElementType } from 'vs/platform/undoRedo/common/undoRedo'; import { URI } from 'vs/base/common/uri'; import { getComparisonKey as uriGetComparisonKey } from 'vs/base/common/resources'; import { onUnexpectedError } from 'vs/base/common/errors'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import Severity from 'vs/base/common/severity'; +import { Schemas } from 'vs/base/common/network'; +import { INotificationService } from 'vs/platform/notification/common/notification'; -class StackElement { - public readonly actual: IUndoRedoElement; +class ResourceStackElement { + public readonly type = UndoRedoElementType.Resource; + public readonly actual: IResourceUndoRedoElement; public readonly label: string; - public resources: URI[]; - public strResources: string[]; + public readonly resource: URI; + public readonly strResource: string; + public readonly resources: URI[]; + public readonly strResources: string[]; - constructor(actual: IUndoRedoElement) { + constructor(actual: IResourceUndoRedoElement) { + this.actual = actual; + this.label = actual.label; + this.resource = actual.resource; + this.strResource = uriGetComparisonKey(this.resource); + this.resources = [this.resource]; + this.strResources = [this.strResource]; + } +} +class WorkspaceStackElement { + public readonly type = UndoRedoElementType.Workspace; + public readonly actual: IWorkspaceUndoRedoElement; + public readonly label: string; + + public readonly resources: URI[]; + public readonly strResources: string[]; + + constructor(actual: IWorkspaceUndoRedoElement) { this.actual = actual; this.label = actual.label; this.resources = actual.resources.slice(0); this.strResources = this.resources.map(resource => uriGetComparisonKey(resource)); } - - public invalidate(resource: URI): void { - const strResource = uriGetComparisonKey(resource); - for (let i = 0, len = this.strResources.length; i < len; i++) { - if (this.strResources[i] === strResource) { - this.resources.splice(i, 1); - this.strResources.splice(i, 1); - break; - } - } - this.actual.invalidate(resource); - } } +type StackElement = ResourceStackElement | WorkspaceStackElement; class ResourceEditStack { public resource: URI; @@ -53,12 +67,15 @@ export class UndoRedoService implements IUndoRedoService { private readonly _editStacks: Map; - constructor() { + constructor( + @IDialogService private readonly _dialogService: IDialogService, + @INotificationService private readonly _notificationService: INotificationService, + ) { this._editStacks = new Map(); } - public pushElement(_element: IUndoRedoElement): void { - const element = new StackElement(_element); + public pushElement(_element: IResourceUndoRedoElement | IWorkspaceUndoRedoElement): void { + const element: StackElement = (_element.type === UndoRedoElementType.Resource ? new ResourceStackElement(_element) : new WorkspaceStackElement(_element)); for (let i = 0, len = element.resources.length; i < len; i++) { const resource = element.resources[i]; const strResource = element.strResources[i]; @@ -73,14 +90,16 @@ export class UndoRedoService implements IUndoRedoService { // remove the future for (const futureElement of editStack.future) { - futureElement.invalidate(resource); + if (futureElement.type === UndoRedoElementType.Workspace) { + this._splitFutureWorkspaceElement(futureElement, strResource); + } } editStack.future = []; editStack.past.push(element); } } - public getLastElement(resource: URI): IUndoRedoElement | null { + public getLastElement(resource: URI): IResourceUndoRedoElement | IWorkspaceUndoRedoElement | null { const strResource = uriGetComparisonKey(resource); if (this._editStacks.has(strResource)) { const editStack = this._editStacks.get(strResource)!; @@ -95,15 +114,75 @@ export class UndoRedoService implements IUndoRedoService { return null; } + private _splitPastWorkspaceElement(toRemove: WorkspaceStackElement, ignoreStrResource: string | null): void { + const individualArr = toRemove.actual.split(); + const individualMap = new Map(); + for (const _element of individualArr) { + const element = new ResourceStackElement(_element); + individualMap.set(element.strResource, element); + } + + for (const strResource of toRemove.strResources) { + if (strResource === ignoreStrResource) { + continue; + } + const editStack = this._editStacks.get(strResource)!; + for (let j = editStack.past.length - 1; j >= 0; j--) { + if (editStack.past[j] === toRemove) { + if (individualMap.has(strResource)) { + // gets replaced + editStack.past[j] = individualMap.get(strResource)!; + } else { + // gets deleted + editStack.past.splice(j, 1); + } + break; + } + } + } + } + + private _splitFutureWorkspaceElement(toRemove: WorkspaceStackElement, ignoreStrResource: string | null): void { + const individualArr = toRemove.actual.split(); + const individualMap = new Map(); + for (const _element of individualArr) { + const element = new ResourceStackElement(_element); + individualMap.set(element.strResource, element); + } + + for (const strResource of toRemove.strResources) { + if (strResource === ignoreStrResource) { + continue; + } + const editStack = this._editStacks.get(strResource)!; + for (let j = editStack.future.length - 1; j >= 0; j--) { + if (editStack.future[j] === toRemove) { + if (individualMap.has(strResource)) { + // gets replaced + editStack.future[j] = individualMap.get(strResource)!; + } else { + // gets deleted + editStack.future.splice(j, 1); + } + break; + } + } + } + } + public removeElements(resource: URI): void { const strResource = uriGetComparisonKey(resource); if (this._editStacks.has(strResource)) { const editStack = this._editStacks.get(strResource)!; for (const pastElement of editStack.past) { - pastElement.invalidate(resource); + if (pastElement.type === UndoRedoElementType.Workspace) { + this._splitPastWorkspaceElement(pastElement, strResource); + } } for (const futureElement of editStack.future) { - futureElement.invalidate(resource); + if (futureElement.type === UndoRedoElementType.Workspace) { + this._splitFutureWorkspaceElement(futureElement, strResource); + } } this._editStacks.delete(strResource); } @@ -118,7 +197,78 @@ export class UndoRedoService implements IUndoRedoService { return false; } - public undo(resource: URI): void { + private _onError(err: Error, element: StackElement): void { + onUnexpectedError(err); + // An error occured while undoing or redoing => drop the undo/redo stack for all affected resources + for (const resource of element.resources) { + this.removeElements(resource); + } + this._notificationService.error(err); + } + + private _safeInvoke(element: StackElement, invoke: () => Promise | void): Promise | void { + let result: Promise | void; + try { + result = invoke(); + } catch (err) { + return this._onError(err, element); + } + + if (result) { + return result.then(undefined, (err) => this._onError(err, element)); + } + } + + private _workspaceUndo(resource: URI, element: WorkspaceStackElement): Promise | void { + // this must be the last past element in all the impacted resources! + let affectedEditStacks: ResourceEditStack[] = []; + for (const strResource of element.strResources) { + affectedEditStacks.push(this._editStacks.get(strResource)!); + } + + let cannotUndoDueToResources: URI[] = []; + for (const editStack of affectedEditStacks) { + if (editStack.past.length === 0 || editStack.past[editStack.past.length - 1] !== element) { + cannotUndoDueToResources.push(editStack.resource); + } + } + + if (cannotUndoDueToResources.length > 0) { + this._splitPastWorkspaceElement(element, null); + const paths = cannotUndoDueToResources.map(r => r.scheme === Schemas.file ? r.fsPath : r.path); + const message = nls.localize('undoFurtherChanges', "Could not undo '{0}' across all files because changes were made to {1}", element.label, paths.join(', ')); + this._notificationService.info(message); + return; + } + + return this._dialogService.show( + Severity.Info, + nls.localize('confirmWorkspace', "Would you like to undo '{0}' across all files?", element.label), + [ + nls.localize('ok', "Yes, change {0} files.", affectedEditStacks.length), + nls.localize('nok', "No, change only this file.") + ] + ).then((result) => { + if (result.choice === 0) { + for (const editStack of affectedEditStacks) { + editStack.past.pop(); + editStack.future.push(element); + } + return this._safeInvoke(element, () => element.actual.undo()); + } else { + this._splitPastWorkspaceElement(element, null); + return this.undo(resource); + } + }); + } + + private _resourceUndo(editStack: ResourceEditStack, element: ResourceStackElement): Promise | void { + editStack.past.pop(); + editStack.future.push(element); + return this._safeInvoke(element, () => element.actual.undo()); + } + + public undo(resource: URI): Promise | void { const strResource = uriGetComparisonKey(resource); if (!this._editStacks.has(strResource)) { return; @@ -130,59 +280,10 @@ export class UndoRedoService implements IUndoRedoService { } const element = editStack.past[editStack.past.length - 1]; - - let replaceCurrentElement: IUndoRedoElement[] | null = null as IUndoRedoElement[] | null; - try { - element.actual.undo({ - replaceCurrentElement: (others: IUndoRedoElement[]): void => { - replaceCurrentElement = others; - } - }); - } catch (e) { - onUnexpectedError(e); - editStack.past.pop(); - editStack.future.push(element); - return; - } - - if (replaceCurrentElement === null) { - // regular case - editStack.past.pop(); - editStack.future.push(element); - return; - } - - const replaceCurrentElementMap = new Map(); - let foundReplacementForThisResource = false; - for (const _replace of replaceCurrentElement) { - const replace = new StackElement(_replace); - for (const strReplaceResource of replace.strResources) { - replaceCurrentElementMap.set(strReplaceResource, replace); - if (strReplaceResource === strResource) { - foundReplacementForThisResource = true; - } - } - } - - for (let i = 0, len = element.strResources.length; i < len; i++) { - const strResource = element.strResources[i]; - if (this._editStacks.has(strResource)) { - const editStack = this._editStacks.get(strResource)!; - for (let j = editStack.past.length - 1; j >= 0; j--) { - if (editStack.past[j] === element) { - if (replaceCurrentElementMap.has(strResource)) { - editStack.past[j] = replaceCurrentElementMap.get(strResource)!; - } else { - editStack.past.splice(j, 1); - } - break; - } - } - } - } - - if (foundReplacementForThisResource) { - this.undo(resource); + if (element.type === UndoRedoElementType.Workspace) { + return this._workspaceUndo(resource, element); + } else { + return this._resourceUndo(editStack, element); } } @@ -195,7 +296,42 @@ export class UndoRedoService implements IUndoRedoService { return false; } - public redo(resource: URI): void { + private _workspaceRedo(resource: URI, element: WorkspaceStackElement): Promise | void { + // this must be the last future element in all the impacted resources! + let affectedEditStacks: ResourceEditStack[] = []; + for (const strResource of element.strResources) { + affectedEditStacks.push(this._editStacks.get(strResource)!); + } + + let cannotRedoDueToResources: URI[] = []; + for (const editStack of affectedEditStacks) { + if (editStack.future.length === 0 || editStack.future[editStack.future.length - 1] !== element) { + cannotRedoDueToResources.push(editStack.resource); + } + } + + if (cannotRedoDueToResources.length > 0) { + this._splitFutureWorkspaceElement(element, null); + const paths = cannotRedoDueToResources.map(r => r.scheme === Schemas.file ? r.fsPath : r.path); + const message = nls.localize('redoFurtherChanges', "Could not redo '{0}' across all files because changes were made to {1}", element.label, paths.join(', ')); + this._notificationService.info(message); + return; + } + + for (const editStack of affectedEditStacks) { + editStack.future.pop(); + editStack.past.push(element); + } + return this._safeInvoke(element, () => element.actual.redo()); + } + + private _resourceRedo(editStack: ResourceEditStack, element: ResourceStackElement): Promise | void { + editStack.future.pop(); + editStack.past.push(element); + return this._safeInvoke(element, () => element.actual.redo()); + } + + public redo(resource: URI): Promise | void { const strResource = uriGetComparisonKey(resource); if (!this._editStacks.has(strResource)) { return; @@ -207,59 +343,10 @@ export class UndoRedoService implements IUndoRedoService { } const element = editStack.future[editStack.future.length - 1]; - - let replaceCurrentElement: IUndoRedoElement[] | null = null as IUndoRedoElement[] | null; - try { - element.actual.redo({ - replaceCurrentElement: (others: IUndoRedoElement[]): void => { - replaceCurrentElement = others; - } - }); - } catch (e) { - onUnexpectedError(e); - editStack.future.pop(); - editStack.past.push(element); - return; - } - - if (replaceCurrentElement === null) { - // regular case - editStack.future.pop(); - editStack.past.push(element); - return; - } - - let foundReplacementForThisResource = false; - const replaceCurrentElementMap = new Map(); - for (const _replace of replaceCurrentElement) { - const replace = new StackElement(_replace); - for (const strReplaceResource of replace.strResources) { - replaceCurrentElementMap.set(strReplaceResource, replace); - if (strReplaceResource === strResource) { - foundReplacementForThisResource = true; - } - } - } - - for (let i = 0, len = element.strResources.length; i < len; i++) { - const strResource = element.strResources[i]; - if (this._editStacks.has(strResource)) { - const editStack = this._editStacks.get(strResource)!; - for (let j = editStack.future.length - 1; j >= 0; j--) { - if (editStack.future[j] === element) { - if (replaceCurrentElementMap.has(strResource)) { - editStack.future[j] = replaceCurrentElementMap.get(strResource)!; - } else { - editStack.future.splice(j, 1); - } - break; - } - } - } - } - - if (foundReplacementForThisResource) { - this.redo(resource); + if (element.type === UndoRedoElementType.Workspace) { + return this._workspaceRedo(resource, element); + } else { + return this._resourceRedo(editStack, element); } } } diff --git a/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts b/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts index 9bb5819cc21..b7f29e36862 100644 --- a/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts +++ b/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts @@ -29,9 +29,6 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService'; import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; import { EditStackElement, MultiModelEditStackElement } from 'vs/editor/common/model/editStack'; -import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; - -const USE_MULTI_MODEL_EDIT_STACK_ELEMENT = false; type ValidationResult = { canApply: true } | { canApply: false, reason: URI }; @@ -138,8 +135,7 @@ class BulkEditModel implements IDisposable { edits: WorkspaceTextEdit[], @IEditorWorkerService private readonly _editorWorker: IEditorWorkerService, @ITextModelService private readonly _textModelResolverService: ITextModelService, - @IUndoRedoService private readonly _undoRedoService: IUndoRedoService, - @IDialogService private readonly _dialogService: IDialogService + @IUndoRedoService private readonly _undoRedoService: IUndoRedoService ) { edits.forEach(this._addEdit, this); } @@ -224,7 +220,7 @@ class BulkEditModel implements IDisposable { apply(): void { const tasks = this._tasks!; - if (tasks.length === 1 || !USE_MULTI_MODEL_EDIT_STACK_ELEMENT) { + if (tasks.length === 1) { // This edit touches a single model => keep things simple for (const task of tasks) { task.model.pushStackElement(); @@ -237,8 +233,7 @@ class BulkEditModel implements IDisposable { const multiModelEditStackElement = new MultiModelEditStackElement( localize('workspaceEdit', "Workspace Edit"), - tasks.map(t => new EditStackElement(t.model, t.getBeforeCursorState())), - this._dialogService + tasks.map(t => new EditStackElement(t.model, t.getBeforeCursorState())) ); this._undoRedoService.pushElement(multiModelEditStackElement); diff --git a/src/vs/workbench/test/browser/api/mainThreadDocumentsAndEditors.test.ts b/src/vs/workbench/test/browser/api/mainThreadDocumentsAndEditors.test.ts index 24f0c27176f..4101a887f69 100644 --- a/src/vs/workbench/test/browser/api/mainThreadDocumentsAndEditors.test.ts +++ b/src/vs/workbench/test/browser/api/mainThreadDocumentsAndEditors.test.ts @@ -23,6 +23,8 @@ import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; import { NullLogService } from 'vs/platform/log/common/log'; import { UndoRedoService } from 'vs/platform/undoRedo/common/undoRedoService'; +import { TestDialogService } from 'vs/platform/dialogs/test/common/testDialogService'; +import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService'; suite('MainThreadDocumentsAndEditors', () => { @@ -45,7 +47,10 @@ suite('MainThreadDocumentsAndEditors', () => { deltas.length = 0; const configService = new TestConfigurationService(); configService.setUserConfiguration('editor', { 'detectIndentation': false }); - modelService = new ModelServiceImpl(configService, new TestTextResourcePropertiesService(configService), new TestThemeService(), new NullLogService(), new UndoRedoService()); + const dialogService = new TestDialogService(); + const notificationService = new TestNotificationService(); + const undoRedoService = new UndoRedoService(dialogService, notificationService); + modelService = new ModelServiceImpl(configService, new TestTextResourcePropertiesService(configService), new TestThemeService(), new NullLogService(), undoRedoService); codeEditorService = new TestCodeEditorService(); textFileService = new class extends mock() { isDirty() { return false; } diff --git a/src/vs/workbench/test/browser/api/mainThreadEditors.test.ts b/src/vs/workbench/test/browser/api/mainThreadEditors.test.ts index ef650b280c6..0d96189e4c9 100644 --- a/src/vs/workbench/test/browser/api/mainThreadEditors.test.ts +++ b/src/vs/workbench/test/browser/api/mainThreadEditors.test.ts @@ -42,6 +42,11 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ import { ILabelService } from 'vs/platform/label/common/label'; import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService'; import { UndoRedoService } from 'vs/platform/undoRedo/common/undoRedoService'; +import { TestDialogService } from 'vs/platform/dialogs/test/common/testDialogService'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; +import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService'; +import { INotificationService } from 'vs/platform/notification/common/notification'; suite('MainThreadEditors', () => { @@ -64,7 +69,10 @@ suite('MainThreadEditors', () => { const configService = new TestConfigurationService(); - modelService = new ModelServiceImpl(configService, new TestTextResourcePropertiesService(configService), new TestThemeService(), new NullLogService(), new UndoRedoService()); + const dialogService = new TestDialogService(); + const notificationService = new TestNotificationService(); + const undoRedoService = new UndoRedoService(dialogService, notificationService); + modelService = new ModelServiceImpl(configService, new TestTextResourcePropertiesService(configService), new TestThemeService(), new NullLogService(), undoRedoService); const services = new ServiceCollection(); @@ -74,6 +82,9 @@ suite('MainThreadEditors', () => { services.set(IWorkspaceContextService, new TestContextService()); services.set(IWorkbenchEnvironmentService, TestEnvironmentService); services.set(IConfigurationService, configService); + services.set(IDialogService, dialogService); + services.set(INotificationService, notificationService); + services.set(IUndoRedoService, undoRedoService); services.set(IModelService, modelService); services.set(ICodeEditorService, new TestCodeEditorService()); services.set(IFileService, new TestFileService()); diff --git a/src/vs/workbench/test/browser/parts/editor/editorModel.test.ts b/src/vs/workbench/test/browser/parts/editor/editorModel.test.ts index 849a327b502..d0c2abb5eea 100644 --- a/src/vs/workbench/test/browser/parts/editor/editorModel.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/editorModel.test.ts @@ -20,6 +20,10 @@ import { ITextResourcePropertiesService } from 'vs/editor/common/services/textRe import { TestTextResourcePropertiesService } from 'vs/workbench/test/browser/workbenchTestServices'; import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; import { UndoRedoService } from 'vs/platform/undoRedo/common/undoRedoService'; +import { TestDialogService } from 'vs/platform/dialogs/test/common/testDialogService'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService'; +import { INotificationService } from 'vs/platform/notification/common/notification'; class MyEditorModel extends EditorModel { } class MyTextEditorModel extends BaseTextEditorModel { @@ -72,9 +76,14 @@ suite('Workbench editor model', () => { }); function stubModelService(instantiationService: TestInstantiationService): IModelService { + const dialogService = new TestDialogService(); + const notificationService = new TestNotificationService(); + const undoRedoService = new UndoRedoService(dialogService, notificationService); instantiationService.stub(IConfigurationService, new TestConfigurationService()); instantiationService.stub(ITextResourcePropertiesService, new TestTextResourcePropertiesService(instantiationService.get(IConfigurationService))); - instantiationService.stub(IUndoRedoService, new UndoRedoService()); + instantiationService.stub(IDialogService, dialogService); + instantiationService.stub(INotificationService, notificationService); + instantiationService.stub(IUndoRedoService, undoRedoService); return instantiationService.createInstance(ModelServiceImpl); } }); diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index 14f31ca5e1a..7a7f9f4ee7e 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -13,7 +13,6 @@ import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtil import { IEditorInputWithOptions, CloseDirection, IEditorIdentifier, IUntitledTextResourceInput, IResourceDiffInput, IResourceSideBySideInput, IEditorInput, IEditor, IEditorCloseEvent, IEditorPartOptions, IRevertOptions, GroupIdentifier, EditorInput, EditorOptions, EditorsOrder, IFileEditorInput, IEditorInputFactoryRegistry, IEditorInputFactory, Extensions as EditorExtensions, ISaveOptions, IMoveResult } from 'vs/workbench/common/editor'; import { IEditorOpeningEvent, EditorServiceImpl, IEditorGroupView, IEditorGroupsAccessor } from 'vs/workbench/browser/parts/editor/editor'; import { Event, Emitter } from 'vs/base/common/event'; -import Severity from 'vs/base/common/severity'; import { IBackupFileService, IResolvedBackup } from 'vs/workbench/services/backup/common/backup'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { IWorkbenchLayoutService, Parts, Position as PartPosition } from 'vs/workbench/services/layout/browser/layoutService'; @@ -45,7 +44,7 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { MockContextKeyService, MockKeybindingService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; import { ITextBufferFactory, DefaultEndOfLine, EndOfLinePreference, IModelDecorationOptions, ITextModel, ITextSnapshot } from 'vs/editor/common/model'; import { Range } from 'vs/editor/common/core/range'; -import { IConfirmation, IConfirmationResult, IDialogService, IDialogOptions, IPickAndOpenOptions, ISaveDialogOptions, IOpenDialogOptions, IFileDialogService, IShowResult, ConfirmResult } from 'vs/platform/dialogs/common/dialogs'; +import { IDialogService, IPickAndOpenOptions, ISaveDialogOptions, IOpenDialogOptions, IFileDialogService, ConfirmResult } from 'vs/platform/dialogs/common/dialogs'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService'; import { IExtensionService, NullExtensionService } from 'vs/workbench/services/extensions/common/extensions'; @@ -99,6 +98,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; import { CancellationToken } from 'vs/base/common/cancellation'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import { TestDialogService } from 'vs/platform/dialogs/test/common/testDialogService'; export import TestTextResourcePropertiesService = CommonWorkbenchTestServices.TestTextResourcePropertiesService; export import TestContextService = CommonWorkbenchTestServices.TestContextService; @@ -322,14 +322,7 @@ export class TestHistoryService implements IHistoryService { openLastEditLocation(): void { } } -export class TestDialogService implements IDialogService { - _serviceBrand: undefined; - - confirm(_confirmation: IConfirmation): Promise { return Promise.resolve({ confirmed: false }); } - show(_severity: Severity, _message: string, _buttons: string[], _options?: IDialogOptions): Promise { return Promise.resolve({ choice: 0 }); } - about(): Promise { return Promise.resolve(); } -} export class TestFileDialogService implements IFileDialogService { diff --git a/src/vs/workbench/test/electron-browser/quickopen.perf.integrationTest.ts b/src/vs/workbench/test/electron-browser/quickopen.perf.integrationTest.ts index 2c5b03c33ed..4af6351e22c 100644 --- a/src/vs/workbench/test/electron-browser/quickopen.perf.integrationTest.ts +++ b/src/vs/workbench/test/electron-browser/quickopen.perf.integrationTest.ts @@ -34,6 +34,11 @@ import { ClassifiedEvent, StrictPropertyCheck, GDPRClassification } from 'vs/pla import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; import { NullLogService } from 'vs/platform/log/common/log'; import { UndoRedoService } from 'vs/platform/undoRedo/common/undoRedoService'; +import { TestDialogService } from 'vs/platform/dialogs/test/common/testDialogService'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; +import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService'; +import { INotificationService } from 'vs/platform/notification/common/notification'; namespace Timer { export interface ITimerEvent { @@ -73,11 +78,17 @@ suite.skip('QuickOpen performance (integration)', () => { const telemetryService = new TestTelemetryService(); const configurationService = new TestConfigurationService(); const textResourcePropertiesService = new TestTextResourcePropertiesService(configurationService); + const dialogService = new TestDialogService(); + const notificationService = new TestNotificationService(); + const undoRedoService = new UndoRedoService(dialogService, notificationService); const instantiationService = new InstantiationService(new ServiceCollection( [ITelemetryService, telemetryService], [IConfigurationService, configurationService], [ITextResourcePropertiesService, textResourcePropertiesService], - [IModelService, new ModelServiceImpl(configurationService, textResourcePropertiesService, new TestThemeService(), new NullLogService(), new UndoRedoService())], + [IDialogService, dialogService], + [INotificationService, notificationService], + [IUndoRedoService, undoRedoService], + [IModelService, new ModelServiceImpl(configurationService, textResourcePropertiesService, new TestThemeService(), new NullLogService(), undoRedoService)], [IWorkspaceContextService, new TestContextService(testWorkspace(URI.file(testWorkspacePath)))], [IEditorService, new TestEditorService()], [IEditorGroupsService, new TestEditorGroupsService()], diff --git a/src/vs/workbench/test/electron-browser/textsearch.perf.integrationTest.ts b/src/vs/workbench/test/electron-browser/textsearch.perf.integrationTest.ts index 4ccd31573b5..be6ef9e5492 100644 --- a/src/vs/workbench/test/electron-browser/textsearch.perf.integrationTest.ts +++ b/src/vs/workbench/test/electron-browser/textsearch.perf.integrationTest.ts @@ -37,6 +37,11 @@ import { ITextResourcePropertiesService } from 'vs/editor/common/services/textRe import { ClassifiedEvent, StrictPropertyCheck, GDPRClassification } from 'vs/platform/telemetry/common/gdprTypings'; import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; import { UndoRedoService } from 'vs/platform/undoRedo/common/undoRedoService'; +import { TestDialogService } from 'vs/platform/dialogs/test/common/testDialogService'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; +import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService'; +import { INotificationService } from 'vs/platform/notification/common/notification'; // declare var __dirname: string; @@ -63,11 +68,17 @@ suite.skip('TextSearch performance (integration)', () => { const configurationService = new TestConfigurationService(); const textResourcePropertiesService = new TestTextResourcePropertiesService(configurationService); const logService = new NullLogService(); + const dialogService = new TestDialogService(); + const notificationService = new TestNotificationService(); + const undoRedoService = new UndoRedoService(dialogService, notificationService); const instantiationService = new InstantiationService(new ServiceCollection( [ITelemetryService, telemetryService], [IConfigurationService, configurationService], [ITextResourcePropertiesService, textResourcePropertiesService], - [IModelService, new ModelServiceImpl(configurationService, textResourcePropertiesService, new TestThemeService(), logService, new UndoRedoService())], + [IDialogService, dialogService], + [INotificationService, notificationService], + [IUndoRedoService, undoRedoService], + [IModelService, new ModelServiceImpl(configurationService, textResourcePropertiesService, new TestThemeService(), logService, undoRedoService)], [IWorkspaceContextService, new TestContextService(testWorkspace(URI.file(testWorkspacePath)))], [IEditorService, new TestEditorService()], [IEditorGroupsService, new TestEditorGroupsService()], From 147f417a9ed408e444ed79694f8a7cb558ab70d5 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 21 Feb 2020 22:12:22 +0100 Subject: [PATCH 044/200] Avoid using TextModel.createFromString --- .../test/bracketMatching.test.ts | 14 +++--- .../codeAction/test/codeAction.test.ts | 3 +- .../codeAction/test/codeActionModel.test.ts | 3 +- .../documentSymbols/test/outlineModel.test.ts | 6 +-- .../contrib/folding/test/foldingModel.test.ts | 29 ++++++------ .../folding/test/foldingRanges.test.ts | 8 ++-- .../folding/test/hiddenRangeModel.test.ts | 4 +- .../contrib/folding/test/indentFold.test.ts | 4 +- .../folding/test/indentRangeProvider.test.ts | 4 +- .../contrib/folding/test/syntaxFold.test.ts | 4 +- .../test/parameterHintsModel.test.ts | 4 +- .../snippet/test/snippetController2.test.ts | 3 +- .../snippet/test/snippetSession.test.ts | 3 +- .../snippet/test/snippetVariables.test.ts | 15 ++++--- .../contrib/suggest/test/suggest.test.ts | 3 +- .../suggest/test/suggestController.test.ts | 3 +- .../suggest/test/suggestMemory.test.ts | 4 +- .../contrib/suggest/test/suggestModel.test.ts | 7 +-- .../contrib/suggest/test/wordDistance.test.ts | 4 +- .../test/browser/commands/sideEditing.test.ts | 4 +- .../controller/cursorMoveCommand.test.ts | 3 +- .../browser/controller/textAreaState.test.ts | 4 +- src/vs/editor/test/browser/testCodeEditor.ts | 6 +-- src/vs/editor/test/browser/testCommand.ts | 4 +- src/vs/editor/test/common/editorTestUtils.ts | 2 +- .../common/model/editableTextModel.test.ts | 3 +- .../model/editableTextModelTestUtils.ts | 3 +- .../test/common/model/model.line.test.ts | 5 ++- .../test/common/model/model.modes.test.ts | 5 ++- src/vs/editor/test/common/model/model.test.ts | 13 +++--- .../common/model/modelDecorations.test.ts | 19 ++++---- .../common/model/modelEditOperation.test.ts | 3 +- .../pieceTreeTextBuffer.test.ts | 12 ++--- .../test/common/model/textModel.test.ts | 44 +++++++++---------- .../test/common/model/textModelSearch.test.ts | 33 +++++++------- .../common/model/textModelWithTokens.test.ts | 25 ++++++----- .../test/common/model/tokensStore.test.ts | 3 +- .../test/common/services/modelService.test.ts | 15 ++++--- .../viewModel/splitLinesCollection.test.ts | 5 ++- .../test/common/viewModel/testViewModel.ts | 3 +- .../debug/test/browser/breakpoints.test.ts | 3 +- .../test/common/smartSnippetInserter.test.ts | 4 +- .../test/browser/snippetsService.test.ts | 42 +++++++++--------- .../backupFileService.test.ts | 11 ++--- .../textFileService.io.test.ts | 22 +++++----- .../browser/api/extHostApiCommands.test.ts | 4 +- .../api/extHostLanguageFeatures.test.ts | 4 +- ...mainThreadDocumentContentProviders.test.ts | 4 +- .../browser/api/mainThreadDocuments.test.ts | 10 ++--- .../parts/editor/rangeDecorations.test.ts | 3 +- 50 files changed, 236 insertions(+), 210 deletions(-) diff --git a/src/vs/editor/contrib/bracketMatching/test/bracketMatching.test.ts b/src/vs/editor/contrib/bracketMatching/test/bracketMatching.test.ts index 87886115932..486693706a5 100644 --- a/src/vs/editor/contrib/bracketMatching/test/bracketMatching.test.ts +++ b/src/vs/editor/contrib/bracketMatching/test/bracketMatching.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import { Position } from 'vs/editor/common/core/position'; import { Selection } from 'vs/editor/common/core/selection'; -import { TextModel } from 'vs/editor/common/model/textModel'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; import { LanguageIdentifier } from 'vs/editor/common/modes'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { BracketMatchingController } from 'vs/editor/contrib/bracketMatching/bracketMatching'; @@ -31,7 +31,7 @@ suite('bracket matching', () => { test('issue #183: jump to matching bracket position', () => { let mode = new BracketMode(); - let model = TextModel.createFromString('var x = (3 + (5-7)) + ((5+3)+5);', undefined, mode.getLanguageIdentifier()); + let model = createTextModel('var x = (3 + (5-7)) + ((5+3)+5);', undefined, mode.getLanguageIdentifier()); withTestCodeEditor(null, { model: model }, (editor, cursor) => { let bracketMatchingController = editor.registerAndInstantiateContribution(BracketMatchingController.ID, BracketMatchingController); @@ -63,7 +63,7 @@ suite('bracket matching', () => { test('Jump to next bracket', () => { let mode = new BracketMode(); - let model = TextModel.createFromString('var x = (3 + (5-7)); y();', undefined, mode.getLanguageIdentifier()); + let model = createTextModel('var x = (3 + (5-7)); y();', undefined, mode.getLanguageIdentifier()); withTestCodeEditor(null, { model: model }, (editor, cursor) => { let bracketMatchingController = editor.registerAndInstantiateContribution(BracketMatchingController.ID, BracketMatchingController); @@ -100,7 +100,7 @@ suite('bracket matching', () => { test('Select to next bracket', () => { let mode = new BracketMode(); - let model = TextModel.createFromString('var x = (3 + (5-7)); y();', undefined, mode.getLanguageIdentifier()); + let model = createTextModel('var x = (3 + (5-7)); y();', undefined, mode.getLanguageIdentifier()); withTestCodeEditor(null, { model: model }, (editor, cursor) => { let bracketMatchingController = editor.registerAndInstantiateContribution(BracketMatchingController.ID, BracketMatchingController); @@ -152,7 +152,7 @@ suite('bracket matching', () => { '};', ].join('\n'); const mode = new BracketMode(); - const model = TextModel.createFromString(text, undefined, mode.getLanguageIdentifier()); + const model = createTextModel(text, undefined, mode.getLanguageIdentifier()); withTestCodeEditor(null, { model: model }, (editor, cursor) => { const bracketMatchingController = editor.registerAndInstantiateContribution(BracketMatchingController.ID, BracketMatchingController); @@ -177,7 +177,7 @@ suite('bracket matching', () => { '};', ].join('\n'); const mode = new BracketMode(); - const model = TextModel.createFromString(text, undefined, mode.getLanguageIdentifier()); + const model = createTextModel(text, undefined, mode.getLanguageIdentifier()); withTestCodeEditor(null, { model: model }, (editor, cursor) => { const bracketMatchingController = editor.registerAndInstantiateContribution(BracketMatchingController.ID, BracketMatchingController); @@ -195,7 +195,7 @@ suite('bracket matching', () => { test('issue #45369: Select to Bracket with multicursor', () => { let mode = new BracketMode(); - let model = TextModel.createFromString('{ } { } { }', undefined, mode.getLanguageIdentifier()); + let model = createTextModel('{ } { } { }', undefined, mode.getLanguageIdentifier()); withTestCodeEditor(null, { model: model }, (editor, cursor) => { let bracketMatchingController = editor.registerAndInstantiateContribution(BracketMatchingController.ID, BracketMatchingController); diff --git a/src/vs/editor/contrib/codeAction/test/codeAction.test.ts b/src/vs/editor/contrib/codeAction/test/codeAction.test.ts index ad59e4ff308..85510ab2a01 100644 --- a/src/vs/editor/contrib/codeAction/test/codeAction.test.ts +++ b/src/vs/editor/contrib/codeAction/test/codeAction.test.ts @@ -13,6 +13,7 @@ import { CodeActionKind } from 'vs/editor/contrib/codeAction/types'; import { IMarkerData, MarkerSeverity } from 'vs/platform/markers/common/markers'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Progress } from 'vs/platform/progress/common/progress'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; function staticCodeActionProvider(...actions: modes.CodeAction[]): modes.CodeActionProvider { return new class implements modes.CodeActionProvider { @@ -93,7 +94,7 @@ suite('CodeAction', () => { setup(function () { disposables.clear(); - model = TextModel.createFromString('test1\ntest2\ntest3', undefined, langId, uri); + model = createTextModel('test1\ntest2\ntest3', undefined, langId, uri); disposables.add(model); }); diff --git a/src/vs/editor/contrib/codeAction/test/codeActionModel.test.ts b/src/vs/editor/contrib/codeAction/test/codeActionModel.test.ts index c605ed49ab6..5edcc7ae851 100644 --- a/src/vs/editor/contrib/codeAction/test/codeActionModel.test.ts +++ b/src/vs/editor/contrib/codeAction/test/codeActionModel.test.ts @@ -15,6 +15,7 @@ import { CodeActionModel, CodeActionsState } from 'vs/editor/contrib/codeAction/ import { createTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; import { MarkerService } from 'vs/platform/markers/common/markerService'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; const testProvider = { provideCodeActions(): modes.CodeActionList { @@ -38,7 +39,7 @@ suite('CodeActionModel', () => { setup(() => { disposables.clear(); markerService = new MarkerService(); - model = TextModel.createFromString('foobar foo bar\nfarboo far boo', undefined, languageIdentifier, uri); + model = createTextModel('foobar foo bar\nfarboo far boo', undefined, languageIdentifier, uri); editor = createTestCodeEditor({ model: model }); editor.setPosition({ lineNumber: 1, column: 1 }); }); diff --git a/src/vs/editor/contrib/documentSymbols/test/outlineModel.test.ts b/src/vs/editor/contrib/documentSymbols/test/outlineModel.test.ts index b41a7f8dd8f..64b8efa88a8 100644 --- a/src/vs/editor/contrib/documentSymbols/test/outlineModel.test.ts +++ b/src/vs/editor/contrib/documentSymbols/test/outlineModel.test.ts @@ -8,7 +8,7 @@ import { OutlineElement, OutlineGroup, OutlineModel } from '../outlineModel'; import { SymbolKind, DocumentSymbol, DocumentSymbolProviderRegistry } from 'vs/editor/common/modes'; import { Range } from 'vs/editor/common/core/range'; import { IMarker, MarkerSeverity } from 'vs/platform/markers/common/markers'; -import { TextModel } from 'vs/editor/common/model/textModel'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; import { URI } from 'vs/base/common/uri'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; @@ -16,7 +16,7 @@ suite('OutlineModel', function () { test('OutlineModel#create, cached', async function () { - let model = TextModel.createFromString('foo', undefined, undefined, URI.file('/fome/path.foo')); + let model = createTextModel('foo', undefined, undefined, URI.file('/fome/path.foo')); let count = 0; let reg = DocumentSymbolProviderRegistry.register({ pattern: '**/path.foo' }, { provideDocumentSymbols() { @@ -42,7 +42,7 @@ suite('OutlineModel', function () { test('OutlineModel#create, cached/cancel', async function () { - let model = TextModel.createFromString('foo', undefined, undefined, URI.file('/fome/path.foo')); + let model = createTextModel('foo', undefined, undefined, URI.file('/fome/path.foo')); let isCancelled = false; let reg = DocumentSymbolProviderRegistry.register({ pattern: '**/path.foo' }, { diff --git a/src/vs/editor/contrib/folding/test/foldingModel.test.ts b/src/vs/editor/contrib/folding/test/foldingModel.test.ts index 87470e12e09..97a8a209941 100644 --- a/src/vs/editor/contrib/folding/test/foldingModel.test.ts +++ b/src/vs/editor/contrib/folding/test/foldingModel.test.ts @@ -4,7 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; import { FoldingModel, setCollapseStateAtLevel, setCollapseStateLevelsDown, setCollapseStateLevelsUp, setCollapseStateForMatchingLines, setCollapseStateUp } from 'vs/editor/contrib/folding/foldingModel'; -import { TextModel, ModelDecorationOptions } from 'vs/editor/common/model/textModel'; +import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; import { computeRanges } from 'vs/editor/contrib/folding/indentRangeProvider'; import { TrackedRangeStickiness, IModelDeltaDecoration, ITextModel, IModelDecorationsChangeAccessor } from 'vs/editor/common/model'; import { EditOperation } from 'vs/editor/common/core/editOperation'; @@ -92,7 +93,7 @@ suite('Folding Model', () => { /* 7*/ ' }', /* 8*/ '}']; - let textModel = TextModel.createFromString(lines.join('\n')); + let textModel = createTextModel(lines.join('\n')); try { let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel)); @@ -131,7 +132,7 @@ suite('Folding Model', () => { /* 7*/ ' }', /* 8*/ '}']; - let textModel = TextModel.createFromString(lines.join('\n')); + let textModel = createTextModel(lines.join('\n')); try { let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel)); @@ -177,7 +178,7 @@ suite('Folding Model', () => { /* 7*/ ' }', /* 8*/ '}']; - let textModel = TextModel.createFromString(lines.join('\n')); + let textModel = createTextModel(lines.join('\n')); try { let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel)); @@ -217,7 +218,7 @@ suite('Folding Model', () => { /* 12*/ ' }', /* 13*/ '}']; - let textModel = TextModel.createFromString(lines.join('\n')); + let textModel = createTextModel(lines.join('\n')); try { let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel)); @@ -254,7 +255,7 @@ suite('Folding Model', () => { /* 7*/ ' }', /* 8*/ '}']; - let textModel = TextModel.createFromString(lines.join('\n')); + let textModel = createTextModel(lines.join('\n')); try { let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel)); @@ -295,7 +296,7 @@ suite('Folding Model', () => { /* 11*/ ' }', /* 12*/ '}']; - let textModel = TextModel.createFromString(lines.join('\n')); + let textModel = createTextModel(lines.join('\n')); try { let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel)); @@ -346,7 +347,7 @@ suite('Folding Model', () => { /* 10*/ '//#endregion', /* 11*/ '']; - let textModel = TextModel.createFromString(lines.join('\n')); + let textModel = createTextModel(lines.join('\n')); try { let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel)); @@ -392,7 +393,7 @@ suite('Folding Model', () => { /* 12*/ ' }', /* 13*/ '}']; - let textModel = TextModel.createFromString(lines.join('\n')); + let textModel = createTextModel(lines.join('\n')); try { let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel)); @@ -448,7 +449,7 @@ suite('Folding Model', () => { /* 15*/ ' //#endregion', /* 16*/ '}']; - let textModel = TextModel.createFromString(lines.join('\n')); + let textModel = createTextModel(lines.join('\n')); try { let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel)); @@ -504,7 +505,7 @@ suite('Folding Model', () => { /* 12*/ ' }', /* 13*/ '}']; - let textModel = TextModel.createFromString(lines.join('\n')); + let textModel = createTextModel(lines.join('\n')); try { let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel)); @@ -556,7 +557,7 @@ suite('Folding Model', () => { /* 12*/ ' }', /* 13*/ '}']; - let textModel = TextModel.createFromString(lines.join('\n')); + let textModel = createTextModel(lines.join('\n')); try { let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel)); @@ -603,7 +604,7 @@ suite('Folding Model', () => { /* 12*/ ' }', /* 13*/ '}']; - let textModel = TextModel.createFromString(lines.join('\n')); + let textModel = createTextModel(lines.join('\n')); try { let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel)); @@ -648,7 +649,7 @@ suite('Folding Model', () => { /* 12*/ ' }', /* 13*/ '}']; - let textModel = TextModel.createFromString(lines.join('\n')); + let textModel = createTextModel(lines.join('\n')); try { let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel)); diff --git a/src/vs/editor/contrib/folding/test/foldingRanges.test.ts b/src/vs/editor/contrib/folding/test/foldingRanges.test.ts index dd572ec9850..214135a9c8c 100644 --- a/src/vs/editor/contrib/folding/test/foldingRanges.test.ts +++ b/src/vs/editor/contrib/folding/test/foldingRanges.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { TextModel } from 'vs/editor/common/model/textModel'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; import { computeRanges } from 'vs/editor/contrib/folding/indentRangeProvider'; import { FoldingMarkers } from 'vs/editor/common/modes/languageConfiguration'; import { MAX_FOLDING_REGIONS } from 'vs/editor/contrib/folding/foldingRanges'; @@ -26,7 +26,7 @@ suite('FoldingRanges', () => { for (let i = 0; i < nRegions; i++) { lines.push('#endregion'); } - let model = TextModel.createFromString(lines.join('\n')); + let model = createTextModel(lines.join('\n')); let actual = computeRanges(model, false, markers, MAX_FOLDING_REGIONS); assert.equal(actual.length, nRegions, 'len'); for (let i = 0; i < nRegions; i++) { @@ -53,7 +53,7 @@ suite('FoldingRanges', () => { /* 12*/ ' }', /* 13*/ '}']; - let textModel = TextModel.createFromString(lines.join('\n')); + let textModel = createTextModel(lines.join('\n')); try { let actual = computeRanges(textModel, false, markers); // let r0 = r(1, 2); @@ -91,7 +91,7 @@ suite('FoldingRanges', () => { for (let i = 0; i < nRegions; i++) { lines.push('#endregion'); } - let model = TextModel.createFromString(lines.join('\n')); + let model = createTextModel(lines.join('\n')); let actual = computeRanges(model, false, markers, MAX_FOLDING_REGIONS); assert.equal(actual.length, nRegions, 'len'); for (let i = 0; i < nRegions; i++) { diff --git a/src/vs/editor/contrib/folding/test/hiddenRangeModel.test.ts b/src/vs/editor/contrib/folding/test/hiddenRangeModel.test.ts index 18fa5acd3fe..9db5f45e4fb 100644 --- a/src/vs/editor/contrib/folding/test/hiddenRangeModel.test.ts +++ b/src/vs/editor/contrib/folding/test/hiddenRangeModel.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; import { FoldingModel } from 'vs/editor/contrib/folding/foldingModel'; -import { TextModel } from 'vs/editor/common/model/textModel'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; import { computeRanges } from 'vs/editor/contrib/folding/indentRangeProvider'; import { TestDecorationProvider } from './foldingModel.test'; import { HiddenRangeModel } from 'vs/editor/contrib/folding/hiddenRangeModel'; @@ -38,7 +38,7 @@ suite('Hidden Range Model', () => { /* 9*/ ' }', /* 10*/ '}']; - let textModel = TextModel.createFromString(lines.join('\n')); + let textModel = createTextModel(lines.join('\n')); let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel)); let hiddenRangeModel = new HiddenRangeModel(foldingModel); diff --git a/src/vs/editor/contrib/folding/test/indentFold.test.ts b/src/vs/editor/contrib/folding/test/indentFold.test.ts index ed51ec95bac..ccdb26a756e 100644 --- a/src/vs/editor/contrib/folding/test/indentFold.test.ts +++ b/src/vs/editor/contrib/folding/test/indentFold.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; import { computeRanges } from 'vs/editor/contrib/folding/indentRangeProvider'; -import { TextModel } from 'vs/editor/common/model/textModel'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; interface IndentRange { start: number; @@ -47,7 +47,7 @@ suite('Indentation Folding', () => { let r8 = r(13, 14);//4 let r9 = r(15, 16);//0 - let model = TextModel.createFromString(lines.join('\n')); + let model = createTextModel(lines.join('\n')); function assertLimit(maxEntries: number, expectedRanges: IndentRange[], message: string) { let indentRanges = computeRanges(model, true, undefined, maxEntries); diff --git a/src/vs/editor/contrib/folding/test/indentRangeProvider.test.ts b/src/vs/editor/contrib/folding/test/indentRangeProvider.test.ts index 07e20256667..8a4dbe7f38a 100644 --- a/src/vs/editor/contrib/folding/test/indentRangeProvider.test.ts +++ b/src/vs/editor/contrib/folding/test/indentRangeProvider.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { TextModel } from 'vs/editor/common/model/textModel'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; import { computeRanges } from 'vs/editor/contrib/folding/indentRangeProvider'; import { FoldingMarkers } from 'vs/editor/common/modes/languageConfiguration'; @@ -15,7 +15,7 @@ interface ExpectedIndentRange { } function assertRanges(lines: string[], expected: ExpectedIndentRange[], offside: boolean, markers?: FoldingMarkers): void { - let model = TextModel.createFromString(lines.join('\n')); + let model = createTextModel(lines.join('\n')); let actual = computeRanges(model, offside, markers); let actualRanges: ExpectedIndentRange[] = []; diff --git a/src/vs/editor/contrib/folding/test/syntaxFold.test.ts b/src/vs/editor/contrib/folding/test/syntaxFold.test.ts index 480b84e5d7c..86a4280259e 100644 --- a/src/vs/editor/contrib/folding/test/syntaxFold.test.ts +++ b/src/vs/editor/contrib/folding/test/syntaxFold.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { TextModel } from 'vs/editor/common/model/textModel'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; import { SyntaxRangeProvider } from 'vs/editor/contrib/folding/syntaxRangeProvider'; import { FoldingRangeProvider, FoldingRange, FoldingContext, ProviderResult } from 'vs/editor/common/modes'; import { ITextModel } from 'vs/editor/common/model'; @@ -69,7 +69,7 @@ suite('Syntax folding', () => { let r8 = r(14, 15); //6 let r9 = r(22, 23); //0 - let model = TextModel.createFromString(lines.join('\n')); + let model = createTextModel(lines.join('\n')); let ranges = [r1, r2, r3, r4, r5, r6, r7, r8, r9]; let providers = [new TestFoldingRangeProvider(model, ranges)]; diff --git a/src/vs/editor/contrib/parameterHints/test/parameterHintsModel.test.ts b/src/vs/editor/contrib/parameterHints/test/parameterHintsModel.test.ts index 2aa301d692a..2d5e1967fe7 100644 --- a/src/vs/editor/contrib/parameterHints/test/parameterHintsModel.test.ts +++ b/src/vs/editor/contrib/parameterHints/test/parameterHintsModel.test.ts @@ -10,7 +10,7 @@ import { URI } from 'vs/base/common/uri'; import { Position } from 'vs/editor/common/core/position'; import { Handler } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; -import { TextModel } from 'vs/editor/common/model/textModel'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; import * as modes from 'vs/editor/common/modes'; import { createTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; @@ -49,7 +49,7 @@ suite('ParameterHintsModel', () => { }); function createMockEditor(fileContents: string) { - const textModel = TextModel.createFromString(fileContents, undefined, undefined, mockFile); + const textModel = createTextModel(fileContents, undefined, undefined, mockFile); const editor = createTestCodeEditor({ model: textModel, serviceCollection: new ServiceCollection( diff --git a/src/vs/editor/contrib/snippet/test/snippetController2.test.ts b/src/vs/editor/contrib/snippet/test/snippetController2.test.ts index 29703e9307a..4f67e00ac4e 100644 --- a/src/vs/editor/contrib/snippet/test/snippetController2.test.ts +++ b/src/vs/editor/contrib/snippet/test/snippetController2.test.ts @@ -12,6 +12,7 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { NullLogService } from 'vs/platform/log/common/log'; import { Handler } from 'vs/editor/common/editorCommon'; import { CoreEditingCommands } from 'vs/editor/browser/controller/coreCommands'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; suite('SnippetController2', function () { @@ -36,7 +37,7 @@ suite('SnippetController2', function () { setup(function () { contextKeys = new MockContextKeyService(); - model = TextModel.createFromString('if\n $state\nfi'); + model = createTextModel('if\n $state\nfi'); editor = createTestCodeEditor({ model: model }); editor.setSelections([new Selection(1, 1, 1, 1), new Selection(2, 5, 2, 5)]); assert.equal(model.getEOL(), '\n'); diff --git a/src/vs/editor/contrib/snippet/test/snippetSession.test.ts b/src/vs/editor/contrib/snippet/test/snippetSession.test.ts index d4ae07a4a49..cedf6be949e 100644 --- a/src/vs/editor/contrib/snippet/test/snippetSession.test.ts +++ b/src/vs/editor/contrib/snippet/test/snippetSession.test.ts @@ -11,6 +11,7 @@ import { TextModel } from 'vs/editor/common/model/textModel'; import { SnippetParser } from 'vs/editor/contrib/snippet/snippetParser'; import { SnippetSession } from 'vs/editor/contrib/snippet/snippetSession'; import { createTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; suite('SnippetSession', function () { @@ -26,7 +27,7 @@ suite('SnippetSession', function () { } setup(function () { - model = TextModel.createFromString('function foo() {\n console.log(a);\n}'); + model = createTextModel('function foo() {\n console.log(a);\n}'); editor = createTestCodeEditor({ model: model }) as IActiveCodeEditor; editor.setSelections([new Selection(1, 1, 1, 1), new Selection(2, 5, 2, 5)]); assert.equal(model.getEOL(), '\n'); diff --git a/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts b/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts index 06eb5f8777c..b7012bec477 100644 --- a/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts +++ b/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts @@ -12,6 +12,7 @@ import { TextModel } from 'vs/editor/common/model/textModel'; import { Workspace, toWorkspaceFolders, IWorkspace, IWorkspaceContextService, toWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { ILabelService } from 'vs/platform/label/common/label'; import { mock } from 'vs/editor/contrib/suggest/test/suggestModel.test'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; suite('Snippet Variables Resolver', function () { @@ -25,7 +26,7 @@ suite('Snippet Variables Resolver', function () { let resolver: VariableResolver; setup(function () { - model = TextModel.createFromString([ + model = createTextModel([ 'this is line one', 'this is line two', ' this is line three' @@ -67,7 +68,7 @@ suite('Snippet Variables Resolver', function () { resolver = new ModelBasedVariableResolver( labelService, - TextModel.createFromString('', undefined, undefined, URI.parse('http://www.pb.o/abc/def/ghi')) + createTextModel('', undefined, undefined, URI.parse('http://www.pb.o/abc/def/ghi')) ); assertVariableResolve(resolver, 'TM_FILENAME', 'ghi'); if (!isWindows) { @@ -77,7 +78,7 @@ suite('Snippet Variables Resolver', function () { resolver = new ModelBasedVariableResolver( labelService, - TextModel.createFromString('', undefined, undefined, URI.parse('mem:fff.ts')) + createTextModel('', undefined, undefined, URI.parse('mem:fff.ts')) ); assertVariableResolve(resolver, 'TM_DIRECTORY', ''); assertVariableResolve(resolver, 'TM_FILEPATH', 'fff.ts'); @@ -92,7 +93,7 @@ suite('Snippet Variables Resolver', function () { } }; - const model = TextModel.createFromString([].join('\n'), undefined, undefined, URI.parse('foo:///foo/files/text.txt')); + const model = createTextModel([].join('\n'), undefined, undefined, URI.parse('foo:///foo/files/text.txt')); const resolver = new CompositeSnippetVariableResolver([new ModelBasedVariableResolver(labelService, model)]); @@ -144,19 +145,19 @@ suite('Snippet Variables Resolver', function () { resolver = new ModelBasedVariableResolver( labelService, - TextModel.createFromString('', undefined, undefined, URI.parse('http://www.pb.o/abc/def/ghi')) + createTextModel('', undefined, undefined, URI.parse('http://www.pb.o/abc/def/ghi')) ); assertVariableResolve(resolver, 'TM_FILENAME_BASE', 'ghi'); resolver = new ModelBasedVariableResolver( labelService, - TextModel.createFromString('', undefined, undefined, URI.parse('mem:.git')) + createTextModel('', undefined, undefined, URI.parse('mem:.git')) ); assertVariableResolve(resolver, 'TM_FILENAME_BASE', '.git'); resolver = new ModelBasedVariableResolver( labelService, - TextModel.createFromString('', undefined, undefined, URI.parse('mem:foo.')) + createTextModel('', undefined, undefined, URI.parse('mem:foo.')) ); assertVariableResolve(resolver, 'TM_FILENAME_BASE', 'foo'); }); diff --git a/src/vs/editor/contrib/suggest/test/suggest.test.ts b/src/vs/editor/contrib/suggest/test/suggest.test.ts index da210d07ab2..f22d8898a0f 100644 --- a/src/vs/editor/contrib/suggest/test/suggest.test.ts +++ b/src/vs/editor/contrib/suggest/test/suggest.test.ts @@ -10,6 +10,7 @@ import { provideSuggestionItems, SnippetSortOrder, CompletionOptions } from 'vs/ import { Position } from 'vs/editor/common/core/position'; import { TextModel } from 'vs/editor/common/model/textModel'; import { Range } from 'vs/editor/common/core/range'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; suite('Suggest', function () { @@ -19,7 +20,7 @@ suite('Suggest', function () { setup(function () { - model = TextModel.createFromString('FOO\nbar\BAR\nfoo', undefined, undefined, URI.parse('foo:bar/path')); + model = createTextModel('FOO\nbar\BAR\nfoo', undefined, undefined, URI.parse('foo:bar/path')); registration = CompletionProviderRegistry.register({ pattern: 'bar/path', scheme: 'foo' }, { provideCompletionItems(_doc, pos) { return { diff --git a/src/vs/editor/contrib/suggest/test/suggestController.test.ts b/src/vs/editor/contrib/suggest/test/suggestController.test.ts index b94da857b16..06c95543ec3 100644 --- a/src/vs/editor/contrib/suggest/test/suggestController.test.ts +++ b/src/vs/editor/contrib/suggest/test/suggestController.test.ts @@ -23,6 +23,7 @@ import { CompletionProviderRegistry, CompletionItemKind, CompletionItemInsertTex import { Event } from 'vs/base/common/event'; import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2'; import { IMenuService, IMenu } from 'vs/platform/actions/common/actions'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; suite('SuggestController', function () { @@ -57,7 +58,7 @@ suite('SuggestController', function () { }] ); - model = TextModel.createFromString('', undefined, undefined, URI.from({ scheme: 'test-ctrl', path: '/path.tst' })); + model = createTextModel('', undefined, undefined, URI.from({ scheme: 'test-ctrl', path: '/path.tst' })); editor = createTestCodeEditor({ model, serviceCollection, diff --git a/src/vs/editor/contrib/suggest/test/suggestMemory.test.ts b/src/vs/editor/contrib/suggest/test/suggestMemory.test.ts index fd2ee8ec49c..28deea319ab 100644 --- a/src/vs/editor/contrib/suggest/test/suggestMemory.test.ts +++ b/src/vs/editor/contrib/suggest/test/suggestMemory.test.ts @@ -6,7 +6,7 @@ import * as assert from 'assert'; import { LRUMemory, NoMemory, PrefixMemory, Memory } from 'vs/editor/contrib/suggest/suggestMemory'; import { ITextModel } from 'vs/editor/common/model'; -import { TextModel } from 'vs/editor/common/model/textModel'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; import { createSuggestItem } from 'vs/editor/contrib/suggest/test/completionModel.test'; import { IPosition } from 'vs/editor/common/core/position'; import { CompletionItem } from 'vs/editor/contrib/suggest/suggest'; @@ -19,7 +19,7 @@ suite('SuggestMemories', function () { setup(function () { pos = { lineNumber: 1, column: 1 }; - buffer = TextModel.createFromString('This is some text.\nthis.\nfoo: ,'); + buffer = createTextModel('This is some text.\nthis.\nfoo: ,'); items = [ createSuggestItem('foo', 0), createSuggestItem('bar', 0) diff --git a/src/vs/editor/contrib/suggest/test/suggestModel.test.ts b/src/vs/editor/contrib/suggest/test/suggestModel.test.ts index c7e14186f2e..c0c0a86d776 100644 --- a/src/vs/editor/contrib/suggest/test/suggestModel.test.ts +++ b/src/vs/editor/contrib/suggest/test/suggestModel.test.ts @@ -32,6 +32,7 @@ import { ISuggestMemoryService } from 'vs/editor/contrib/suggest/suggestMemory'; import { ITextModel } from 'vs/editor/common/model'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { MockKeybindingService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; export interface Ctor { new(): T; @@ -124,7 +125,7 @@ suite('SuggestModel - Context', function () { }); test('Context - shouldAutoTrigger', function () { - const model = TextModel.createFromString('Das Pferd frisst keinen Gurkensalat - Philipp Reis 1861.\nWer hat\'s erfunden?'); + const model = createTextModel('Das Pferd frisst keinen Gurkensalat - Philipp Reis 1861.\nWer hat\'s erfunden?'); disposables.push(model); assertAutoTrigger(model, 3, true, 'end of word, Das|'); @@ -138,7 +139,7 @@ suite('SuggestModel - Context', function () { const innerMode = new InnerMode(); disposables.push(outerMode, innerMode); - const model = TextModel.createFromString('aa', undefined, outerMode.getLanguageIdentifier()); + const model = createTextModel('aa', undefined, outerMode.getLanguageIdentifier()); disposables.push(model); assertAutoTrigger(model, 1, true, 'a| { ]; function _runTest(selection: Selection, editRange: Range, editText: string, editForceMoveMarkers: boolean, expected: Selection, msg: string): void { - const model = TextModel.createFromString(LINES.join('\n')); + const model = createTextModel(LINES.join('\n')); const config = new TestConfiguration({}); const monospaceLineBreaksComputerFactory = MonospaceLineBreaksComputerFactory.create(config.options); const viewModel = new ViewModel(0, config, model, monospaceLineBreaksComputerFactory, monospaceLineBreaksComputerFactory, null!); diff --git a/src/vs/editor/test/browser/controller/cursorMoveCommand.test.ts b/src/vs/editor/test/browser/controller/cursorMoveCommand.test.ts index 36f2f54dc23..e918feacef0 100644 --- a/src/vs/editor/test/browser/controller/cursorMoveCommand.test.ts +++ b/src/vs/editor/test/browser/controller/cursorMoveCommand.test.ts @@ -14,6 +14,7 @@ import { TextModel } from 'vs/editor/common/model/textModel'; import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl'; import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration'; import { MonospaceLineBreaksComputerFactory } from 'vs/editor/common/viewModel/monospaceLineBreaksComputer'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; suite('Cursor move command test', () => { @@ -31,7 +32,7 @@ suite('Cursor move command test', () => { '1' ].join('\n'); - thisModel = TextModel.createFromString(text); + thisModel = createTextModel(text); thisConfiguration = new TestConfiguration({}); const monospaceLineBreaksComputerFactory = MonospaceLineBreaksComputerFactory.create(thisConfiguration.options); thisViewModel = new ViewModel(0, thisConfiguration, thisModel, monospaceLineBreaksComputerFactory, monospaceLineBreaksComputerFactory, null!); diff --git a/src/vs/editor/test/browser/controller/textAreaState.test.ts b/src/vs/editor/test/browser/controller/textAreaState.test.ts index 005a30b465f..163e9de5422 100644 --- a/src/vs/editor/test/browser/controller/textAreaState.test.ts +++ b/src/vs/editor/test/browser/controller/textAreaState.test.ts @@ -8,7 +8,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { ITextAreaWrapper, PagedScreenReaderStrategy, TextAreaState } from 'vs/editor/browser/controller/textAreaState'; import { Position } from 'vs/editor/common/core/position'; import { Selection } from 'vs/editor/common/core/selection'; -import { TextModel } from 'vs/editor/common/model/textModel'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; export class MockTextAreaWrapper extends Disposable implements ITextAreaWrapper { @@ -506,7 +506,7 @@ suite('TextAreaState', () => { suite('PagedScreenReaderStrategy', () => { function testPagedScreenReaderStrategy(lines: string[], selection: Selection, expected: TextAreaState): void { - const model = TextModel.createFromString(lines.join('\n')); + const model = createTextModel(lines.join('\n')); const actual = PagedScreenReaderStrategy.fromEditorSelection(TextAreaState.EMPTY, model, selection, 10, true); assert.ok(equalsTextAreaState(actual, expected)); model.dispose(); diff --git a/src/vs/editor/test/browser/testCodeEditor.ts b/src/vs/editor/test/browser/testCodeEditor.ts index d138d2e396a..5153fc082ec 100644 --- a/src/vs/editor/test/browser/testCodeEditor.ts +++ b/src/vs/editor/test/browser/testCodeEditor.ts @@ -11,7 +11,7 @@ import * as editorOptions from 'vs/editor/common/config/editorOptions'; import { Cursor } from 'vs/editor/common/controller/cursor'; import { IConfiguration, IEditorContribution } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; -import { TextModel } from 'vs/editor/common/model/textModel'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl'; import { TestCodeEditorService, TestCommandService } from 'vs/editor/test/browser/editorTestServices'; import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration'; @@ -77,9 +77,9 @@ export function withTestCodeEditor(text: string | string[] | null, options: Test // create a model if necessary and remember it in order to dispose it. if (!options.model) { if (typeof text === 'string') { - options.model = TextModel.createFromString(text); + options.model = createTextModel(text); } else if (text) { - options.model = TextModel.createFromString(text.join('\n')); + options.model = createTextModel(text.join('\n')); } } diff --git a/src/vs/editor/test/browser/testCommand.ts b/src/vs/editor/test/browser/testCommand.ts index f5670377c6e..4c280c1d960 100644 --- a/src/vs/editor/test/browser/testCommand.ts +++ b/src/vs/editor/test/browser/testCommand.ts @@ -8,7 +8,7 @@ import { IRange } from 'vs/editor/common/core/range'; import { Selection, ISelection } from 'vs/editor/common/core/selection'; import { ICommand, Handler, IEditOperationBuilder } from 'vs/editor/common/editorCommon'; import { IIdentifiedSingleEditOperation, ITextModel } from 'vs/editor/common/model'; -import { TextModel } from 'vs/editor/common/model/textModel'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; import { LanguageIdentifier } from 'vs/editor/common/modes'; import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; @@ -21,7 +21,7 @@ export function testCommand( expectedSelection: Selection, forceTokenization?: boolean ): void { - let model = TextModel.createFromString(lines.join('\n'), undefined, languageIdentifier); + let model = createTextModel(lines.join('\n'), undefined, languageIdentifier); withTestCodeEditor('', { model: model }, (_editor, cursor) => { if (!cursor) { return; diff --git a/src/vs/editor/test/common/editorTestUtils.ts b/src/vs/editor/test/common/editorTestUtils.ts index 3116fa31171..48c5ce03264 100644 --- a/src/vs/editor/test/common/editorTestUtils.ts +++ b/src/vs/editor/test/common/editorTestUtils.ts @@ -9,7 +9,7 @@ import { TextModel } from 'vs/editor/common/model/textModel'; import { LanguageIdentifier } from 'vs/editor/common/modes'; export function withEditorModel(text: string[], callback: (model: TextModel) => void): void { - let model = TextModel.createFromString(text.join('\n')); + let model = createTextModel(text.join('\n')); callback(model); model.dispose(); } diff --git a/src/vs/editor/test/common/model/editableTextModel.test.ts b/src/vs/editor/test/common/model/editableTextModel.test.ts index bbb6b3dd781..b37a3b9820e 100644 --- a/src/vs/editor/test/common/model/editableTextModel.test.ts +++ b/src/vs/editor/test/common/model/editableTextModel.test.ts @@ -10,9 +10,10 @@ import { MirrorTextModel } from 'vs/editor/common/model/mirrorTextModel'; import { TextModel } from 'vs/editor/common/model/textModel'; import { IModelContentChangedEvent } from 'vs/editor/common/model/textModelEvents'; import { assertSyncedModels, testApplyEditsWithSyncedModels } from 'vs/editor/test/common/model/editableTextModelTestUtils'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; function createEditableTextModelFromString(text: string): TextModel { - return TextModel.createFromString(text, TextModel.DEFAULT_CREATION_OPTIONS, null); + return createTextModel(text, TextModel.DEFAULT_CREATION_OPTIONS, null); } suite('EditorModel - EditableTextModel.applyEdits updates mightContainRTL', () => { diff --git a/src/vs/editor/test/common/model/editableTextModelTestUtils.ts b/src/vs/editor/test/common/model/editableTextModelTestUtils.ts index a4124f4b519..e1e4b5c999e 100644 --- a/src/vs/editor/test/common/model/editableTextModelTestUtils.ts +++ b/src/vs/editor/test/common/model/editableTextModelTestUtils.ts @@ -9,6 +9,7 @@ import { EndOfLinePreference, EndOfLineSequence, IIdentifiedSingleEditOperation import { MirrorTextModel } from 'vs/editor/common/model/mirrorTextModel'; import { TextModel } from 'vs/editor/common/model/textModel'; import { IModelContentChangedEvent } from 'vs/editor/common/model/textModelEvents'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; export function testApplyEditsWithSyncedModels(original: string[], edits: IIdentifiedSingleEditOperation[], expected: string[], inputEditsAreInvalid: boolean = false): void { let originalStr = original.join('\n'); @@ -88,7 +89,7 @@ function assertLineMapping(model: TextModel, msg: string): void { export function assertSyncedModels(text: string, callback: (model: TextModel, assertMirrorModels: () => void) => void, setup: ((model: TextModel) => void) | null = null): void { - let model = TextModel.createFromString(text, TextModel.DEFAULT_CREATION_OPTIONS, null); + let model = createTextModel(text, TextModel.DEFAULT_CREATION_OPTIONS, null); model.setEOL(EndOfLineSequence.LF); assertLineMapping(model, 'model'); 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 518a30d94b8..139b169bf1d 100644 --- a/src/vs/editor/test/common/model/model.line.test.ts +++ b/src/vs/editor/test/common/model/model.line.test.ts @@ -9,6 +9,7 @@ import { Range } from 'vs/editor/common/core/range'; import { TextModel } from 'vs/editor/common/model/textModel'; import { LanguageIdentifier, MetadataConsts } from 'vs/editor/common/modes'; import { ViewLineToken, ViewLineTokenFactory } from 'vs/editor/test/common/core/viewLineToken'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; interface ILineEdit { startColumn: number; @@ -106,7 +107,7 @@ suite('ModelLinesTokens', () => { function testApplyEdits(initial: IBufferLineState[], edits: IEdit[], expected: IBufferLineState[]): void { const initialText = initial.map(el => el.text).join('\n'); - const model = TextModel.createFromString(initialText, TextModel.DEFAULT_CREATION_OPTIONS, new LanguageIdentifier('test', 0)); + const model = createTextModel(initialText, TextModel.DEFAULT_CREATION_OPTIONS, new LanguageIdentifier('test', 0)); for (let lineIndex = 0; lineIndex < initial.length; lineIndex++) { const lineTokens = initial[lineIndex].tokens; const lineTextLength = model.getLineMaxColumn(lineIndex + 1) - 1; @@ -442,7 +443,7 @@ suite('ModelLinesTokens', () => { } test('insertion on empty line', () => { - const model = TextModel.createFromString('some text', TextModel.DEFAULT_CREATION_OPTIONS, new LanguageIdentifier('test', 0)); + const model = createTextModel('some text', TextModel.DEFAULT_CREATION_OPTIONS, new LanguageIdentifier('test', 0)); const tokens = TestToken.toTokens([new TestToken(0, 1)]); LineTokens.convertToEndOffset(tokens, model.getLineMaxColumn(1) - 1); model.setLineTokens(1, tokens); diff --git a/src/vs/editor/test/common/model/model.modes.test.ts b/src/vs/editor/test/common/model/model.modes.test.ts index 559a6f89f07..ce313b77a70 100644 --- a/src/vs/editor/test/common/model/model.modes.test.ts +++ b/src/vs/editor/test/common/model/model.modes.test.ts @@ -12,6 +12,7 @@ import { TokenizationResult2 } from 'vs/editor/common/core/token'; import { TextModel } from 'vs/editor/common/model/textModel'; import * as modes from 'vs/editor/common/modes'; import { NULL_STATE } from 'vs/editor/common/modes/nullMode'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; // --------- utils @@ -46,7 +47,7 @@ suite('Editor Model - Model Modes 1', () => { const LANGUAGE_ID = 'modelModeTest1'; calledFor = []; languageRegistration = modes.TokenizationRegistry.register(LANGUAGE_ID, tokenizationSupport); - thisModel = TextModel.createFromString(TEXT, undefined, new modes.LanguageIdentifier(LANGUAGE_ID, 0)); + thisModel = createTextModel(TEXT, undefined, new modes.LanguageIdentifier(LANGUAGE_ID, 0)); }); teardown(() => { @@ -199,7 +200,7 @@ suite('Editor Model - Model Modes 2', () => { 'Line5'; const LANGUAGE_ID = 'modelModeTest2'; languageRegistration = modes.TokenizationRegistry.register(LANGUAGE_ID, tokenizationSupport); - thisModel = TextModel.createFromString(TEXT, undefined, new modes.LanguageIdentifier(LANGUAGE_ID, 0)); + thisModel = createTextModel(TEXT, undefined, new modes.LanguageIdentifier(LANGUAGE_ID, 0)); }); teardown(() => { diff --git a/src/vs/editor/test/common/model/model.test.ts b/src/vs/editor/test/common/model/model.test.ts index 7fb877d7fd8..77ce4133e46 100644 --- a/src/vs/editor/test/common/model/model.test.ts +++ b/src/vs/editor/test/common/model/model.test.ts @@ -15,6 +15,7 @@ import { IState, LanguageIdentifier, MetadataConsts, TokenizationRegistry } from import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { NULL_STATE } from 'vs/editor/common/modes/nullMode'; import { MockMode } from 'vs/editor/test/common/mocks/mockMode'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; // --------- utils @@ -35,7 +36,7 @@ suite('Editor Model - Model', () => { LINE3 + '\n' + LINE4 + '\r\n' + LINE5; - thisModel = TextModel.createFromString(text); + thisModel = createTextModel(text); }); teardown(() => { @@ -349,7 +350,7 @@ suite('Editor Model - Model Line Separators', () => { LINE3 + '\u2028' + LINE4 + '\r\n' + LINE5; - thisModel = TextModel.createFromString(text); + thisModel = createTextModel(text); }); teardown(() => { @@ -365,7 +366,7 @@ suite('Editor Model - Model Line Separators', () => { }); test('Bug 13333:Model should line break on lonely CR too', () => { - let model = TextModel.createFromString('Hello\rWorld!\r\nAnother line'); + let model = createTextModel('Hello\rWorld!\r\nAnother line'); assert.equal(model.getLineCount(), 3); assert.equal(model.getValue(), 'Hello\r\nWorld!\r\nAnother line'); model.dispose(); @@ -430,7 +431,7 @@ suite('Editor Model - Words', () => { test('Get word at position', () => { const text = ['This text has some words. ']; - const thisModel = TextModel.createFromString(text.join('\n')); + const thisModel = createTextModel(text.join('\n')); disposables.push(thisModel); assert.deepEqual(thisModel.getWordAtPosition(new Position(1, 1)), { word: 'This', startColumn: 1, endColumn: 5 }); @@ -451,7 +452,7 @@ suite('Editor Model - Words', () => { const innerMode = new InnerMode(); disposables.push(outerMode, innerMode); - const model = TextModel.createFromString('abab', undefined, outerMode.getLanguageIdentifier()); + const model = createTextModel('abab', undefined, outerMode.getLanguageIdentifier()); disposables.push(model); assert.deepEqual(model.getWordAtPosition(new Position(1, 1)), { word: 'ab', startColumn: 1, endColumn: 3 }); @@ -476,7 +477,7 @@ suite('Editor Model - Words', () => { }; disposables.push(mode); - const thisModel = TextModel.createFromString('.🐷-a-b', undefined, MODE_ID); + const thisModel = createTextModel('.🐷-a-b', undefined, MODE_ID); disposables.push(thisModel); assert.deepEqual(thisModel.getWordAtPosition(new Position(1, 1)), { word: '.', startColumn: 1, endColumn: 2 }); diff --git a/src/vs/editor/test/common/model/modelDecorations.test.ts b/src/vs/editor/test/common/model/modelDecorations.test.ts index afe58fd37d7..4f7690f1111 100644 --- a/src/vs/editor/test/common/model/modelDecorations.test.ts +++ b/src/vs/editor/test/common/model/modelDecorations.test.ts @@ -9,6 +9,7 @@ import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { EndOfLineSequence, IModelDeltaDecoration, TrackedRangeStickiness } from 'vs/editor/common/model'; import { TextModel } from 'vs/editor/common/model/textModel'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; // --------- utils @@ -92,7 +93,7 @@ suite('Editor Model - Model Decorations', () => { LINE3 + '\n' + LINE4 + '\r\n' + LINE5; - thisModel = TextModel.createFromString(text); + thisModel = createTextModel(text); }); teardown(() => { @@ -400,7 +401,7 @@ suite('Editor Model - Model Decorations', () => { }); test('removeAllDecorationsWithOwnerId can be called after model dispose', () => { - let model = TextModel.createFromString('asd'); + let model = createTextModel('asd'); model.dispose(); model.removeAllDecorationsWithOwnerId(1); }); @@ -415,7 +416,7 @@ suite('Editor Model - Model Decorations', () => { suite('Decorations and editing', () => { function _runTest(decRange: Range, stickiness: TrackedRangeStickiness, editRange: Range, editText: string, editForceMoveMarkers: boolean, expectedDecRange: Range, msg: string): void { - let model = TextModel.createFromString([ + let model = createTextModel([ 'My First Line', 'My Second Line', 'Third Line' @@ -1148,7 +1149,7 @@ suite('deltaDecorations', () => { function testDeltaDecorations(text: string[], decorations: ILightWeightDecoration[], newDecorations: ILightWeightDecoration[]): void { - let model = TextModel.createFromString(text.join('\n')); + let model = createTextModel(text.join('\n')); // Add initial decorations & assert they are added let initialIds = model.deltaDecorations([], decorations.map(toModelDeltaDecoration)); @@ -1177,7 +1178,7 @@ suite('deltaDecorations', () => { } test('result respects input', () => { - let model = TextModel.createFromString([ + let model = createTextModel([ 'Hello world,', 'How are you?' ].join('\n')); @@ -1265,7 +1266,7 @@ suite('deltaDecorations', () => { test('issue #4317: editor.setDecorations doesn\'t update the hover message', () => { - let model = TextModel.createFromString('Hello world!'); + let model = createTextModel('Hello world!'); let ids = model.deltaDecorations([], [{ range: { @@ -1299,7 +1300,7 @@ suite('deltaDecorations', () => { }); test('model doesn\'t get confused with individual tracked ranges', () => { - let model = TextModel.createFromString([ + let model = createTextModel([ 'Hello world,', 'How are you?' ].join('\n')); @@ -1340,7 +1341,7 @@ suite('deltaDecorations', () => { }); test('issue #16922: Clicking on link doesn\'t seem to do anything', () => { - let model = TextModel.createFromString([ + let model = createTextModel([ 'Hello world,', 'How are you?', 'Fine.', @@ -1371,7 +1372,7 @@ suite('deltaDecorations', () => { test('issue #41492: URL highlighting persists after pasting over url', () => { - let model = TextModel.createFromString([ + let model = createTextModel([ 'My First Line' ].join('\n')); diff --git a/src/vs/editor/test/common/model/modelEditOperation.test.ts b/src/vs/editor/test/common/model/modelEditOperation.test.ts index 4340d6bbbeb..2fc1337cbe2 100644 --- a/src/vs/editor/test/common/model/modelEditOperation.test.ts +++ b/src/vs/editor/test/common/model/modelEditOperation.test.ts @@ -6,6 +6,7 @@ import * as assert from 'assert'; import { Range } from 'vs/editor/common/core/range'; import { IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; import { TextModel } from 'vs/editor/common/model/textModel'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; suite('Editor Model - Model Edit Operation', () => { const LINE1 = 'My First Line'; @@ -23,7 +24,7 @@ suite('Editor Model - Model Edit Operation', () => { LINE3 + '\n' + LINE4 + '\r\n' + LINE5; - model = TextModel.createFromString(text); + model = createTextModel(text); }); teardown(() => { diff --git a/src/vs/editor/test/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.test.ts b/src/vs/editor/test/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.test.ts index e24401abda1..ddec1faccd1 100644 --- a/src/vs/editor/test/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.test.ts +++ b/src/vs/editor/test/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.test.ts @@ -12,7 +12,7 @@ import { PieceTreeBase } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceT import { PieceTreeTextBuffer } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer'; import { PieceTreeTextBufferBuilder } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder'; import { NodeColor, SENTINEL, TreeNode } from 'vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase'; -import { TextModel } from 'vs/editor/common/model/textModel'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; import { SearchData } from 'vs/editor/common/model/textModelSearch'; const alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\r\n'; @@ -1761,7 +1761,7 @@ function getValueInSnapshot(snapshot: ITextSnapshot) { } suite('snapshot', () => { test('bug #45564, piece tree pieces should be immutable', () => { - const model = TextModel.createFromString('\n'); + const model = createTextModel('\n'); model.applyEdits([ { range: new Range(2, 1, 2, 1), @@ -1789,7 +1789,7 @@ suite('snapshot', () => { }); test('immutable snapshot 1', () => { - const model = TextModel.createFromString('abc\ndef'); + const model = createTextModel('abc\ndef'); const snapshot = model.createSnapshot(); model.applyEdits([ { @@ -1809,7 +1809,7 @@ suite('snapshot', () => { }); test('immutable snapshot 2', () => { - const model = TextModel.createFromString('abc\ndef'); + const model = createTextModel('abc\ndef'); const snapshot = model.createSnapshot(); model.applyEdits([ { @@ -1829,7 +1829,7 @@ suite('snapshot', () => { }); test('immutable snapshot 3', () => { - const model = TextModel.createFromString('abc\ndef'); + const model = createTextModel('abc\ndef'); model.applyEdits([ { range: new Range(2, 4, 2, 4), @@ -1896,4 +1896,4 @@ suite('chunk based search', () => { assert.equal(ret.length, 1); assert.deepEqual(ret[0].range, new Range(2, 2, 2, 3)); }); -}); \ No newline at end of file +}); diff --git a/src/vs/editor/test/common/model/textModel.test.ts b/src/vs/editor/test/common/model/textModel.test.ts index bf4ee50506c..2da334170d2 100644 --- a/src/vs/editor/test/common/model/textModel.test.ts +++ b/src/vs/editor/test/common/model/textModel.test.ts @@ -163,7 +163,7 @@ suite('Editor Model - TextModel', () => { test('getValueLengthInRange', () => { - let m = TextModel.createFromString('My First Line\r\nMy Second Line\r\nMy Third Line'); + let m = createTextModel('My First Line\r\nMy Second Line\r\nMy Third Line'); assert.equal(m.getValueLengthInRange(new Range(1, 1, 1, 1)), ''.length); assert.equal(m.getValueLengthInRange(new Range(1, 1, 1, 2)), 'M'.length); assert.equal(m.getValueLengthInRange(new Range(1, 2, 1, 3)), 'y'.length); @@ -176,7 +176,7 @@ suite('Editor Model - TextModel', () => { assert.equal(m.getValueLengthInRange(new Range(1, 2, 3, 1000)), 'y First Line\r\nMy Second Line\r\nMy Third Line'.length); assert.equal(m.getValueLengthInRange(new Range(1, 1, 1000, 1000)), 'My First Line\r\nMy Second Line\r\nMy Third Line'.length); - m = TextModel.createFromString('My First Line\nMy Second Line\nMy Third Line'); + m = createTextModel('My First Line\nMy Second Line\nMy Third Line'); assert.equal(m.getValueLengthInRange(new Range(1, 1, 1, 1)), ''.length); assert.equal(m.getValueLengthInRange(new Range(1, 1, 1, 2)), 'M'.length); assert.equal(m.getValueLengthInRange(new Range(1, 2, 1, 3)), 'y'.length); @@ -662,7 +662,7 @@ suite('Editor Model - TextModel', () => { test('validatePosition', () => { - let m = TextModel.createFromString('line one\nline two'); + let m = createTextModel('line one\nline two'); assert.deepEqual(m.validatePosition(new Position(0, 0)), new Position(1, 1)); assert.deepEqual(m.validatePosition(new Position(0, 1)), new Position(1, 1)); @@ -691,7 +691,7 @@ suite('Editor Model - TextModel', () => { test('validatePosition around high-low surrogate pairs 1', () => { - let m = TextModel.createFromString('a📚b'); + let m = createTextModel('a📚b'); assert.deepEqual(m.validatePosition(new Position(0, 0)), new Position(1, 1)); assert.deepEqual(m.validatePosition(new Position(0, 1)), new Position(1, 1)); @@ -718,7 +718,7 @@ suite('Editor Model - TextModel', () => { test('validatePosition around high-low surrogate pairs 2', () => { - let m = TextModel.createFromString('a📚📚b'); + let m = createTextModel('a📚📚b'); assert.deepEqual(m.validatePosition(new Position(1, 1)), new Position(1, 1)); assert.deepEqual(m.validatePosition(new Position(1, 2)), new Position(1, 2)); @@ -732,7 +732,7 @@ suite('Editor Model - TextModel', () => { test('validatePosition handle NaN.', () => { - let m = TextModel.createFromString('line one\nline two'); + let m = createTextModel('line one\nline two'); assert.deepEqual(m.validatePosition(new Position(NaN, 1)), new Position(1, 1)); assert.deepEqual(m.validatePosition(new Position(1, NaN)), new Position(1, 1)); @@ -743,7 +743,7 @@ suite('Editor Model - TextModel', () => { }); test('issue #71480: validatePosition handle floats', () => { - let m = TextModel.createFromString('line one\nline two'); + let m = createTextModel('line one\nline two'); assert.deepEqual(m.validatePosition(new Position(0.2, 1)), new Position(1, 1), 'a'); assert.deepEqual(m.validatePosition(new Position(1.2, 1)), new Position(1, 1), 'b'); @@ -756,7 +756,7 @@ suite('Editor Model - TextModel', () => { }); test('issue #71480: validateRange handle floats', () => { - let m = TextModel.createFromString('line one\nline two'); + let m = createTextModel('line one\nline two'); assert.deepEqual(m.validateRange(new Range(0.2, 1.5, 0.8, 2.5)), new Range(1, 1, 1, 1)); assert.deepEqual(m.validateRange(new Range(1.2, 1.7, 1.8, 2.2)), new Range(1, 1, 1, 2)); @@ -764,7 +764,7 @@ suite('Editor Model - TextModel', () => { test('validateRange around high-low surrogate pairs 1', () => { - let m = TextModel.createFromString('a📚b'); + let m = createTextModel('a📚b'); assert.deepEqual(m.validateRange(new Range(0, 0, 0, 1)), new Range(1, 1, 1, 1)); assert.deepEqual(m.validateRange(new Range(0, 0, 0, 7)), new Range(1, 1, 1, 1)); @@ -792,7 +792,7 @@ suite('Editor Model - TextModel', () => { test('validateRange around high-low surrogate pairs 2', () => { - let m = TextModel.createFromString('a📚📚b'); + let m = createTextModel('a📚📚b'); assert.deepEqual(m.validateRange(new Range(0, 0, 0, 1)), new Range(1, 1, 1, 1)); assert.deepEqual(m.validateRange(new Range(0, 0, 0, 7)), new Range(1, 1, 1, 1)); @@ -835,7 +835,7 @@ suite('Editor Model - TextModel', () => { test('modifyPosition', () => { - let m = TextModel.createFromString('line one\nline two'); + let m = createTextModel('line one\nline two'); assert.deepEqual(m.modifyPosition(new Position(1, 1), 0), new Position(1, 1)); assert.deepEqual(m.modifyPosition(new Position(0, 0), 0), new Position(1, 1)); assert.deepEqual(m.modifyPosition(new Position(30, 1), 0), new Position(2, 9)); @@ -913,7 +913,7 @@ suite('Editor Model - TextModel', () => { }); test('getLineFirstNonWhitespaceColumn', () => { - let model = TextModel.createFromString([ + let model = createTextModel([ 'asd', ' asd', '\tasd', @@ -943,7 +943,7 @@ suite('Editor Model - TextModel', () => { }); test('getLineLastNonWhitespaceColumn', () => { - let model = TextModel.createFromString([ + let model = createTextModel([ 'asd', 'asd ', 'asd\t', @@ -973,7 +973,7 @@ suite('Editor Model - TextModel', () => { }); test('#50471. getValueInRange with invalid range', () => { - let m = TextModel.createFromString('My First Line\r\nMy Second Line\r\nMy Third Line'); + let m = createTextModel('My First Line\r\nMy Second Line\r\nMy Third Line'); assert.equal(m.getValueInRange(new Range(1, NaN, 1, 3)), 'My'); assert.equal(m.getValueInRange(new Range(NaN, NaN, NaN, NaN)), ''); }); @@ -982,24 +982,24 @@ suite('Editor Model - TextModel', () => { suite('TextModel.mightContainRTL', () => { test('nope', () => { - let model = TextModel.createFromString('hello world!'); + let model = createTextModel('hello world!'); assert.equal(model.mightContainRTL(), false); }); test('yes', () => { - let model = TextModel.createFromString('Hello,\nזוהי עובדה מבוססת שדעתו'); + let model = createTextModel('Hello,\nזוהי עובדה מבוססת שדעתו'); assert.equal(model.mightContainRTL(), true); }); test('setValue resets 1', () => { - let model = TextModel.createFromString('hello world!'); + let model = createTextModel('hello world!'); assert.equal(model.mightContainRTL(), false); model.setValue('Hello,\nזוהי עובדה מבוססת שדעתו'); assert.equal(model.mightContainRTL(), true); }); test('setValue resets 2', () => { - let model = TextModel.createFromString('Hello,\nهناك حقيقة مثبتة منذ زمن طويل'); + let model = createTextModel('Hello,\nهناك حقيقة مثبتة منذ زمن طويل'); assert.equal(model.mightContainRTL(), true); model.setValue('hello world!'); assert.equal(model.mightContainRTL(), false); @@ -1010,14 +1010,14 @@ suite('TextModel.mightContainRTL', () => { suite('TextModel.createSnapshot', () => { test('empty file', () => { - let model = TextModel.createFromString(''); + let model = createTextModel(''); let snapshot = model.createSnapshot(); assert.equal(snapshot.read(), null); model.dispose(); }); test('file with BOM', () => { - let model = TextModel.createFromString(UTF8_BOM_CHARACTER + 'Hello'); + let model = createTextModel(UTF8_BOM_CHARACTER + 'Hello'); assert.equal(model.getLineContent(1), 'Hello'); let snapshot = model.createSnapshot(true); assert.equal(snapshot.read(), UTF8_BOM_CHARACTER + 'Hello'); @@ -1026,7 +1026,7 @@ suite('TextModel.createSnapshot', () => { }); test('regular file', () => { - let model = TextModel.createFromString('My First Line\n\t\tMy Second Line\n Third Line\n\n1'); + let model = createTextModel('My First Line\n\t\tMy Second Line\n Third Line\n\n1'); let snapshot = model.createSnapshot(); assert.equal(snapshot.read(), 'My First Line\n\t\tMy Second Line\n Third Line\n\n1'); assert.equal(snapshot.read(), null); @@ -1040,7 +1040,7 @@ suite('TextModel.createSnapshot', () => { } const text = lines.join('\n'); - let model = TextModel.createFromString(text); + let model = createTextModel(text); let snapshot = model.createSnapshot(); let actual = ''; diff --git a/src/vs/editor/test/common/model/textModelSearch.test.ts b/src/vs/editor/test/common/model/textModelSearch.test.ts index 2a55d718b50..e5af37e781b 100644 --- a/src/vs/editor/test/common/model/textModelSearch.test.ts +++ b/src/vs/editor/test/common/model/textModelSearch.test.ts @@ -11,6 +11,7 @@ import { EndOfLineSequence, FindMatch } from 'vs/editor/common/model'; import { TextModel } from 'vs/editor/common/model/textModel'; import { SearchData, SearchParams, TextModelSearch, isMultilineRegexSource } from 'vs/editor/common/model/textModelSearch'; import { USUAL_WORD_SEPARATORS } from 'vs/editor/common/model/wordHelper'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; // --------- Find suite('TextModelSearch', () => { @@ -51,12 +52,12 @@ suite('TextModelSearch', () => { let expectedMatches = expectedRanges.map(entry => new FindMatch(entry, null)); let searchParams = new SearchParams(searchString, isRegex, matchCase, wordSeparators); - let model = TextModel.createFromString(text); + let model = createTextModel(text); _assertFindMatches(model, searchParams, expectedMatches); model.dispose(); - let model2 = TextModel.createFromString(text); + let model2 = createTextModel(text); model2.setEOL(EndOfLineSequence.CRLF); _assertFindMatches(model2, searchParams, expectedMatches); model2.dispose(); @@ -380,7 +381,7 @@ suite('TextModelSearch', () => { }); test('findNextMatch without regex', () => { - let model = TextModel.createFromString('line line one\nline two\nthree'); + let model = createTextModel('line line one\nline two\nthree'); let searchParams = new SearchParams('line', false, false, null); @@ -403,7 +404,7 @@ suite('TextModelSearch', () => { }); test('findNextMatch with beginning boundary regex', () => { - let model = TextModel.createFromString('line one\nline two\nthree'); + let model = createTextModel('line one\nline two\nthree'); let searchParams = new SearchParams('^line', true, false, null); @@ -423,7 +424,7 @@ suite('TextModelSearch', () => { }); test('findNextMatch with beginning boundary regex and line has repetitive beginnings', () => { - let model = TextModel.createFromString('line line one\nline two\nthree'); + let model = createTextModel('line line one\nline two\nthree'); let searchParams = new SearchParams('^line', true, false, null); @@ -443,7 +444,7 @@ suite('TextModelSearch', () => { }); test('findNextMatch with beginning boundary multiline regex and line has repetitive beginnings', () => { - let model = TextModel.createFromString('line line one\nline two\nline three\nline four'); + let model = createTextModel('line line one\nline two\nline three\nline four'); let searchParams = new SearchParams('^line.*\\nline', true, false, null); @@ -460,7 +461,7 @@ suite('TextModelSearch', () => { }); test('findNextMatch with ending boundary regex', () => { - let model = TextModel.createFromString('one line line\ntwo line\nthree'); + let model = createTextModel('one line line\ntwo line\nthree'); let searchParams = new SearchParams('line$', true, false, null); @@ -480,7 +481,7 @@ suite('TextModelSearch', () => { }); test('findMatches with capturing matches', () => { - let model = TextModel.createFromString('one line line\ntwo line\nthree'); + let model = createTextModel('one line line\ntwo line\nthree'); let searchParams = new SearchParams('(l(in)e)', true, false, null); @@ -495,7 +496,7 @@ suite('TextModelSearch', () => { }); test('findMatches multiline with capturing matches', () => { - let model = TextModel.createFromString('one line line\ntwo line\nthree'); + let model = createTextModel('one line line\ntwo line\nthree'); let searchParams = new SearchParams('(l(in)e)\\n', true, false, null); @@ -509,7 +510,7 @@ suite('TextModelSearch', () => { }); test('findNextMatch with capturing matches', () => { - let model = TextModel.createFromString('one line line\ntwo line\nthree'); + let model = createTextModel('one line line\ntwo line\nthree'); let searchParams = new SearchParams('(l(in)e)', true, false, null); @@ -520,7 +521,7 @@ suite('TextModelSearch', () => { }); test('findNextMatch multiline with capturing matches', () => { - let model = TextModel.createFromString('one line line\ntwo line\nthree'); + let model = createTextModel('one line line\ntwo line\nthree'); let searchParams = new SearchParams('(l(in)e)\\n', true, false, null); @@ -531,7 +532,7 @@ suite('TextModelSearch', () => { }); test('findPreviousMatch with capturing matches', () => { - let model = TextModel.createFromString('one line line\ntwo line\nthree'); + let model = createTextModel('one line line\ntwo line\nthree'); let searchParams = new SearchParams('(l(in)e)', true, false, null); @@ -542,7 +543,7 @@ suite('TextModelSearch', () => { }); test('findPreviousMatch multiline with capturing matches', () => { - let model = TextModel.createFromString('one line line\ntwo line\nthree'); + let model = createTextModel('one line line\ntwo line\nthree'); let searchParams = new SearchParams('(l(in)e)\\n', true, false, null); @@ -553,7 +554,7 @@ suite('TextModelSearch', () => { }); test('\\n matches \\r\\n', () => { - let model = TextModel.createFromString('a\r\nb\r\nc\r\nd\r\ne\r\nf\r\ng\r\nh\r\ni'); + let model = createTextModel('a\r\nb\r\nc\r\nd\r\ne\r\nf\r\ng\r\nh\r\ni'); assert.equal(model.getEOL(), '\r\n'); @@ -576,7 +577,7 @@ suite('TextModelSearch', () => { }); test('\\r can never be found', () => { - let model = TextModel.createFromString('a\r\nb\r\nc\r\nd\r\ne\r\nf\r\ng\r\nh\r\ni'); + let model = createTextModel('a\r\nb\r\nc\r\nd\r\ne\r\nf\r\ng\r\nh\r\ni'); assert.equal(model.getEOL(), '\r\n'); @@ -763,7 +764,7 @@ suite('TextModelSearch', () => { }); test('issue #74715. \\d* finds empty string and stops searching.', () => { - let model = TextModel.createFromString('10.243.30.10'); + let model = createTextModel('10.243.30.10'); let searchParams = new SearchParams('\\d*', true, false, null); diff --git a/src/vs/editor/test/common/model/textModelWithTokens.test.ts b/src/vs/editor/test/common/model/textModelWithTokens.test.ts index 09353818317..815c8a073e4 100644 --- a/src/vs/editor/test/common/model/textModelWithTokens.test.ts +++ b/src/vs/editor/test/common/model/textModelWithTokens.test.ts @@ -15,6 +15,7 @@ import { CharacterPair } from 'vs/editor/common/modes/languageConfiguration'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { NULL_STATE } from 'vs/editor/common/modes/nullMode'; import { ViewLineToken } from 'vs/editor/test/common/core/viewLineToken'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; suite('TextModelWithTokens', () => { @@ -72,7 +73,7 @@ suite('TextModelWithTokens', () => { brackets: brackets }); - let model = TextModel.createFromString( + let model = createTextModel( contents.join('\n'), TextModel.DEFAULT_CREATION_OPTIONS, languageIdentifier @@ -178,7 +179,7 @@ suite('TextModelWithTokens - bracket matching', () => { let text = ')]}{[(' + '\n' + ')]}{[('; - let model = TextModel.createFromString(text, undefined, languageIdentifier); + let model = createTextModel(text, undefined, languageIdentifier); assertIsNotBracket(model, 1, 1); assertIsNotBracket(model, 1, 2); @@ -206,7 +207,7 @@ suite('TextModelWithTokens - bracket matching', () => { '}, bar: {hallo: [{' + '\n' + '}, {' + '\n' + '}]}}'; - let model = TextModel.createFromString(text, undefined, languageIdentifier); + let model = createTextModel(text, undefined, languageIdentifier); let brackets: [Position, Range, Range][] = [ [new Position(1, 11), new Range(1, 11, 1, 12), new Range(5, 4, 5, 5)], @@ -284,7 +285,7 @@ suite('TextModelWithTokens', () => { 'end;', ].join('\n'); - const model = TextModel.createFromString(text, undefined, languageIdentifier); + const model = createTextModel(text, undefined, languageIdentifier); // ... is not matched assertIsNotBracket(model, 10, 9); @@ -322,7 +323,7 @@ suite('TextModelWithTokens', () => { 'endrecord', ].join('\n'); - const model = TextModel.createFromString(text, undefined, languageIdentifier); + const model = createTextModel(text, undefined, languageIdentifier); // ... is matched assertIsBracket(model, new Position(1, 1), [new Range(1, 1, 1, 12), new Range(4, 1, 4, 10)]); @@ -388,7 +389,7 @@ suite('TextModelWithTokens', () => { ], }); - const model = TextModel.createFromString([ + const model = createTextModel([ 'function hello() {', ' console.log(`${100}`);', '}' @@ -459,7 +460,7 @@ suite('TextModelWithTokens regression tests', () => { let registration1 = TokenizationRegistry.register(LANG_ID1, tokenizationSupport); let registration2 = TokenizationRegistry.register(LANG_ID2, tokenizationSupport); - let model = TextModel.createFromString('A model with\ntwo lines'); + let model = createTextModel('A model with\ntwo lines'); assertViewLineTokens(model, 1, true, [createViewLineToken(12, 1)]); assertViewLineTokens(model, 2, true, [createViewLineToken(9, 1)]); @@ -498,7 +499,7 @@ suite('TextModelWithTokens regression tests', () => { ] }); - let model = TextModel.createFromString([ + let model = createTextModel([ 'Imports System', 'Imports System.Collections.Generic', '', @@ -528,7 +529,7 @@ suite('TextModelWithTokens regression tests', () => { ] }); - let model = TextModel.createFromString([ + let model = createTextModel([ 'sequence "outer"', ' sequence "inner"', ' endsequence', @@ -561,7 +562,7 @@ suite('TextModelWithTokens regression tests', () => { let registration = TokenizationRegistry.register(outerMode.language, tokenizationSupport); - let model = TextModel.createFromString('A model with one line', undefined, outerMode); + let model = createTextModel('A model with one line', undefined, outerMode); model.forceTokenization(1); assert.equal(model.getLanguageIdAtPosition(1, 1), innerMode.id); @@ -574,7 +575,7 @@ suite('TextModelWithTokens regression tests', () => { suite('TextModel.getLineIndentGuide', () => { function assertIndentGuides(lines: [number, number, number, number, string][], tabSize: number): void { let text = lines.map(l => l[4]).join('\n'); - let model = TextModel.createFromString(text); + let model = createTextModel(text); model.updateOptions({ tabSize: tabSize }); let actualIndents = model.getLinesIndentGuides(1, model.getLineCount()); @@ -747,7 +748,7 @@ suite('TextModel.getLineIndentGuide', () => { }); test('issue #49173', () => { - let model = TextModel.createFromString([ + let model = createTextModel([ 'class A {', ' public m1(): void {', ' }', diff --git a/src/vs/editor/test/common/model/tokensStore.test.ts b/src/vs/editor/test/common/model/tokensStore.test.ts index eb81aa1570b..51d85e382f8 100644 --- a/src/vs/editor/test/common/model/tokensStore.test.ts +++ b/src/vs/editor/test/common/model/tokensStore.test.ts @@ -9,6 +9,7 @@ import { Range } from 'vs/editor/common/core/range'; import { TextModel } from 'vs/editor/common/model/textModel'; import { IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; import { MetadataConsts, TokenMetadata } from 'vs/editor/common/modes'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; suite('TokensStore', () => { @@ -96,7 +97,7 @@ suite('TokensStore', () => { function testTokensAdjustment(rawInitialState: string[], edits: IIdentifiedSingleEditOperation[], rawFinalState: string[]) { const initialState = parseTokensState(rawInitialState); - const model = TextModel.createFromString(initialState.text); + const model = createTextModel(initialState.text); model.setSemanticTokens([initialState.tokens]); model.applyEdits(edits); diff --git a/src/vs/editor/test/common/services/modelService.test.ts b/src/vs/editor/test/common/services/modelService.test.ts index 72ec78849d8..84ebc8f2e0e 100644 --- a/src/vs/editor/test/common/services/modelService.test.ts +++ b/src/vs/editor/test/common/services/modelService.test.ts @@ -11,7 +11,7 @@ import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Range } from 'vs/editor/common/core/range'; import { createStringBuilder } from 'vs/editor/common/core/stringBuilder'; import { DefaultEndOfLine } from 'vs/editor/common/model'; -import { TextModel, createTextBuffer } from 'vs/editor/common/model/textModel'; +import { createTextBuffer } from 'vs/editor/common/model/textModel'; import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; import { ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfigurationService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -21,6 +21,7 @@ import { NullLogService } from 'vs/platform/log/common/log'; import { UndoRedoService } from 'vs/platform/undoRedo/common/undoRedoService'; import { TestDialogService } from 'vs/platform/dialogs/test/common/testDialogService'; import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; const GENERATE_TESTS = false; @@ -51,7 +52,7 @@ suite('ModelService', () => { test('_computeEdits no change', function () { - const model = TextModel.createFromString( + const model = createTextModel( [ 'This is line one', //16 'and this is line number two', //27 @@ -77,7 +78,7 @@ suite('ModelService', () => { test('_computeEdits first line changed', function () { - const model = TextModel.createFromString( + const model = createTextModel( [ 'This is line one', //16 'and this is line number two', //27 @@ -105,7 +106,7 @@ suite('ModelService', () => { test('_computeEdits EOL changed', function () { - const model = TextModel.createFromString( + const model = createTextModel( [ 'This is line one', //16 'and this is line number two', //27 @@ -131,7 +132,7 @@ suite('ModelService', () => { test('_computeEdits EOL and other change 1', function () { - const model = TextModel.createFromString( + const model = createTextModel( [ 'This is line one', //16 'and this is line number two', //27 @@ -167,7 +168,7 @@ suite('ModelService', () => { test('_computeEdits EOL and other change 2', function () { - const model = TextModel.createFromString( + const model = createTextModel( [ 'package main', // 1 'func foo() {', // 2 @@ -309,7 +310,7 @@ suite('ModelService', () => { }); function assertComputeEdits(lines1: string[], lines2: string[]): void { - const model = TextModel.createFromString(lines1.join('\n')); + const model = createTextModel(lines1.join('\n')); const textBuffer = createTextBuffer(lines2.join('\n'), DefaultEndOfLine.LF); // compute required edits diff --git a/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts b/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts index 393d6997976..2c5cca1cb71 100644 --- a/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts +++ b/src/vs/editor/test/common/viewModel/splitLinesCollection.test.ts @@ -18,6 +18,7 @@ import { LineBreakData, ISimpleModel, SplitLine, SplitLinesCollection } from 'vs import { ViewLineData } from 'vs/editor/common/viewModel/viewModel'; import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; suite('Editor ViewModel - SplitLinesCollection', () => { test('SplitLine', () => { @@ -96,7 +97,7 @@ suite('Editor ViewModel - SplitLinesCollection', () => { const lineBreaksComputerFactory = new MonospaceLineBreaksComputerFactory(wordWrapBreakBeforeCharacters, wordWrapBreakAfterCharacters); - const model = TextModel.createFromString([ + const model = createTextModel([ 'int main() {', '\tprintf("Hello world!");', '}', @@ -347,7 +348,7 @@ suite('SplitLinesCollection', () => { }; const LANGUAGE_ID = 'modelModeTest1'; languageRegistration = modes.TokenizationRegistry.register(LANGUAGE_ID, tokenizationSupport); - model = TextModel.createFromString(_text.join('\n'), undefined, new modes.LanguageIdentifier(LANGUAGE_ID, 0)); + model = createTextModel(_text.join('\n'), undefined, new modes.LanguageIdentifier(LANGUAGE_ID, 0)); // force tokenization model.forceTokenization(model.getLineCount()); }); diff --git a/src/vs/editor/test/common/viewModel/testViewModel.ts b/src/vs/editor/test/common/viewModel/testViewModel.ts index b316d88b004..38f1fc586f1 100644 --- a/src/vs/editor/test/common/viewModel/testViewModel.ts +++ b/src/vs/editor/test/common/viewModel/testViewModel.ts @@ -8,12 +8,13 @@ import { TextModel } from 'vs/editor/common/model/textModel'; import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl'; import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration'; import { MonospaceLineBreaksComputerFactory } from 'vs/editor/common/viewModel/monospaceLineBreaksComputer'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; export function testViewModel(text: string[], options: IEditorOptions, callback: (viewModel: ViewModel, model: TextModel) => void): void { const EDITOR_ID = 1; const configuration = new TestConfiguration(options); - const model = TextModel.createFromString(text.join('\n')); + const model = createTextModel(text.join('\n')); const monospaceLineBreaksComputerFactory = MonospaceLineBreaksComputerFactory.create(configuration.options); const viewModel = new ViewModel(EDITOR_ID, configuration, model, monospaceLineBreaksComputerFactory, monospaceLineBreaksComputerFactory, null!); diff --git a/src/vs/workbench/contrib/debug/test/browser/breakpoints.test.ts b/src/vs/workbench/contrib/debug/test/browser/breakpoints.test.ts index d6ad9e7fbf2..83b69ec0ed7 100644 --- a/src/vs/workbench/contrib/debug/test/browser/breakpoints.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/breakpoints.test.ts @@ -17,6 +17,7 @@ import { LanguageIdentifier, LanguageId } from 'vs/editor/common/modes'; import { createBreakpointDecorations } from 'vs/workbench/contrib/debug/browser/breakpointEditorContribution'; import { OverviewRulerLane } from 'vs/editor/common/model'; import { MarkdownString } from 'vs/base/common/htmlContent'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; function createMockSession(model: DebugModel, name = 'mockSession', options?: IDebugSessionOptions): DebugSession { return new DebugSession({ resolved: { name, type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, options, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, NullOpenerService, undefined!, undefined!); @@ -319,7 +320,7 @@ suite('Debug - Breakpoints', () => { test('decorations', () => { const modelUri = uri.file('/myfolder/my file first.js'); const languageIdentifier = new LanguageIdentifier('testMode', LanguageId.PlainText); - const textModel = TextModel.createFromString( + const textModel = createTextModel( ['this is line one', 'this is line two', ' this is line three it has whitespace at start', 'this is line four', 'this is line five'].join('\n'), TextModel.DEFAULT_CREATION_OPTIONS, languageIdentifier diff --git a/src/vs/workbench/contrib/preferences/test/common/smartSnippetInserter.test.ts b/src/vs/workbench/contrib/preferences/test/common/smartSnippetInserter.test.ts index 90a2e3a7bbb..16c83919b14 100644 --- a/src/vs/workbench/contrib/preferences/test/common/smartSnippetInserter.test.ts +++ b/src/vs/workbench/contrib/preferences/test/common/smartSnippetInserter.test.ts @@ -5,13 +5,13 @@ import * as assert from 'assert'; import { SmartSnippetInserter } from 'vs/workbench/contrib/preferences/common/smartSnippetInserter'; -import { TextModel } from 'vs/editor/common/model/textModel'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; import { Position } from 'vs/editor/common/core/position'; suite('SmartSnippetInserter', () => { function testSmartSnippetInserter(text: string[], runner: (assert: (desiredPos: Position, pos: Position, prepend: string, append: string) => void) => void): void { - let model = TextModel.createFromString(text.join('\n')); + let model = createTextModel(text.join('\n')); runner((desiredPos, pos, prepend, append) => { let actual = SmartSnippetInserter.insertSnippet(model, desiredPos); let expected = { diff --git a/src/vs/workbench/contrib/snippets/test/browser/snippetsService.test.ts b/src/vs/workbench/contrib/snippets/test/browser/snippetsService.test.ts index f37af65df09..70d7065de59 100644 --- a/src/vs/workbench/contrib/snippets/test/browser/snippetsService.test.ts +++ b/src/vs/workbench/contrib/snippets/test/browser/snippetsService.test.ts @@ -8,7 +8,7 @@ import { SnippetCompletionProvider } from 'vs/workbench/contrib/snippets/browser import { Position } from 'vs/editor/common/core/position'; import { ModesRegistry } from 'vs/editor/common/modes/modesRegistry'; import { ModeServiceImpl } from 'vs/editor/common/services/modeServiceImpl'; -import { TextModel } from 'vs/editor/common/model/textModel'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; import { ISnippetsService } from 'vs/workbench/contrib/snippets/browser/snippets.contribution'; import { Snippet, SnippetSource } from 'vs/workbench/contrib/snippets/browser/snippetsFile'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; @@ -67,7 +67,7 @@ suite('SnippetsService', function () { test('snippet completions - simple', function () { const provider = new SnippetCompletionProvider(modeService, snippetService); - const model = TextModel.createFromString('', undefined, modeService.getLanguageIdentifier('fooLang')); + const model = createTextModel('', undefined, modeService.getLanguageIdentifier('fooLang')); return provider.provideCompletionItems(model, new Position(1, 1), context)!.then(result => { assert.equal(result.incomplete, undefined); @@ -78,7 +78,7 @@ suite('SnippetsService', function () { test('snippet completions - with prefix', function () { const provider = new SnippetCompletionProvider(modeService, snippetService); - const model = TextModel.createFromString('bar', undefined, modeService.getLanguageIdentifier('fooLang')); + const model = createTextModel('bar', undefined, modeService.getLanguageIdentifier('fooLang')); return provider.provideCompletionItems(model, new Position(1, 4), context)!.then(result => { assert.equal(result.incomplete, undefined); @@ -113,7 +113,7 @@ suite('SnippetsService', function () { )]); const provider = new SnippetCompletionProvider(modeService, snippetService); - const model = TextModel.createFromString('bar-bar', undefined, modeService.getLanguageIdentifier('fooLang')); + const model = createTextModel('bar-bar', undefined, modeService.getLanguageIdentifier('fooLang')); await provider.provideCompletionItems(model, new Position(1, 3), context)!.then(result => { assert.equal(result.incomplete, undefined); @@ -174,19 +174,19 @@ suite('SnippetsService', function () { const provider = new SnippetCompletionProvider(modeService, snippetService); - let model = TextModel.createFromString('\t { assert.equal(result.suggestions.length, 1); model.dispose(); - model = TextModel.createFromString('\t { assert.equal(result.suggestions.length, 1); assert.equal((result.suggestions[0].range as any).insert.startColumn, 2); model.dispose(); - model = TextModel.createFromString('a { assert.equal(result.suggestions.length, 1); @@ -209,7 +209,7 @@ suite('SnippetsService', function () { const provider = new SnippetCompletionProvider(modeService, snippetService); - let model = TextModel.createFromString('\n\t\n>/head>', undefined, modeService.getLanguageIdentifier('fooLang')); + let model = createTextModel('\n\t\n>/head>', undefined, modeService.getLanguageIdentifier('fooLang')); return provider.provideCompletionItems(model, new Position(1, 1), context)!.then(result => { assert.equal(result.suggestions.length, 1); return provider.provideCompletionItems(model, new Position(2, 2), context)!; @@ -239,7 +239,7 @@ suite('SnippetsService', function () { const provider = new SnippetCompletionProvider(modeService, snippetService); - let model = TextModel.createFromString('', undefined, modeService.getLanguageIdentifier('fooLang')); + let model = createTextModel('', undefined, modeService.getLanguageIdentifier('fooLang')); return provider.provideCompletionItems(model, new Position(1, 1), context)!.then(result => { assert.equal(result.suggestions.length, 2); let [first, second] = result.suggestions; @@ -266,7 +266,7 @@ suite('SnippetsService', function () { )]); const provider = new SnippetCompletionProvider(modeService, snippetService); - let model = TextModel.createFromString('p-', undefined, modeService.getLanguageIdentifier('fooLang')); + let model = createTextModel('p-', undefined, modeService.getLanguageIdentifier('fooLang')); let result = await provider.provideCompletionItems(model, new Position(1, 2), context)!; assert.equal(result.suggestions.length, 1); @@ -291,7 +291,7 @@ suite('SnippetsService', function () { const provider = new SnippetCompletionProvider(modeService, snippetService); - let model = TextModel.createFromString('Thisisaverylonglinegoingwithmore100bcharactersandthismakesintellisensebecomea Thisisaverylonglinegoingwithmore100bcharactersandthismakesintellisensebecomea b', undefined, modeService.getLanguageIdentifier('fooLang')); + let model = createTextModel('Thisisaverylonglinegoingwithmore100bcharactersandthismakesintellisensebecomea Thisisaverylonglinegoingwithmore100bcharactersandthismakesintellisensebecomea b', undefined, modeService.getLanguageIdentifier('fooLang')); let result = await provider.provideCompletionItems(model, new Position(1, 158), context)!; assert.equal(result.suggestions.length, 1); @@ -310,7 +310,7 @@ suite('SnippetsService', function () { const provider = new SnippetCompletionProvider(modeService, snippetService); - let model = TextModel.createFromString(':', undefined, modeService.getLanguageIdentifier('fooLang')); + let model = createTextModel(':', undefined, modeService.getLanguageIdentifier('fooLang')); let result = await provider.provideCompletionItems(model, new Position(1, 2), context)!; assert.equal(result.suggestions.length, 0); @@ -329,7 +329,7 @@ suite('SnippetsService', function () { const provider = new SnippetCompletionProvider(modeService, snippetService); - let model = TextModel.createFromString('template', undefined, modeService.getLanguageIdentifier('fooLang')); + let model = createTextModel('template', undefined, modeService.getLanguageIdentifier('fooLang')); let result = await provider.provideCompletionItems(model, new Position(1, 9), context)!; assert.equal(result.suggestions.length, 1); @@ -352,7 +352,7 @@ suite('SnippetsService', function () { const provider = new SnippetCompletionProvider(modeService, snippetService); - let model = TextModel.createFromString('Thisisaverylonglinegoingwithmore100bcharactersandthismakesintellisensebecomea Thisisaverylonglinegoingwithmore100bcharactersandthismakesintellisensebecomea b text_after_b', undefined, modeService.getLanguageIdentifier('fooLang')); + let model = createTextModel('Thisisaverylonglinegoingwithmore100bcharactersandthismakesintellisensebecomea Thisisaverylonglinegoingwithmore100bcharactersandthismakesintellisensebecomea b text_after_b', undefined, modeService.getLanguageIdentifier('fooLang')); let result = await provider.provideCompletionItems(model, new Position(1, 158), context)!; assert.equal(result.suggestions.length, 1); @@ -374,7 +374,7 @@ suite('SnippetsService', function () { const provider = new SnippetCompletionProvider(modeService, snippetService); - let model = TextModel.createFromString('.🐷-a-b', undefined, modeService.getLanguageIdentifier('fooLang')); + let model = createTextModel('.🐷-a-b', undefined, modeService.getLanguageIdentifier('fooLang')); let result = await provider.provideCompletionItems(model, new Position(1, 8), context)!; assert.equal(result.suggestions.length, 1); @@ -395,7 +395,7 @@ suite('SnippetsService', function () { const provider = new SnippetCompletionProvider(modeService, snippetService); - let model = TextModel.createFromString('a ', undefined, modeService.getLanguageIdentifier('fooLang')); + let model = createTextModel('a ', undefined, modeService.getLanguageIdentifier('fooLang')); let result = await provider.provideCompletionItems(model, new Position(1, 3), context)!; assert.equal(result.suggestions.length, 1); @@ -422,14 +422,14 @@ suite('SnippetsService', function () { const provider = new SnippetCompletionProvider(modeService, snippetService); - let model = TextModel.createFromString(' <', undefined, modeService.getLanguageIdentifier('fooLang')); + let model = createTextModel(' <', undefined, modeService.getLanguageIdentifier('fooLang')); let result = await provider.provideCompletionItems(model, new Position(1, 3), context)!; assert.equal(result.suggestions.length, 1); let [first] = result.suggestions; assert.equal((first.range as any).insert.startColumn, 2); - model = TextModel.createFromString('1', undefined, modeService.getLanguageIdentifier('fooLang')); + model = createTextModel('1', undefined, modeService.getLanguageIdentifier('fooLang')); result = await provider.provideCompletionItems(model, new Position(1, 2), context)!; assert.equal(result.suggestions.length, 1); @@ -450,7 +450,7 @@ suite('SnippetsService', function () { const provider = new SnippetCompletionProvider(modeService, snippetService); - let model = TextModel.createFromString('not wordFoo bar', undefined, modeService.getLanguageIdentifier('fooLang')); + let model = createTextModel('not wordFoo bar', undefined, modeService.getLanguageIdentifier('fooLang')); let result = await provider.provideCompletionItems(model, new Position(1, 3), context)!; assert.equal(result.suggestions.length, 1); @@ -458,7 +458,7 @@ suite('SnippetsService', function () { assert.equal((first.range as any).insert.endColumn, 3); assert.equal((first.range as any).replace.endColumn, 9); - model = TextModel.createFromString('not woFoo bar', undefined, modeService.getLanguageIdentifier('fooLang')); + model = createTextModel('not woFoo bar', undefined, modeService.getLanguageIdentifier('fooLang')); result = await provider.provideCompletionItems(model, new Position(1, 3), context)!; assert.equal(result.suggestions.length, 1); @@ -466,7 +466,7 @@ suite('SnippetsService', function () { assert.equal((first.range as any).insert.endColumn, 3); assert.equal((first.range as any).replace.endColumn, 3); - model = TextModel.createFromString('not word', undefined, modeService.getLanguageIdentifier('fooLang')); + model = createTextModel('not word', undefined, modeService.getLanguageIdentifier('fooLang')); result = await provider.provideCompletionItems(model, new Position(1, 1), context)!; assert.equal(result.suggestions.length, 1); diff --git a/src/vs/workbench/services/backup/test/electron-browser/backupFileService.test.ts b/src/vs/workbench/services/backup/test/electron-browser/backupFileService.test.ts index 77e239607a4..1edea0078b3 100644 --- a/src/vs/workbench/services/backup/test/electron-browser/backupFileService.test.ts +++ b/src/vs/workbench/services/backup/test/electron-browser/backupFileService.test.ts @@ -12,7 +12,8 @@ import * as path from 'vs/base/common/path'; import * as pfs from 'vs/base/node/pfs'; import { URI } from 'vs/base/common/uri'; import { BackupFilesModel } from 'vs/workbench/services/backup/common/backupFileService'; -import { TextModel, createTextBufferFactory } from 'vs/editor/common/model/textModel'; +import { createTextBufferFactory } from 'vs/editor/common/model/textModel'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; import { getRandomTestPath } from 'vs/base/test/node/testUtils'; import { DefaultEndOfLine, ITextSnapshot } from 'vs/editor/common/model'; import { Schemas } from 'vs/base/common/network'; @@ -205,7 +206,7 @@ suite('BackupFileService', () => { }); test('text file (ITextSnapshot)', async () => { - const model = TextModel.createFromString('test'); + const model = createTextModel('test'); await service.backup(fooFile, model.createSnapshot()); assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1); @@ -217,7 +218,7 @@ suite('BackupFileService', () => { }); test('untitled file (ITextSnapshot)', async () => { - const model = TextModel.createFromString('test'); + const model = createTextModel('test'); await service.backup(untitledFile, model.createSnapshot()); assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 1); @@ -229,7 +230,7 @@ suite('BackupFileService', () => { test('text file (large file, ITextSnapshot)', async () => { const largeString = (new Array(10 * 1024)).join('Large String\n'); - const model = TextModel.createFromString(largeString); + const model = createTextModel(largeString); await service.backup(fooFile, model.createSnapshot()); assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1); @@ -242,7 +243,7 @@ suite('BackupFileService', () => { test('untitled file (large file, ITextSnapshot)', async () => { const largeString = (new Array(10 * 1024)).join('Large String\n'); - const model = TextModel.createFromString(largeString); + const model = createTextModel(largeString); await service.backup(untitledFile, model.createSnapshot()); assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 1); diff --git a/src/vs/workbench/services/textfile/test/electron-browser/textFileService.io.test.ts b/src/vs/workbench/services/textfile/test/electron-browser/textFileService.io.test.ts index 22504c3b467..7be9d0e1c6d 100644 --- a/src/vs/workbench/services/textfile/test/electron-browser/textFileService.io.test.ts +++ b/src/vs/workbench/services/textfile/test/electron-browser/textFileService.io.test.ts @@ -22,7 +22,7 @@ import { join, basename } from 'vs/base/common/path'; import { getPathFromAmdModule } from 'vs/base/common/amd'; import { UTF16be, UTF16le, UTF8_with_bom, UTF8 } from 'vs/base/node/encoding'; import { DefaultEndOfLine, ITextSnapshot } from 'vs/editor/common/model'; -import { TextModel } from 'vs/editor/common/model/textModel'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; import { isWindows } from 'vs/base/common/platform'; import { readFileSync, statSync } from 'fs'; import { detectEncodingByBOM } from 'vs/base/test/node/encoding/encoding.test'; @@ -152,7 +152,7 @@ suite('Files - TextFileService i/o', () => { test('create - UTF 8 BOM - empty content - snapshot', async () => { const resource = URI.file(join(testDir, 'small_new.utf8bom')); - await service.create(resource, TextModel.createFromString('').createSnapshot()); + await service.create(resource, createTextModel('').createSnapshot()); assert.equal(await exists(resource.fsPath), true); @@ -163,7 +163,7 @@ suite('Files - TextFileService i/o', () => { test('create - UTF 8 BOM - content provided - snapshot', async () => { const resource = URI.file(join(testDir, 'small_new.utf8bom')); - await service.create(resource, TextModel.createFromString('Hello World').createSnapshot()); + await service.create(resource, createTextModel('Hello World').createSnapshot()); assert.equal(await exists(resource.fsPath), true); @@ -176,7 +176,7 @@ suite('Files - TextFileService i/o', () => { }); test('write - use encoding (UTF 16 BE) - small content as snapshot', async () => { - await testEncoding(URI.file(join(testDir, 'small.txt')), UTF16be, TextModel.createFromString('Hello\nWorld').createSnapshot(), 'Hello\nWorld'); + await testEncoding(URI.file(join(testDir, 'small.txt')), UTF16be, createTextModel('Hello\nWorld').createSnapshot(), 'Hello\nWorld'); }); test('write - use encoding (UTF 16 BE) - large content as string', async () => { @@ -184,7 +184,7 @@ suite('Files - TextFileService i/o', () => { }); test('write - use encoding (UTF 16 BE) - large content as snapshot', async () => { - await testEncoding(URI.file(join(testDir, 'lorem.txt')), UTF16be, TextModel.createFromString('Hello\nWorld').createSnapshot(), 'Hello\nWorld'); + await testEncoding(URI.file(join(testDir, 'lorem.txt')), UTF16be, createTextModel('Hello\nWorld').createSnapshot(), 'Hello\nWorld'); }); async function testEncoding(resource: URI, encoding: string, content: string | ITextSnapshot, expectedContent: string) { @@ -232,7 +232,7 @@ suite('Files - TextFileService i/o', () => { resolved = await service.readStream(resource, { encoding }); assert.equal(snapshotToString(resolved.value.create(DefaultEndOfLine.CRLF).createSnapshot(false)), content); - await service.write(resource, TextModel.createFromString(content).createSnapshot(), { encoding }); + await service.write(resource, createTextModel(content).createSnapshot(), { encoding }); resolved = await service.readStream(resource, { encoding }); assert.equal(snapshotToString(resolved.value.create(DefaultEndOfLine.CRLF).createSnapshot(false)), content); @@ -254,7 +254,7 @@ suite('Files - TextFileService i/o', () => { const content = (await readFile(resource.fsPath)).toString(); - await service.write(resource, TextModel.createFromString(content).createSnapshot()); + await service.write(resource, createTextModel(content).createSnapshot()); const resolved = await service.readStream(resource); assert.equal(resolved.value.getFirstLineText(999999), content); @@ -275,7 +275,7 @@ suite('Files - TextFileService i/o', () => { const resolved = await service.readStream(resource); assert.equal(resolved.encoding, UTF16le); - await testEncoding(URI.file(join(testDir, 'some_utf16le.css')), UTF16le, TextModel.createFromString('Hello\nWorld').createSnapshot(), 'Hello\nWorld'); + await testEncoding(URI.file(join(testDir, 'some_utf16le.css')), UTF16le, createTextModel('Hello\nWorld').createSnapshot(), 'Hello\nWorld'); }); test('write - UTF8 variations - content as string', async () => { @@ -312,7 +312,7 @@ suite('Files - TextFileService i/o', () => { let detectedEncoding = await detectEncodingByBOM(resource.fsPath); assert.equal(detectedEncoding, null); - const model = TextModel.createFromString((await readFile(resource.fsPath)).toString() + 'updates'); + const model = createTextModel((await readFile(resource.fsPath)).toString() + 'updates'); await service.write(resource, model.createSnapshot(), { encoding: UTF8_with_bom }); detectedEncoding = await detectEncodingByBOM(resource.fsPath); @@ -357,7 +357,7 @@ suite('Files - TextFileService i/o', () => { test('write - ensure BOM in empty file - content as snapshot', async () => { const resource = URI.file(join(testDir, 'small.txt')); - await service.write(resource, TextModel.createFromString('').createSnapshot(), { encoding: UTF8_with_bom }); + await service.write(resource, createTextModel('').createSnapshot(), { encoding: UTF8_with_bom }); let detectedEncoding = await detectEncodingByBOM(resource.fsPath); assert.equal(detectedEncoding, UTF8_with_bom); @@ -380,7 +380,7 @@ suite('Files - TextFileService i/o', () => { assert.equal(result.name, basename(resource.fsPath)); assert.equal(result.size, statSync(resource.fsPath).size); - assert.equal(snapshotToString(result.value.create(DefaultEndOfLine.LF).createSnapshot(false)), snapshotToString(TextModel.createFromString(readFileSync(resource.fsPath).toString()).createSnapshot(false))); + assert.equal(snapshotToString(result.value.create(DefaultEndOfLine.LF).createSnapshot(false)), snapshotToString(createTextModel(readFileSync(resource.fsPath).toString()).createSnapshot(false))); } test('read - small text', async () => { diff --git a/src/vs/workbench/test/browser/api/extHostApiCommands.test.ts b/src/vs/workbench/test/browser/api/extHostApiCommands.test.ts index 506745c103a..ccdf0740f5b 100644 --- a/src/vs/workbench/test/browser/api/extHostApiCommands.test.ts +++ b/src/vs/workbench/test/browser/api/extHostApiCommands.test.ts @@ -7,7 +7,7 @@ import * as assert from 'assert'; import { setUnexpectedErrorHandler, errorHandler } from 'vs/base/common/errors'; import { URI } from 'vs/base/common/uri'; import * as types from 'vs/workbench/api/common/extHostTypes'; -import { TextModel } from 'vs/editor/common/model/textModel'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; import { TestRPCProtocol } from './testRPCProtocol'; import { MarkerService } from 'vs/platform/markers/common/markerService'; import { IMarkerService } from 'vs/platform/markers/common/markers'; @@ -49,7 +49,7 @@ import 'vs/editor/contrib/smartSelect/smartSelect'; import 'vs/editor/contrib/suggest/suggest'; const defaultSelector = { scheme: 'far' }; -const model: ITextModel = TextModel.createFromString( +const model: ITextModel = createTextModel( [ 'This is the first line', 'This is the second line', diff --git a/src/vs/workbench/test/browser/api/extHostLanguageFeatures.test.ts b/src/vs/workbench/test/browser/api/extHostLanguageFeatures.test.ts index 28e991cb356..148148e352e 100644 --- a/src/vs/workbench/test/browser/api/extHostLanguageFeatures.test.ts +++ b/src/vs/workbench/test/browser/api/extHostLanguageFeatures.test.ts @@ -8,7 +8,7 @@ import { TestInstantiationService } from 'vs/platform/instantiation/test/common/ import { setUnexpectedErrorHandler, errorHandler } from 'vs/base/common/errors'; import { URI } from 'vs/base/common/uri'; import * as types from 'vs/workbench/api/common/extHostTypes'; -import { TextModel } from 'vs/editor/common/model/textModel'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; import { Position as EditorPosition, Position } from 'vs/editor/common/core/position'; import { Range as EditorRange } from 'vs/editor/common/core/range'; import { TestRPCProtocol } from './testRPCProtocol'; @@ -51,7 +51,7 @@ import { NullApiDeprecationService } from 'vs/workbench/api/common/extHostApiDep import { Progress } from 'vs/platform/progress/common/progress'; const defaultSelector = { scheme: 'far' }; -const model: ITextModel = TextModel.createFromString( +const model: ITextModel = createTextModel( [ 'This is the first line', 'This is the second line', diff --git a/src/vs/workbench/test/browser/api/mainThreadDocumentContentProviders.test.ts b/src/vs/workbench/test/browser/api/mainThreadDocumentContentProviders.test.ts index dfef5fee0f5..234f1163ae4 100644 --- a/src/vs/workbench/test/browser/api/mainThreadDocumentContentProviders.test.ts +++ b/src/vs/workbench/test/browser/api/mainThreadDocumentContentProviders.test.ts @@ -6,7 +6,7 @@ import * as assert from 'assert'; import { URI } from 'vs/base/common/uri'; import { MainThreadDocumentContentProviders } from 'vs/workbench/api/browser/mainThreadDocumentContentProviders'; -import { TextModel } from 'vs/editor/common/model/textModel'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; import { mock } from 'vs/workbench/test/browser/api/mock'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; @@ -18,7 +18,7 @@ suite('MainThreadDocumentContentProviders', function () { test('events are processed properly', function () { let uri = URI.parse('test:uri'); - let model = TextModel.createFromString('1', undefined, undefined, uri); + let model = createTextModel('1', undefined, undefined, uri); let providers = new MainThreadDocumentContentProviders(new TestRPCProtocol(), null!, null!, new class extends mock() { diff --git a/src/vs/workbench/test/browser/api/mainThreadDocuments.test.ts b/src/vs/workbench/test/browser/api/mainThreadDocuments.test.ts index 8ec19753623..a14eca8b02a 100644 --- a/src/vs/workbench/test/browser/api/mainThreadDocuments.test.ts +++ b/src/vs/workbench/test/browser/api/mainThreadDocuments.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import { BoundModelReferenceCollection } from 'vs/workbench/api/browser/mainThreadDocuments'; -import { TextModel } from 'vs/editor/common/model/textModel'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; import { timeout } from 'vs/base/common/async'; suite('BoundModelReferenceCollection', () => { @@ -21,7 +21,7 @@ suite('BoundModelReferenceCollection', () => { let didDispose = false; col.add({ - object: { textEditorModel: TextModel.createFromString('farboo') }, + object: { textEditorModel: createTextModel('farboo') }, dispose() { didDispose = true; } @@ -36,20 +36,20 @@ suite('BoundModelReferenceCollection', () => { let disposed: number[] = []; col.add({ - object: { textEditorModel: TextModel.createFromString('farboo') }, + object: { textEditorModel: createTextModel('farboo') }, dispose() { disposed.push(0); } }); col.add({ - object: { textEditorModel: TextModel.createFromString('boofar') }, + object: { textEditorModel: createTextModel('boofar') }, dispose() { disposed.push(1); } }); col.add({ - object: { textEditorModel: TextModel.createFromString(new Array(71).join('x')) }, + object: { textEditorModel: createTextModel(new Array(71).join('x')) }, dispose() { disposed.push(2); } diff --git a/src/vs/workbench/test/browser/parts/editor/rangeDecorations.test.ts b/src/vs/workbench/test/browser/parts/editor/rangeDecorations.test.ts index ef2555ad3f7..06f12dbcaea 100644 --- a/src/vs/workbench/test/browser/parts/editor/rangeDecorations.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/rangeDecorations.test.ts @@ -21,6 +21,7 @@ import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; import { CoreNavigationCommands } from 'vs/editor/browser/controller/coreCommands'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; suite('Editor - Range decorations', () => { @@ -136,7 +137,7 @@ suite('Editor - Range decorations', () => { } function aModel(resource: URI, content: string = text): TextModel { - let model = TextModel.createFromString(content, TextModel.DEFAULT_CREATION_OPTIONS, null, resource); + let model = createTextModel(content, TextModel.DEFAULT_CREATION_OPTIONS, null, resource); modelsToDispose.push(model); return model; } From 58aa111492425e79fe2e8433ce3f6227b7cd7a86 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 21 Feb 2020 22:24:44 +0100 Subject: [PATCH 045/200] Remove TextModel.createFromString --- src/vs/editor/common/model/textModel.ts | 24 ------------------- .../gotoSymbol/peek/referencesWidget.ts | 6 +++-- src/vs/editor/test/common/editorTestUtils.ts | 8 ++++++- .../contrib/bulkEdit/browser/bulkEditTree.ts | 8 +++++-- 4 files changed, 17 insertions(+), 29 deletions(-) diff --git a/src/vs/editor/common/model/textModel.ts b/src/vs/editor/common/model/textModel.ts index 612d22bc66f..89050a1e6de 100644 --- a/src/vs/editor/common/model/textModel.ts +++ b/src/vs/editor/common/model/textModel.ts @@ -37,9 +37,6 @@ import { Color } from 'vs/base/common/color'; import { Constants } from 'vs/base/common/uint'; import { EditorTheme } from 'vs/editor/common/view/viewContext'; import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; -import { UndoRedoService } from 'vs/platform/undoRedo/common/undoRedoService'; -import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { NoOpNotification, INotificationService } from 'vs/platform/notification/common/notification'; function createTextBufferBuilder() { return new PieceTreeTextBufferBuilder(); @@ -191,27 +188,6 @@ export class TextModel extends Disposable implements model.ITextModel { largeFileOptimizations: EDITOR_MODEL_DEFAULTS.largeFileOptimizations, }; - public static createFromString(text: string, options: model.ITextModelCreationOptions = TextModel.DEFAULT_CREATION_OPTIONS, languageIdentifier: LanguageIdentifier | null = null, uri: URI | null = null): TextModel { - const dialogService = new class implements IDialogService { - _serviceBrand: undefined; - confirm() { return Promise.resolve({ confirmed: false }); } - show() { return Promise.resolve({ choice: 0 }); } - about() { return Promise.resolve(); } - }; - const noop = new NoOpNotification(); - const notificationService = new class implements INotificationService { - _serviceBrand: undefined; - info() { return noop; } - warn() { return noop; } - error() { return noop; } - notify() { return noop; } - prompt() { return noop; } - status() { return Disposable.None; } - setFilter() { } - }; - return new TextModel(text, options, languageIdentifier, uri, new UndoRedoService(dialogService, notificationService)); - } - public static resolveOptions(textBuffer: model.ITextBuffer, options: model.ITextModelCreationOptions): model.TextModelResolvedOptions { if (options.detectIndentation) { const guessedIndentation = guessIndentation(textBuffer, options.tabSize, options.insertSpaces); diff --git a/src/vs/editor/contrib/gotoSymbol/peek/referencesWidget.ts b/src/vs/editor/contrib/gotoSymbol/peek/referencesWidget.ts index 7c279e25546..dc6368509b3 100644 --- a/src/vs/editor/contrib/gotoSymbol/peek/referencesWidget.ts +++ b/src/vs/editor/contrib/gotoSymbol/peek/referencesWidget.ts @@ -33,6 +33,7 @@ import * as peekView from 'vs/editor/contrib/peekView/peekView'; import { FileReferences, OneReference, ReferencesModel } from '../referencesModel'; import { FuzzyScore } from 'vs/base/common/filters'; import { SplitView, Sizing } from 'vs/base/browser/ui/splitview/splitview'; +import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; class DecorationsManager implements IDisposable { @@ -215,7 +216,8 @@ export class ReferenceWidget extends peekView.PeekViewWidget { @ITextModelService private readonly _textModelResolverService: ITextModelService, @IInstantiationService private readonly _instantiationService: IInstantiationService, @peekView.IPeekViewService private readonly _peekViewService: peekView.IPeekViewService, - @ILabelService private readonly _uriLabel: ILabelService + @ILabelService private readonly _uriLabel: ILabelService, + @IUndoRedoService private readonly _undoRedoService: IUndoRedoService, ) { super(editor, { showFrame: false, showArrow: true, isResizeable: true, isAccessible: true }); @@ -304,7 +306,7 @@ export class ReferenceWidget extends peekView.PeekViewWidget { }; this._preview = this._instantiationService.createInstance(EmbeddedCodeEditorWidget, this._previewContainer, options, this.editor); dom.hide(this._previewContainer); - this._previewNotAvailableMessage = TextModel.createFromString(nls.localize('missingPreviewMessage', "no preview available")); + this._previewNotAvailableMessage = new TextModel(nls.localize('missingPreviewMessage', "no preview available"), TextModel.DEFAULT_CREATION_OPTIONS, null, null, this._undoRedoService); // tree this._treeContainer = dom.append(containerElement, dom.$('div.ref-tree.inline')); diff --git a/src/vs/editor/test/common/editorTestUtils.ts b/src/vs/editor/test/common/editorTestUtils.ts index 48c5ce03264..8298cf24b81 100644 --- a/src/vs/editor/test/common/editorTestUtils.ts +++ b/src/vs/editor/test/common/editorTestUtils.ts @@ -7,6 +7,9 @@ import { URI } from 'vs/base/common/uri'; import { DefaultEndOfLine, ITextModelCreationOptions } from 'vs/editor/common/model'; import { TextModel } from 'vs/editor/common/model/textModel'; import { LanguageIdentifier } from 'vs/editor/common/modes'; +import { TestDialogService } from 'vs/platform/dialogs/test/common/testDialogService'; +import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService'; +import { UndoRedoService } from 'vs/platform/undoRedo/common/undoRedoService'; export function withEditorModel(text: string[], callback: (model: TextModel) => void): void { let model = createTextModel(text.join('\n')); @@ -36,5 +39,8 @@ export function createTextModel(text: string, _options: IRelaxedTextModelCreatio isForSimpleWidget: (typeof _options.isForSimpleWidget === 'undefined' ? TextModel.DEFAULT_CREATION_OPTIONS.isForSimpleWidget : _options.isForSimpleWidget), largeFileOptimizations: (typeof _options.largeFileOptimizations === 'undefined' ? TextModel.DEFAULT_CREATION_OPTIONS.largeFileOptimizations : _options.largeFileOptimizations), }; - return TextModel.createFromString(text, options, languageIdentifier, uri); + const dialogService = new TestDialogService(); + const notificationService = new TestNotificationService(); + const undoRedoService = new UndoRedoService(dialogService, notificationService); + return new TextModel(text, options, languageIdentifier, uri, undoRedoService); } diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts index 6804221716a..0c55961052c 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditTree.ts @@ -26,6 +26,7 @@ import { ThemeIcon } from 'vs/platform/theme/common/themeService'; import { WorkspaceFileEdit } from 'vs/editor/common/modes'; import { compare } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; +import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; // --- VIEW MODEL @@ -174,7 +175,10 @@ export class BulkEditDataSource implements IAsyncDataSource Date: Fri, 21 Feb 2020 23:10:01 +0100 Subject: [PATCH 046/200] Always prompt when splitting a workspace stack element --- .../undoRedo/common/undoRedoService.ts | 76 +++++++++++++++---- 1 file changed, 63 insertions(+), 13 deletions(-) diff --git a/src/vs/platform/undoRedo/common/undoRedoService.ts b/src/vs/platform/undoRedo/common/undoRedoService.ts index 8b68917e466..1f5191e2a74 100644 --- a/src/vs/platform/undoRedo/common/undoRedoService.ts +++ b/src/vs/platform/undoRedo/common/undoRedoService.ts @@ -33,6 +33,30 @@ class ResourceStackElement { this.strResources = [this.strResource]; } } + +const enum RemovedResourceReason { + ExternalRemoval = 0, + NoParallelUniverses = 1 +} + +class RemovedResources { + public readonly set: Set = new Set(); + public readonly reason: [URI[], URI[]] = [[], []]; + + public createMessage(): string { + let messages: string[] = []; + if (this.reason[RemovedResourceReason.ExternalRemoval].length > 0) { + const paths = this.reason[RemovedResourceReason.ExternalRemoval].map(uri => uri.scheme === Schemas.file ? uri.fsPath : uri.path); + messages.push(nls.localize('externalRemoval', "The following files have been closed: {0}.", paths.join(', '))); + } + if (this.reason[RemovedResourceReason.NoParallelUniverses].length > 0) { + const paths = this.reason[RemovedResourceReason.NoParallelUniverses].map(uri => uri.scheme === Schemas.file ? uri.fsPath : uri.path); + messages.push(nls.localize('noParallelUniverses', "The following files have been modified in an incompatible way: {0}.", paths.join(', '))); + } + return messages.join('\n'); + } +} + class WorkspaceStackElement { public readonly type = UndoRedoElementType.Workspace; public readonly actual: IWorkspaceUndoRedoElement; @@ -40,12 +64,24 @@ class WorkspaceStackElement { public readonly resources: URI[]; public readonly strResources: string[]; + public removedResources: RemovedResources | null; constructor(actual: IWorkspaceUndoRedoElement) { this.actual = actual; this.label = actual.label; this.resources = actual.resources.slice(0); this.strResources = this.resources.map(resource => uriGetComparisonKey(resource)); + this.removedResources = null; + } + + public removeResource(resource: URI, strResource: string, reason: RemovedResourceReason): void { + if (!this.removedResources) { + this.removedResources = new RemovedResources(); + } + if (!this.removedResources.set.has(strResource)) { + this.removedResources.set.add(strResource); + this.removedResources.reason[reason].push(resource); + } } } type StackElement = ResourceStackElement | WorkspaceStackElement; @@ -91,7 +127,7 @@ export class UndoRedoService implements IUndoRedoService { // remove the future for (const futureElement of editStack.future) { if (futureElement.type === UndoRedoElementType.Workspace) { - this._splitFutureWorkspaceElement(futureElement, strResource); + futureElement.removeResource(resource, strResource, RemovedResourceReason.NoParallelUniverses); } } editStack.future = []; @@ -114,7 +150,7 @@ export class UndoRedoService implements IUndoRedoService { return null; } - private _splitPastWorkspaceElement(toRemove: WorkspaceStackElement, ignoreStrResource: string | null): void { + private _splitPastWorkspaceElement(toRemove: WorkspaceStackElement, ignoreResources: Set | null): void { const individualArr = toRemove.actual.split(); const individualMap = new Map(); for (const _element of individualArr) { @@ -123,7 +159,7 @@ export class UndoRedoService implements IUndoRedoService { } for (const strResource of toRemove.strResources) { - if (strResource === ignoreStrResource) { + if (ignoreResources && ignoreResources.has(strResource)) { continue; } const editStack = this._editStacks.get(strResource)!; @@ -142,7 +178,7 @@ export class UndoRedoService implements IUndoRedoService { } } - private _splitFutureWorkspaceElement(toRemove: WorkspaceStackElement, ignoreStrResource: string | null): void { + private _splitFutureWorkspaceElement(toRemove: WorkspaceStackElement, ignoreResources: Set | null): void { const individualArr = toRemove.actual.split(); const individualMap = new Map(); for (const _element of individualArr) { @@ -151,7 +187,7 @@ export class UndoRedoService implements IUndoRedoService { } for (const strResource of toRemove.strResources) { - if (strResource === ignoreStrResource) { + if (ignoreResources && ignoreResources.has(strResource)) { continue; } const editStack = this._editStacks.get(strResource)!; @@ -174,14 +210,14 @@ export class UndoRedoService implements IUndoRedoService { const strResource = uriGetComparisonKey(resource); if (this._editStacks.has(strResource)) { const editStack = this._editStacks.get(strResource)!; - for (const pastElement of editStack.past) { - if (pastElement.type === UndoRedoElementType.Workspace) { - this._splitPastWorkspaceElement(pastElement, strResource); + for (const element of editStack.past) { + if (element.type === UndoRedoElementType.Workspace) { + element.removeResource(resource, strResource, RemovedResourceReason.ExternalRemoval); } } - for (const futureElement of editStack.future) { - if (futureElement.type === UndoRedoElementType.Workspace) { - this._splitFutureWorkspaceElement(futureElement, strResource); + for (const element of editStack.future) { + if (element.type === UndoRedoElementType.Workspace) { + element.removeResource(resource, strResource, RemovedResourceReason.ExternalRemoval); } } this._editStacks.delete(strResource); @@ -220,6 +256,13 @@ export class UndoRedoService implements IUndoRedoService { } private _workspaceUndo(resource: URI, element: WorkspaceStackElement): Promise | void { + if (element.removedResources) { + this._splitPastWorkspaceElement(element, element.removedResources.set); + const message = nls.localize('cannotWorkspaceUndo', "Could not undo '{0}' across all files. {1}", element.label, element.removedResources.createMessage()); + this._notificationService.info(message); + return; + } + // this must be the last past element in all the impacted resources! let affectedEditStacks: ResourceEditStack[] = []; for (const strResource of element.strResources) { @@ -236,7 +279,7 @@ export class UndoRedoService implements IUndoRedoService { if (cannotUndoDueToResources.length > 0) { this._splitPastWorkspaceElement(element, null); const paths = cannotUndoDueToResources.map(r => r.scheme === Schemas.file ? r.fsPath : r.path); - const message = nls.localize('undoFurtherChanges', "Could not undo '{0}' across all files because changes were made to {1}", element.label, paths.join(', ')); + const message = nls.localize('cannotWorkspaceUndoDueToChanges', "Could not undo '{0}' across all files because changes were made to {1}", element.label, paths.join(', ')); this._notificationService.info(message); return; } @@ -297,6 +340,13 @@ export class UndoRedoService implements IUndoRedoService { } private _workspaceRedo(resource: URI, element: WorkspaceStackElement): Promise | void { + if (element.removedResources) { + this._splitFutureWorkspaceElement(element, element.removedResources.set); + const message = nls.localize('cannotWorkspaceRedo', "Could not redo '{0}' across all files. {1}", element.label, element.removedResources.createMessage()); + this._notificationService.info(message); + return; + } + // this must be the last future element in all the impacted resources! let affectedEditStacks: ResourceEditStack[] = []; for (const strResource of element.strResources) { @@ -313,7 +363,7 @@ export class UndoRedoService implements IUndoRedoService { if (cannotRedoDueToResources.length > 0) { this._splitFutureWorkspaceElement(element, null); const paths = cannotRedoDueToResources.map(r => r.scheme === Schemas.file ? r.fsPath : r.path); - const message = nls.localize('redoFurtherChanges', "Could not redo '{0}' across all files because changes were made to {1}", element.label, paths.join(', ')); + const message = nls.localize('cannotWorkspaceRedoDueToChanges', "Could not redo '{0}' across all files because changes were made to {1}", element.label, paths.join(', ')); this._notificationService.info(message); return; } From b77192b4a76836831c4dcdae5c73f5bccfc0a6bf Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Fri, 21 Feb 2020 14:24:09 -0800 Subject: [PATCH 047/200] Fix #91121. --- .../contrib/search/browser/searchView.ts | 8 +++-- .../contrib/search/browser/searchWidget.ts | 30 ++++++++----------- .../contrib/search/common/searchModel.ts | 15 +++++++--- .../searchEditor/browser/searchEditor.ts | 15 +++++----- .../services/search/common/search.ts | 6 ++++ 5 files changed, 44 insertions(+), 30 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 3b8ebb0eaa2..f5174129360 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -34,7 +34,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { TreeResourceNavigator, WorkbenchObjectTree, getSelectionKeyboardEvent } from 'vs/platform/list/browser/listService'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IProgressService, IProgressStep, IProgress } from 'vs/platform/progress/common/progress'; -import { IPatternInfo, ISearchComplete, ISearchConfiguration, ISearchConfigurationProperties, ITextQuery, VIEW_ID, SearchSortOrder } from 'vs/workbench/services/search/common/search'; +import { IPatternInfo, ISearchComplete, ISearchConfiguration, ISearchConfigurationProperties, ITextQuery, VIEW_ID, SearchSortOrder, SearchCompletionExitCode } from 'vs/workbench/services/search/common/search'; import { ISearchHistoryService, ISearchHistoryValues } from 'vs/workbench/contrib/search/common/searchHistoryService'; import { diffInserted, diffInsertedOutline, diffRemoved, diffRemovedOutline, editorFindMatchHighlight, editorFindMatchHighlightBorder, listActiveSelectionForeground, foreground } from 'vs/platform/theme/common/colorRegistry'; import { ICssStyleCollector, ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; @@ -1348,7 +1348,7 @@ export class SearchView extends ViewPane { this.inputPatternIncludes.onSearchSubmit(); }); - this.viewModel.cancelSearch(); + this.viewModel.cancelSearch(true); this.currentSearchQ = this.currentSearchQ .then(() => this.doSearch(query, excludePatternText, includePatternText, triggeredOnType)) @@ -1393,6 +1393,10 @@ export class SearchView extends ViewPane { this.updateActions(); const hasResults = !this.viewModel.searchResult.isEmpty(); + if (completed?.exit === SearchCompletionExitCode.NewSearchStarted) { + return; + } + if (completed && completed.limitHit) { this.searchWidget.searchInput.showMessage({ content: nls.localize('searchMaxResultsWarning', "The result set only contains a subset of all matches. Please be more specific in your search to narrow down the results."), diff --git a/src/vs/workbench/contrib/search/browser/searchWidget.ts b/src/vs/workbench/contrib/search/browser/searchWidget.ts index 863e6cbe446..428becff1e7 100644 --- a/src/vs/workbench/contrib/search/browser/searchWidget.ts +++ b/src/vs/workbench/contrib/search/browser/searchWidget.ts @@ -154,7 +154,6 @@ export class SearchWidget extends Widget { private readonly _onDidToggleContext = new Emitter(); readonly onDidToggleContext: Event = this._onDidToggleContext.event; - private temporarilySkipSearchOnChange = false; private showContextCheckbox!: Checkbox; private contextLinesInput!: InputBox; @@ -488,12 +487,10 @@ export class SearchWidget extends Widget { this.setReplaceAllActionState(false); if (this.searchConfiguration.searchOnType) { - if (!this.temporarilySkipSearchOnChange) { - this._onSearchCancel.fire({ focus: false }); - if (this.searchInput.getRegex()) { - try { - const regex = new RegExp(this.searchInput.getValue(), 'ug'); - const matchienessHeuristic = ` + if (this.searchInput.getRegex()) { + try { + const regex = new RegExp(this.searchInput.getValue(), 'ug'); + const matchienessHeuristic = ` ~!@#$%^&*()_+ \`1234567890-= qwertyuiop[]\\ @@ -503,18 +500,17 @@ export class SearchWidget extends Widget { zxcvbnm,./ ZXCVBNM<>? `.match(regex)?.length ?? 0; - const delayMultiplier = - matchienessHeuristic < 50 ? 1 : - matchienessHeuristic < 100 ? 5 : // expressions like `.` or `\w` - 10; // only things matching empty string + const delayMultiplier = + matchienessHeuristic < 50 ? 1 : + matchienessHeuristic < 100 ? 5 : // expressions like `.` or `\w` + 10; // only things matching empty string - this.submitSearch(true, this.searchConfiguration.searchOnTypeDebouncePeriod * delayMultiplier); - } catch { - // pass - } - } else { - this.submitSearch(true, this.searchConfiguration.searchOnTypeDebouncePeriod); + this.submitSearch(true, this.searchConfiguration.searchOnTypeDebouncePeriod * delayMultiplier); + } catch { + // pass } + } else { + this.submitSearch(true, this.searchConfiguration.searchOnTypeDebouncePeriod); } } } diff --git a/src/vs/workbench/contrib/search/common/searchModel.ts b/src/vs/workbench/contrib/search/common/searchModel.ts index b0c40aa7ca6..bea090b9701 100644 --- a/src/vs/workbench/contrib/search/common/searchModel.ts +++ b/src/vs/workbench/contrib/search/common/searchModel.ts @@ -20,7 +20,7 @@ import { IModelService } from 'vs/editor/common/services/modelService'; import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IProgress, IProgressStep } from 'vs/platform/progress/common/progress'; import { ReplacePattern } from 'vs/workbench/services/search/common/replace'; -import { IFileMatch, IPatternInfo, ISearchComplete, ISearchProgressItem, ISearchConfigurationProperties, ISearchService, ITextQuery, ITextSearchPreviewOptions, ITextSearchMatch, ITextSearchStats, resultIsMatch, ISearchRange, OneLineRange, ITextSearchContext, ITextSearchResult, SearchSortOrder } from 'vs/workbench/services/search/common/search'; +import { IFileMatch, IPatternInfo, ISearchComplete, ISearchProgressItem, ISearchConfigurationProperties, ISearchService, ITextQuery, ITextSearchPreviewOptions, ITextSearchMatch, ITextSearchStats, resultIsMatch, ISearchRange, OneLineRange, ITextSearchContext, ITextSearchResult, SearchSortOrder, SearchCompletionExitCode } from 'vs/workbench/services/search/common/search'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { overviewRulerFindMatchForeground, minimapFindMatch } from 'vs/platform/theme/common/colorRegistry'; import { themeColorFromId } from 'vs/platform/theme/common/themeService'; @@ -968,6 +968,7 @@ export class SearchModel extends Disposable { readonly onReplaceTermChanged: Event = this._onReplaceTermChanged.event; private currentCancelTokenSource: CancellationTokenSource | null = null; + private searchCancelledForNewSearch: boolean = false; constructor( @ISearchService private readonly searchService: ISearchService, @@ -1016,7 +1017,7 @@ export class SearchModel extends Disposable { } search(query: ITextQuery, onProgress?: (result: ISearchProgressItem) => void): Promise { - this.cancelSearch(); + this.cancelSearch(true); this._searchQuery = query; @@ -1114,7 +1115,12 @@ export class SearchModel extends Disposable { private onSearchError(e: any, duration: number): void { if (errors.isPromiseCanceledError(e)) { - this.onSearchCompleted(null, duration); + this.onSearchCompleted( + this.searchCancelledForNewSearch + ? { exit: SearchCompletionExitCode.NewSearchStarted, results: [] } + : null, + duration); + this.searchCancelledForNewSearch = false; } } @@ -1133,8 +1139,9 @@ export class SearchModel extends Disposable { return this.configurationService.getValue('search'); } - cancelSearch(): boolean { + cancelSearch(cancelledForNewSearch = false): boolean { if (this.currentCancelTokenSource) { + this.searchCancelledForNewSearch = cancelledForNewSearch; this.currentCancelTokenSource.cancel(); return true; } diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts index 66cdb94e6e0..cdf0e788d75 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts @@ -77,6 +77,7 @@ export class SearchEditor extends BaseTextEditor { private searchHistoryDelayer: Delayer; private messageDisposables: IDisposable[] = []; private container: HTMLElement; + private searchModel: SearchModel; constructor( @ITelemetryService telemetryService: ITelemetryService, @@ -107,6 +108,8 @@ export class SearchEditor extends BaseTextEditor { this.inputFocusContextKey = InputBoxFocusedKey.bindTo(scopedContextKeyService); this.searchOperation = this._register(new LongRunningOperation(progressService)); this.searchHistoryDelayer = new Delayer(2000); + + this.searchModel = this._register(this.instantiationService.createInstance(SearchModel)); } createEditor(parent: HTMLElement) { @@ -326,6 +329,8 @@ export class SearchEditor extends BaseTextEditor { } private async doRunSearch() { + this.searchModel.cancelSearch(true); + const startInput = this.getInput(); this.searchHistoryDelayer.trigger(() => { @@ -372,30 +377,26 @@ export class SearchEditor extends BaseTextEditor { catch (err) { return; } - const searchModel = this.instantiationService.createInstance(SearchModel); + this.searchOperation.start(500); - await searchModel.search(query).finally(() => this.searchOperation.stop()); + await this.searchModel.search(query).finally(() => this.searchOperation.stop()); const input = this.getInput(); if (!input || input !== startInput || JSON.stringify(config) !== JSON.stringify(this.readConfigFromWidget())) { - - searchModel.dispose(); return; } const controller = ReferencesController.get(this.searchResultEditor); controller.closeWidget(false); const labelFormatter = (uri: URI): string => this.labelService.getUriLabel(uri, { relative: true }); - const results = serializeSearchResultForEditor(searchModel.searchResult, config.includes, config.excludes, config.contextLines, labelFormatter, false); + const results = serializeSearchResultForEditor(this.searchModel.searchResult, config.includes, config.excludes, config.contextLines, labelFormatter, false); const { header, body } = await input.getModels(); this.modelService.updateModel(body, results.text); header.setValue(serializeSearchConfiguration(config)); input.setDirty(input.resource.scheme !== 'search-editor'); input.setMatchRanges(results.matchRanges); - - searchModel.dispose(); } layout(dimension: DOM.Dimension) { diff --git a/src/vs/workbench/services/search/common/search.ts b/src/vs/workbench/services/search/common/search.ts index 99ba52ad15f..f8a834f47fe 100644 --- a/src/vs/workbench/services/search/common/search.ts +++ b/src/vs/workbench/services/search/common/search.ts @@ -198,6 +198,12 @@ export interface ISearchCompleteStats { export interface ISearchComplete extends ISearchCompleteStats { results: IFileMatch[]; + exit?: SearchCompletionExitCode +} + +export const enum SearchCompletionExitCode { + Normal, + NewSearchStarted } export interface ITextSearchStats { From 0c369e25915e01eaad07b1666c48343b985a6aab Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Fri, 21 Feb 2020 14:58:17 -0800 Subject: [PATCH 048/200] Tentatively fix #90962 by requiring `enter` --- src/vs/workbench/contrib/search/browser/searchView.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index f5174129360..ec2e7031e6d 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -879,7 +879,6 @@ export class SearchView extends ViewPane { this.searchWidget.setValue(selectedText); this.pauseSearching = false; updatedText = true; - if (this.searchConfig.searchOnType) { this.triggerQueryChange(); } } } From 1a6b2ea39a53f57c436ae0146b348f8f6aa43907 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Sat, 22 Feb 2020 00:20:50 +0100 Subject: [PATCH 049/200] Improve the discoverability of column selection --- .../editor/browser/controller/coreCommands.ts | 22 +- .../editor/browser/controller/mouseHandler.ts | 1 + src/vs/editor/browser/view/viewController.ts | 10 +- .../editor/browser/widget/codeEditorWidget.ts | 3 + src/vs/editor/common/config/editorOptions.ts | 10 + src/vs/editor/common/editorContextKeys.ts | 1 + .../common/standalone/standaloneEnums.ts | 199 ++++++++--------- src/vs/editor/contrib/dnd/dnd.ts | 4 +- src/vs/monaco.d.ts | 205 +++++++++--------- .../browser/codeEditor.contribution.ts | 1 + .../browser/toggleColumnSelection.ts | 43 ++++ 11 files changed, 296 insertions(+), 203 deletions(-) create mode 100644 src/vs/workbench/contrib/codeEditor/browser/toggleColumnSelection.ts diff --git a/src/vs/editor/browser/controller/coreCommands.ts b/src/vs/editor/browser/controller/coreCommands.ts index 567b9cdc08a..ca00039a592 100644 --- a/src/vs/editor/browser/controller/coreCommands.ts +++ b/src/vs/editor/browser/controller/coreCommands.ts @@ -24,7 +24,7 @@ import { MenuId } from 'vs/platform/actions/common/actions'; import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeybindingWeight, KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; const CORE_WEIGHT = KeybindingWeight.EditorCore; @@ -1530,6 +1530,26 @@ export namespace CoreNavigationCommands { }); } +const columnSelectionCondition = ContextKeyExpr.and( + EditorContextKeys.textInputFocus, + EditorContextKeys.columnSelection +); +function registerColumnSelection(id: string, keybinding: number): void { + KeybindingsRegistry.registerKeybindingRule({ + id: id, + primary: keybinding, + when: columnSelectionCondition, + weight: CORE_WEIGHT + 1 + }); +} + +registerColumnSelection(CoreNavigationCommands.CursorColumnSelectLeft.id, KeyMod.Shift | KeyCode.LeftArrow); +registerColumnSelection(CoreNavigationCommands.CursorColumnSelectRight.id, KeyMod.Shift | KeyCode.RightArrow); +registerColumnSelection(CoreNavigationCommands.CursorColumnSelectUp.id, KeyMod.Shift | KeyCode.UpArrow); +registerColumnSelection(CoreNavigationCommands.CursorColumnSelectPageUp.id, KeyMod.Shift | KeyCode.PageUp); +registerColumnSelection(CoreNavigationCommands.CursorColumnSelectDown.id, KeyMod.Shift | KeyCode.DownArrow); +registerColumnSelection(CoreNavigationCommands.CursorColumnSelectPageDown.id, KeyMod.Shift | KeyCode.PageDown); + /** * A command that will: * 1. invoke a command on the focused editor. diff --git a/src/vs/editor/browser/controller/mouseHandler.ts b/src/vs/editor/browser/controller/mouseHandler.ts index 0e5f6b0d454..5bab90e5eb1 100644 --- a/src/vs/editor/browser/controller/mouseHandler.ts +++ b/src/vs/editor/browser/controller/mouseHandler.ts @@ -352,6 +352,7 @@ class MouseDownOperation extends Disposable { if (!options.get(EditorOption.readOnly) && options.get(EditorOption.dragAndDrop) + && !options.get(EditorOption.columnSelection) && !this._mouseState.altKey // we don't support multiple mouse && e.detail < 2 // only single click on a selection can work && !this._isActive // the mouse is not down yet diff --git a/src/vs/editor/browser/view/viewController.ts b/src/vs/editor/browser/view/viewController.ts index d2eb4bdd1f1..d99af7b2b66 100644 --- a/src/vs/editor/browser/view/viewController.ts +++ b/src/vs/editor/browser/view/viewController.ts @@ -133,7 +133,9 @@ export class ViewController { } public dispatchMouse(data: IMouseDispatchData): void { - const selectionClipboardIsOn = (platform.isLinux && this.configuration.options.get(EditorOption.selectionClipboard)); + const options = this.configuration.options; + const selectionClipboardIsOn = (platform.isLinux && options.get(EditorOption.selectionClipboard)); + const columnSelection = options.get(EditorOption.columnSelection); if (data.middleButton && !selectionClipboardIsOn) { this._columnSelect(data.position, data.mouseColumn, data.inSelectionMode); } else if (data.startedOnLineNumbers) { @@ -196,7 +198,11 @@ export class ViewController { if (data.altKey) { this._columnSelect(data.position, data.mouseColumn, true); } else { - this._moveToSelect(data.position); + if (columnSelection) { + this._columnSelect(data.position, data.mouseColumn, true); + } else { + this._moveToSelect(data.position); + } } } else { this.moveTo(data.position); diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index 5262d8003b9..c8be02dfba7 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -1666,6 +1666,7 @@ class EditorContextKeysManager extends Disposable { private readonly _editorTextFocus: IContextKey; private readonly _editorTabMovesFocus: IContextKey; private readonly _editorReadonly: IContextKey; + private readonly _editorColumnSelection: IContextKey; private readonly _hasMultipleSelections: IContextKey; private readonly _hasNonEmptySelection: IContextKey; private readonly _canUndo: IContextKey; @@ -1687,6 +1688,7 @@ class EditorContextKeysManager extends Disposable { this._editorTextFocus = EditorContextKeys.editorTextFocus.bindTo(contextKeyService); this._editorTabMovesFocus = EditorContextKeys.tabMovesFocus.bindTo(contextKeyService); this._editorReadonly = EditorContextKeys.readOnly.bindTo(contextKeyService); + this._editorColumnSelection = EditorContextKeys.columnSelection.bindTo(contextKeyService); this._hasMultipleSelections = EditorContextKeys.hasMultipleSelections.bindTo(contextKeyService); this._hasNonEmptySelection = EditorContextKeys.hasNonEmptySelection.bindTo(contextKeyService); this._canUndo = EditorContextKeys.canUndo.bindTo(contextKeyService); @@ -1714,6 +1716,7 @@ class EditorContextKeysManager extends Disposable { this._editorTabMovesFocus.set(options.get(EditorOption.tabFocusMode)); this._editorReadonly.set(options.get(EditorOption.readOnly)); + this._editorColumnSelection.set(options.get(EditorOption.columnSelection)); } private _updateFromSelection(): void { diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index a187f9718e5..eb7a44e4089 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -324,6 +324,11 @@ export interface IEditorOptions { * Defaults to true. */ scrollPredominantAxis?: boolean; + /** + * Enable that the selection with the mouse and keys is doing column selection. + * Defaults to false. + */ + columnSelection?: boolean; /** * The modifier to be used to add multiple cursors with the mouse. * Defaults to 'alt' @@ -3294,6 +3299,7 @@ export const enum EditorOption { autoSurround, codeLens, colorDecorators, + columnSelection, comments, contextmenu, copyWithSyntaxHighlighting, @@ -3518,6 +3524,10 @@ export const EditorOptions = { EditorOption.colorDecorators, 'colorDecorators', true, { description: nls.localize('colorDecorators', "Controls whether the editor should render the inline color decorators and color picker.") } )), + columnSelection: register(new EditorBooleanOption( + EditorOption.columnSelection, 'columnSelection', false, + { description: nls.localize('columnSelection', "Enable that the selection with the mouse and keys is doing column selection.") } + )), comments: register(new EditorComments()), contextmenu: register(new EditorBooleanOption( EditorOption.contextmenu, 'contextmenu', true, diff --git a/src/vs/editor/common/editorContextKeys.ts b/src/vs/editor/common/editorContextKeys.ts index f15b8091073..01668420569 100644 --- a/src/vs/editor/common/editorContextKeys.ts +++ b/src/vs/editor/common/editorContextKeys.ts @@ -23,6 +23,7 @@ export namespace EditorContextKeys { export const textInputFocus = new RawContextKey('textInputFocus', false); export const readOnly = new RawContextKey('editorReadonly', false); + export const columnSelection = new RawContextKey('editorColumnSelection', false); export const writable: ContextKeyExpr = readOnly.toNegated(); export const hasNonEmptySelection = new RawContextKey('editorHasSelection', false); export const hasOnlyEmptySelection: ContextKeyExpr = hasNonEmptySelection.toNegated(); diff --git a/src/vs/editor/common/standalone/standaloneEnums.ts b/src/vs/editor/common/standalone/standaloneEnums.ts index 90a9fe1b996..14e4cfb12fc 100644 --- a/src/vs/editor/common/standalone/standaloneEnums.ts +++ b/src/vs/editor/common/standalone/standaloneEnums.ts @@ -178,105 +178,106 @@ export enum EditorOption { autoSurround = 10, codeLens = 11, colorDecorators = 12, - comments = 13, - contextmenu = 14, - copyWithSyntaxHighlighting = 15, - cursorBlinking = 16, - cursorSmoothCaretAnimation = 17, - cursorStyle = 18, - cursorSurroundingLines = 19, - cursorSurroundingLinesStyle = 20, - cursorWidth = 21, - disableLayerHinting = 22, - disableMonospaceOptimizations = 23, - dragAndDrop = 24, - emptySelectionClipboard = 25, - extraEditorClassName = 26, - fastScrollSensitivity = 27, - find = 28, - fixedOverflowWidgets = 29, - folding = 30, - foldingStrategy = 31, - foldingHighlight = 32, - fontFamily = 33, - fontInfo = 34, - fontLigatures = 35, - fontSize = 36, - fontWeight = 37, - formatOnPaste = 38, - formatOnType = 39, - glyphMargin = 40, - gotoLocation = 41, - hideCursorInOverviewRuler = 42, - highlightActiveIndentGuide = 43, - hover = 44, - inDiffEditor = 45, - letterSpacing = 46, - lightbulb = 47, - lineDecorationsWidth = 48, - lineHeight = 49, - lineNumbers = 50, - lineNumbersMinChars = 51, - links = 52, - matchBrackets = 53, - minimap = 54, - mouseStyle = 55, - mouseWheelScrollSensitivity = 56, - mouseWheelZoom = 57, - multiCursorMergeOverlapping = 58, - multiCursorModifier = 59, - multiCursorPaste = 60, - occurrencesHighlight = 61, - overviewRulerBorder = 62, - overviewRulerLanes = 63, - padding = 64, - parameterHints = 65, - peekWidgetDefaultFocus = 66, - definitionLinkOpensInPeek = 67, - quickSuggestions = 68, - quickSuggestionsDelay = 69, - readOnly = 70, - renderControlCharacters = 71, - renderIndentGuides = 72, - renderFinalNewline = 73, - renderLineHighlight = 74, - renderValidationDecorations = 75, - renderWhitespace = 76, - revealHorizontalRightPadding = 77, - roundedSelection = 78, - rulers = 79, - scrollbar = 80, - scrollBeyondLastColumn = 81, - scrollBeyondLastLine = 82, - scrollPredominantAxis = 83, - selectionClipboard = 84, - selectionHighlight = 85, - selectOnLineNumbers = 86, - showFoldingControls = 87, - showUnused = 88, - snippetSuggestions = 89, - smoothScrolling = 90, - stopRenderingLineAfter = 91, - suggest = 92, - suggestFontSize = 93, - suggestLineHeight = 94, - suggestOnTriggerCharacters = 95, - suggestSelection = 96, - tabCompletion = 97, - useTabStops = 98, - wordSeparators = 99, - wordWrap = 100, - wordWrapBreakAfterCharacters = 101, - wordWrapBreakBeforeCharacters = 102, - wordWrapColumn = 103, - wordWrapMinified = 104, - wrappingIndent = 105, - wrappingStrategy = 106, - editorClassName = 107, - pixelRatio = 108, - tabFocusMode = 109, - layoutInfo = 110, - wrappingInfo = 111 + columnSelection = 13, + comments = 14, + contextmenu = 15, + copyWithSyntaxHighlighting = 16, + cursorBlinking = 17, + cursorSmoothCaretAnimation = 18, + cursorStyle = 19, + cursorSurroundingLines = 20, + cursorSurroundingLinesStyle = 21, + cursorWidth = 22, + disableLayerHinting = 23, + disableMonospaceOptimizations = 24, + dragAndDrop = 25, + emptySelectionClipboard = 26, + extraEditorClassName = 27, + fastScrollSensitivity = 28, + find = 29, + fixedOverflowWidgets = 30, + folding = 31, + foldingStrategy = 32, + foldingHighlight = 33, + fontFamily = 34, + fontInfo = 35, + fontLigatures = 36, + fontSize = 37, + fontWeight = 38, + formatOnPaste = 39, + formatOnType = 40, + glyphMargin = 41, + gotoLocation = 42, + hideCursorInOverviewRuler = 43, + highlightActiveIndentGuide = 44, + hover = 45, + inDiffEditor = 46, + letterSpacing = 47, + lightbulb = 48, + lineDecorationsWidth = 49, + lineHeight = 50, + lineNumbers = 51, + lineNumbersMinChars = 52, + links = 53, + matchBrackets = 54, + minimap = 55, + mouseStyle = 56, + mouseWheelScrollSensitivity = 57, + mouseWheelZoom = 58, + multiCursorMergeOverlapping = 59, + multiCursorModifier = 60, + multiCursorPaste = 61, + occurrencesHighlight = 62, + overviewRulerBorder = 63, + overviewRulerLanes = 64, + padding = 65, + parameterHints = 66, + peekWidgetDefaultFocus = 67, + definitionLinkOpensInPeek = 68, + quickSuggestions = 69, + quickSuggestionsDelay = 70, + readOnly = 71, + renderControlCharacters = 72, + renderIndentGuides = 73, + renderFinalNewline = 74, + renderLineHighlight = 75, + renderValidationDecorations = 76, + renderWhitespace = 77, + revealHorizontalRightPadding = 78, + roundedSelection = 79, + rulers = 80, + scrollbar = 81, + scrollBeyondLastColumn = 82, + scrollBeyondLastLine = 83, + scrollPredominantAxis = 84, + selectionClipboard = 85, + selectionHighlight = 86, + selectOnLineNumbers = 87, + showFoldingControls = 88, + showUnused = 89, + snippetSuggestions = 90, + smoothScrolling = 91, + stopRenderingLineAfter = 92, + suggest = 93, + suggestFontSize = 94, + suggestLineHeight = 95, + suggestOnTriggerCharacters = 96, + suggestSelection = 97, + tabCompletion = 98, + useTabStops = 99, + wordSeparators = 100, + wordWrap = 101, + wordWrapBreakAfterCharacters = 102, + wordWrapBreakBeforeCharacters = 103, + wordWrapColumn = 104, + wordWrapMinified = 105, + wrappingIndent = 106, + wrappingStrategy = 107, + editorClassName = 108, + pixelRatio = 109, + tabFocusMode = 110, + layoutInfo = 111, + wrappingInfo = 112 } /** diff --git a/src/vs/editor/contrib/dnd/dnd.ts b/src/vs/editor/contrib/dnd/dnd.ts index ea3cda654bc..599e804eb05 100644 --- a/src/vs/editor/contrib/dnd/dnd.ts +++ b/src/vs/editor/contrib/dnd/dnd.ts @@ -68,7 +68,7 @@ export class DragAndDropController extends Disposable implements IEditorContribu } private onEditorKeyDown(e: IKeyboardEvent): void { - if (!this._editor.getOption(EditorOption.dragAndDrop)) { + if (!this._editor.getOption(EditorOption.dragAndDrop) || this._editor.getOption(EditorOption.columnSelection)) { return; } @@ -84,7 +84,7 @@ export class DragAndDropController extends Disposable implements IEditorContribu } private onEditorKeyUp(e: IKeyboardEvent): void { - if (!this._editor.getOption(EditorOption.dragAndDrop)) { + if (!this._editor.getOption(EditorOption.dragAndDrop) || this._editor.getOption(EditorOption.columnSelection)) { return; } diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 9fb1bf00712..4ca0cb14c12 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -2849,6 +2849,11 @@ declare namespace monaco.editor { * Defaults to true. */ scrollPredominantAxis?: boolean; + /** + * Enable that the selection with the mouse and keys is doing column selection. + * Defaults to false. + */ + columnSelection?: boolean; /** * The modifier to be used to add multiple cursors with the mouse. * Defaults to 'alt' @@ -3794,105 +3799,106 @@ declare namespace monaco.editor { autoSurround = 10, codeLens = 11, colorDecorators = 12, - comments = 13, - contextmenu = 14, - copyWithSyntaxHighlighting = 15, - cursorBlinking = 16, - cursorSmoothCaretAnimation = 17, - cursorStyle = 18, - cursorSurroundingLines = 19, - cursorSurroundingLinesStyle = 20, - cursorWidth = 21, - disableLayerHinting = 22, - disableMonospaceOptimizations = 23, - dragAndDrop = 24, - emptySelectionClipboard = 25, - extraEditorClassName = 26, - fastScrollSensitivity = 27, - find = 28, - fixedOverflowWidgets = 29, - folding = 30, - foldingStrategy = 31, - foldingHighlight = 32, - fontFamily = 33, - fontInfo = 34, - fontLigatures = 35, - fontSize = 36, - fontWeight = 37, - formatOnPaste = 38, - formatOnType = 39, - glyphMargin = 40, - gotoLocation = 41, - hideCursorInOverviewRuler = 42, - highlightActiveIndentGuide = 43, - hover = 44, - inDiffEditor = 45, - letterSpacing = 46, - lightbulb = 47, - lineDecorationsWidth = 48, - lineHeight = 49, - lineNumbers = 50, - lineNumbersMinChars = 51, - links = 52, - matchBrackets = 53, - minimap = 54, - mouseStyle = 55, - mouseWheelScrollSensitivity = 56, - mouseWheelZoom = 57, - multiCursorMergeOverlapping = 58, - multiCursorModifier = 59, - multiCursorPaste = 60, - occurrencesHighlight = 61, - overviewRulerBorder = 62, - overviewRulerLanes = 63, - padding = 64, - parameterHints = 65, - peekWidgetDefaultFocus = 66, - definitionLinkOpensInPeek = 67, - quickSuggestions = 68, - quickSuggestionsDelay = 69, - readOnly = 70, - renderControlCharacters = 71, - renderIndentGuides = 72, - renderFinalNewline = 73, - renderLineHighlight = 74, - renderValidationDecorations = 75, - renderWhitespace = 76, - revealHorizontalRightPadding = 77, - roundedSelection = 78, - rulers = 79, - scrollbar = 80, - scrollBeyondLastColumn = 81, - scrollBeyondLastLine = 82, - scrollPredominantAxis = 83, - selectionClipboard = 84, - selectionHighlight = 85, - selectOnLineNumbers = 86, - showFoldingControls = 87, - showUnused = 88, - snippetSuggestions = 89, - smoothScrolling = 90, - stopRenderingLineAfter = 91, - suggest = 92, - suggestFontSize = 93, - suggestLineHeight = 94, - suggestOnTriggerCharacters = 95, - suggestSelection = 96, - tabCompletion = 97, - useTabStops = 98, - wordSeparators = 99, - wordWrap = 100, - wordWrapBreakAfterCharacters = 101, - wordWrapBreakBeforeCharacters = 102, - wordWrapColumn = 103, - wordWrapMinified = 104, - wrappingIndent = 105, - wrappingStrategy = 106, - editorClassName = 107, - pixelRatio = 108, - tabFocusMode = 109, - layoutInfo = 110, - wrappingInfo = 111 + columnSelection = 13, + comments = 14, + contextmenu = 15, + copyWithSyntaxHighlighting = 16, + cursorBlinking = 17, + cursorSmoothCaretAnimation = 18, + cursorStyle = 19, + cursorSurroundingLines = 20, + cursorSurroundingLinesStyle = 21, + cursorWidth = 22, + disableLayerHinting = 23, + disableMonospaceOptimizations = 24, + dragAndDrop = 25, + emptySelectionClipboard = 26, + extraEditorClassName = 27, + fastScrollSensitivity = 28, + find = 29, + fixedOverflowWidgets = 30, + folding = 31, + foldingStrategy = 32, + foldingHighlight = 33, + fontFamily = 34, + fontInfo = 35, + fontLigatures = 36, + fontSize = 37, + fontWeight = 38, + formatOnPaste = 39, + formatOnType = 40, + glyphMargin = 41, + gotoLocation = 42, + hideCursorInOverviewRuler = 43, + highlightActiveIndentGuide = 44, + hover = 45, + inDiffEditor = 46, + letterSpacing = 47, + lightbulb = 48, + lineDecorationsWidth = 49, + lineHeight = 50, + lineNumbers = 51, + lineNumbersMinChars = 52, + links = 53, + matchBrackets = 54, + minimap = 55, + mouseStyle = 56, + mouseWheelScrollSensitivity = 57, + mouseWheelZoom = 58, + multiCursorMergeOverlapping = 59, + multiCursorModifier = 60, + multiCursorPaste = 61, + occurrencesHighlight = 62, + overviewRulerBorder = 63, + overviewRulerLanes = 64, + padding = 65, + parameterHints = 66, + peekWidgetDefaultFocus = 67, + definitionLinkOpensInPeek = 68, + quickSuggestions = 69, + quickSuggestionsDelay = 70, + readOnly = 71, + renderControlCharacters = 72, + renderIndentGuides = 73, + renderFinalNewline = 74, + renderLineHighlight = 75, + renderValidationDecorations = 76, + renderWhitespace = 77, + revealHorizontalRightPadding = 78, + roundedSelection = 79, + rulers = 80, + scrollbar = 81, + scrollBeyondLastColumn = 82, + scrollBeyondLastLine = 83, + scrollPredominantAxis = 84, + selectionClipboard = 85, + selectionHighlight = 86, + selectOnLineNumbers = 87, + showFoldingControls = 88, + showUnused = 89, + snippetSuggestions = 90, + smoothScrolling = 91, + stopRenderingLineAfter = 92, + suggest = 93, + suggestFontSize = 94, + suggestLineHeight = 95, + suggestOnTriggerCharacters = 96, + suggestSelection = 97, + tabCompletion = 98, + useTabStops = 99, + wordSeparators = 100, + wordWrap = 101, + wordWrapBreakAfterCharacters = 102, + wordWrapBreakBeforeCharacters = 103, + wordWrapColumn = 104, + wordWrapMinified = 105, + wrappingIndent = 106, + wrappingStrategy = 107, + editorClassName = 108, + pixelRatio = 109, + tabFocusMode = 110, + layoutInfo = 111, + wrappingInfo = 112 } export const EditorOptions: { acceptSuggestionOnCommitCharacter: IEditorOption; @@ -3908,6 +3914,7 @@ declare namespace monaco.editor { autoSurround: IEditorOption; codeLens: IEditorOption; colorDecorators: IEditorOption; + columnSelection: IEditorOption; comments: IEditorOption; contextmenu: IEditorOption; copyWithSyntaxHighlighting: IEditorOption; diff --git a/src/vs/workbench/contrib/codeEditor/browser/codeEditor.contribution.ts b/src/vs/workbench/contrib/codeEditor/browser/codeEditor.contribution.ts index 49cf081d967..eae8927c912 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/codeEditor.contribution.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/codeEditor.contribution.ts @@ -10,6 +10,7 @@ import './inspectKeybindings'; import './largeFileOptimizations'; import './inspectEditorTokens/inspectEditorTokens'; import './saveParticipants'; +import './toggleColumnSelection'; import './toggleMinimap'; import './toggleMultiCursorModifier'; import './toggleRenderControlCharacter'; diff --git a/src/vs/workbench/contrib/codeEditor/browser/toggleColumnSelection.ts b/src/vs/workbench/contrib/codeEditor/browser/toggleColumnSelection.ts new file mode 100644 index 00000000000..6624e94c7bd --- /dev/null +++ b/src/vs/workbench/contrib/codeEditor/browser/toggleColumnSelection.ts @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { Action } from 'vs/base/common/actions'; +import { MenuId, MenuRegistry, SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; + +export class ToggleColumnSelectionAction extends Action { + public static readonly ID = 'editor.action.toggleColumnSelection'; + public static readonly LABEL = nls.localize('toggleColumnSelection', "Toggle Column Selection Mode"); + + constructor( + id: string, + label: string, + @IConfigurationService private readonly _configurationService: IConfigurationService + ) { + super(id, label); + } + + public run(): Promise { + const newValue = !this._configurationService.getValue('editor.columnSelection'); + return this._configurationService.updateValue('editor.columnSelection', newValue, ConfigurationTarget.USER); + } +} + +const registry = Registry.as(ActionExtensions.WorkbenchActions); +registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleColumnSelectionAction, ToggleColumnSelectionAction.ID, ToggleColumnSelectionAction.LABEL), 'View: Toggle Column Selection Mode', nls.localize('view', "View")); + +MenuRegistry.appendMenuItem(MenuId.MenubarSelectionMenu, { + group: '3_multi', + command: { + id: ToggleColumnSelectionAction.ID, + title: nls.localize({ key: 'miColumnSelection', comment: ['&& denotes a mnemonic'] }, "Column &&Selection Mode"), + toggled: ContextKeyExpr.equals('config.editor.columnSelection', true) + }, + order: 1.5 +}); From 99d5733e5f1a377a2b38ad7262170a5dc89aec27 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 21 Feb 2020 16:25:42 -0800 Subject: [PATCH 050/200] New iteration of webview editor API proposal For #77131 **Motivation** While our existing webview editor API proposal more or less works, building an editable webview editor is fairly tricky using it! This is especially true for simple text based editors. It'd also be nice if we could get bi-directional live editing for text files. For example, if I open the same file in a webview editor and in VS Code's normal editor, edits on either side should be reflected in the other. While this can sort of be implemented using the existing API, it has some big limitations **Overview of changes** To address these problems, we've decided have two types of webview editors: - Text based webview editors. These editors used a `TextDocument` as their data model, which considerably simplifies implementing an editable webview. In almost all cases, this should be what you use for text files - Complex webview editors. This is basically the existing proposed API. This gives extension hooks into all the VS Code events, such as `save`, `undo`, and so on. These should be used for binary files or in very complex text editor cases. Both editor types now have an explicit model layer based on documents. Text editor use `TextDocument` for this, while custom editors use `WebviewEditorCustomDocument`. This replaces the delegate based approach previously used. --- .eslintrc.json | 1 + extensions/image-preview/src/preview.ts | 10 +- .../src/features/previewManager.ts | 10 +- src/vs/vscode.proposed.d.ts | 184 +++++++-- .../api/browser/mainThreadWebview.ts | 91 ++-- .../workbench/api/common/extHost.api.impl.ts | 10 +- .../workbench/api/common/extHost.protocol.ts | 20 +- src/vs/workbench/api/common/extHostWebview.ts | 387 ++++++++++++++---- .../contrib/customEditor/browser/commands.ts | 36 +- .../customEditor/browser/customEditorInput.ts | 170 ++++++-- .../browser/customEditorInputFactory.ts | 16 +- .../customEditor/browser/customEditors.ts | 22 +- .../browser/webviewEditor.contribution.ts | 4 +- .../customEditor/common/customEditor.ts | 11 +- .../customEditor/common/customEditorModel.ts | 137 ++----- .../test/browser/api/extHostWebview.test.ts | 31 +- 16 files changed, 777 insertions(+), 363 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index e1dc514e65e..5414b07f942 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -669,6 +669,7 @@ "create", "delete", "dispose", + "edit", "end", "expand", "hide", diff --git a/extensions/image-preview/src/preview.ts b/extensions/image-preview/src/preview.ts index 5e733b13cc2..92268c53b39 100644 --- a/extensions/image-preview/src/preview.ts +++ b/extensions/image-preview/src/preview.ts @@ -27,11 +27,15 @@ export class PreviewManager implements vscode.WebviewCustomEditorProvider { private readonly zoomStatusBarEntry: ZoomStatusBarEntry, ) { } - public async resolveWebviewEditor( - resource: vscode.Uri, + public async provideWebviewCustomEditorDocument(resource: vscode.Uri) { + return vscode.window.createWebviewEditorCustomDocument(PreviewManager.viewType, resource, undefined, {}); + } + + public async resolveWebviewCustomEditor( + document: vscode.WebviewEditorCustomDocument, webviewEditor: vscode.WebviewPanel, ): Promise { - const preview = new Preview(this.extensionRoot, resource, webviewEditor, this.sizeStatusBarEntry, this.binarySizeStatusBarEntry, this.zoomStatusBarEntry); + const preview = new Preview(this.extensionRoot, document.uri, webviewEditor, this.sizeStatusBarEntry, this.binarySizeStatusBarEntry, this.zoomStatusBarEntry); this._previews.add(preview); this.setActivePreview(preview); diff --git a/extensions/markdown-language-features/src/features/previewManager.ts b/extensions/markdown-language-features/src/features/previewManager.ts index 333a0651a44..75dfaaa6ef1 100644 --- a/extensions/markdown-language-features/src/features/previewManager.ts +++ b/extensions/markdown-language-features/src/features/previewManager.ts @@ -148,12 +148,16 @@ export class MarkdownPreviewManager extends Disposable implements vscode.Webview this.registerDynamicPreview(preview); } - public async resolveWebviewEditor( - resource: vscode.Uri, + public async provideWebviewCustomEditorDocument(resource: vscode.Uri) { + return vscode.window.createWebviewEditorCustomDocument('vscode.markdown.preview.editor', resource, undefined, {}); + } + + public async resolveWebviewCustomEditor( + document: vscode.WebviewEditorCustomDocument, webview: vscode.WebviewPanel ): Promise { const preview = DynamicMarkdownPreview.revive( - { resource, locked: false, resourceColumn: vscode.ViewColumn.One }, + { resource: document.uri, locked: false, resourceColumn: vscode.ViewColumn.One }, webview, this._contentProvider, this._previewConfigurations, diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 6e4a1efcea1..d04c46c8151 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1187,65 +1187,77 @@ declare module 'vscode' { //#region Custom editors: https://github.com/microsoft/vscode/issues/77131 + // TODO: + // - Naming! + // - Think about where a rename would live. + // - Think about handling go to line? + // - Should we expose edits? + // - More properties from `TextDocument`? + /** - * Defines the editing functionality of a webview editor. This allows the webview editor to hook into standard + * Defines the capabilities of a custom webview editor. + */ + interface WebviewCustomEditorCapabilities { + /** + * Defines the editing capability of a custom webview document. + * + * When not provided, the document is considered readonly. + */ + readonly editing?: WebviewCustomEditorEditingCapability; + } + + /** + * Defines the editing capability of a custom webview editor. This allows the webview editor to hook into standard * editor events such as `undo` or `save`. * * @param EditType Type of edits. */ - interface WebviewCustomEditorEditingDelegate { + interface WebviewCustomEditorEditingCapability { /** - * Save a resource. - * - * @param resource Resource being saved. + * Save the resource. * * @return Thenable signaling that the save has completed. */ - save(resource: Uri): Thenable; + save(): Thenable; /** - * Save an existing resource at a new path. + * Save the existing resource at a new path. * - * @param resource Resource being saved. * @param targetResource Location to save to. * * @return Thenable signaling that the save has completed. */ - saveAs(resource: Uri, targetResource: Uri): Thenable; + saveAs(targetResource: Uri): Thenable; /** * Event triggered by extensions to signal to VS Code that an edit has occurred. */ - // TODO@matt - // eslint-disable-next-line vscode-dts-event-naming - readonly onEdit: Event<{ readonly resource: Uri, readonly edit: EditType }>; + readonly onDidEdit: Event; /** * Apply a set of edits. * - * Note that is not invoked when `onEdit` is called as `onEdit` implies also updating the view to reflect the edit. + * Note that is not invoked when `onDidEdit` is called because `onDidEdit` implies also updating the view to reflect the edit. * - * @param resource Resource being edited. * @param edit Array of edits. Sorted from oldest to most recent. * * @return Thenable signaling that the change has completed. */ - applyEdits(resource: Uri, edits: readonly EditType[]): Thenable; + applyEdits(edits: readonly EditType[]): Thenable; /** * Undo a set of edits. * * This is triggered when a user undoes an edit or when revert is called on a file. * - * @param resource Resource being edited. * @param edit Array of edits. Sorted from most recent to oldest. * * @return Thenable signaling that the change has completed. */ - undoEdits(resource: Uri, edits: readonly EditType[]): Thenable; + undoEdits(edits: readonly EditType[]): Thenable; /** - * Back up `resource` in its current state. + * Back up the resource in its current state. * * Backups are used for hot exit and to prevent data loss. Your `backup` method should persist the resource in * its current state, i.e. with the edits applied. Most commonly this means saving the resource to disk in @@ -1257,57 +1269,159 @@ declare module 'vscode' { * made in quick succession, `backup` is only triggered after the last one. `backup` is not invoked when * `auto save` is enabled (since auto save already persists resource ). * - * @param resource The resource to back up. * @param cancellation Token that signals the current backup since a new backup is coming in. It is up to your * extension to decided how to respond to cancellation. If for example your extension is backing up a large file * in an operation that takes time to complete, your extension may decide to finish the ongoing backup rather * than cancelling it to ensure that VS Code has some valid backup. */ - backup?(resource: Uri, cancellation: CancellationToken): Thenable; + backup(cancellation: CancellationToken): Thenable; } + /** + * Represents a custom document for a custom webview editor. + * + * Custom documents are only used within a given `WebviewCustomEditorProvider`. The lifecycle of a + * `WebviewEditorCustomDocument` is managed by VS Code. When more more references remain to a given `WebviewEditorCustomDocument` + * then it is disposed of. + * + * @param UserDataType Type of custom object that extensions can store on the document. + */ + interface WebviewEditorCustomDocument { + /** + * The associated viewType for this document. + */ + readonly viewType: string; + + /** + * The associated uri for this document. + */ + readonly uri: Uri; + + /** + * Event fired when there are no more references to the `WebviewEditorCustomDocument`. + */ + readonly onDidDispose: Event; + + /** + * Custom data that an extension can store on the document. + */ + readonly userData: UserDataType; + + // TODO: Should we expose edits here? + // This could be helpful for tracking the life cycle of edits + } + + /** + * Provider for webview editors that use a custom data model. + * + * Custom webview editors use [`WebviewEditorCustomDocument`](#WebviewEditorCustomDocument) as their data model. + * This gives extensions full control over actions such as edit, save, and backup. + * + * You should use custom text based editors when dealing with binary files or more complex scenarios. For simple text + * based documents, use [`WebviewTextEditorProvider`](#WebviewTextEditorProvider) instead. + */ export interface WebviewCustomEditorProvider { + /** + * Create the model for a given + * + * @param document Resource being resolved. + */ + provideWebviewCustomEditorDocument(resource: Uri): Thenable; + /** * Resolve a webview editor for a given resource. * * To resolve a webview editor, a provider must fill in its initial html content and hook up all * the event listeners it is interested it. The provider should also take ownership of the passed in `WebviewPanel`. * - * @param resource Resource being resolved. + * @param document Document for resource being resolved. * @param webview Webview being resolved. The provider should take ownership of this webview. * * @return Thenable indicating that the webview editor has been resolved. */ - resolveWebviewEditor( - resource: Uri, - webview: WebviewPanel, - ): Thenable; + resolveWebviewCustomEditor(document: WebviewEditorCustomDocument, webview: WebviewPanel): Thenable; + } + /** + * Provider for text based webview editors. + * + * Text based webview editors use a [`TextDocument`](#TextDocument) as their data model. This considerably simplifies + * implementing a webview editor as it allows VS Code to handle many common operations such as + * undo and backup. The provider is responsible for synchronizing text changes between the webview and the `TextDocument`. + * + * You should use text based webview editors when dealing with text based file formats, such as `xml` or `json`. + * For binary files or more specialized use cases, see [WebviewCustomEditorProvider](#WebviewCustomEditorProvider). + */ + export interface WebviewTextEditorProvider { /** - * Controls the editing functionality of a webview editor. This allows the webview editor to hook into standard - * editor events such as `undo` or `save`. + * Resolve a webview editor for a given resource. * - * WebviewEditors that do not have `editingCapability` are considered to be readonly. Users can still interact - * with readonly editors, but these editors will not integrate with VS Code's standard editor functionality. + * To resolve a webview editor, the provider must fill in its initial html content and hook up all + * the event listeners it is interested it. The provider should also take ownership of the passed in `WebviewPanel`. + * + * @param document Resource being resolved. + * @param webview Webview being resolved. The provider should take ownership of this webview. + * + * @return Thenable indicating that the webview editor has been resolved. */ - readonly editingDelegate?: WebviewCustomEditorEditingDelegate; + resolveWebviewTextEditor(document: TextDocument, webview: WebviewPanel): Thenable; } namespace window { /** - * Register a new provider for webview editors of a given type. + * Register a new provider for text based webview editors. * - * @param viewType Type of the webview editor provider. - * @param provider Resolves webview editors. - * @param options Content settings for a webview panels the provider is given. + * @param viewType Type of the webview editor provider. This should match the `viewType` from the + * `package.json` contributions + * @param provider Provider that resolves webview editors. + * @param webviewOptions Content settings for the webview panels that provider is given. + * + * @return Disposable that unregisters the `WebviewTextEditorProvider`. + */ + export function registerWebviewTextEditorProvider( + viewType: string, + provider: WebviewTextEditorProvider, + webviewOptions?: WebviewPanelOptions, + ): Disposable; + + /** + * Register a new provider for custom webview editors. + * + * @param viewType Type of the webview editor provider. This should match the `viewType` from the + * `package.json` contributions. + * @param provider Provider that resolves webview editors. + * @param webviewOptions Content settings for the webview panels that provider is given. * * @return Disposable that unregisters the `WebviewCustomEditorProvider`. */ export function registerWebviewCustomEditorProvider( viewType: string, provider: WebviewCustomEditorProvider, - options?: WebviewPanelOptions, + webviewOptions?: WebviewPanelOptions, ): Disposable; + + /** + * Create a new `WebviewEditorCustomDocument`. + * + * Note that this method only creates a custom document object. To have it be registered with VS Code, you + * must return the document from `WebviewCustomEditorProvider.provideWebviewCustomEditorDocument`. + * + * @param viewType Type of the webview editor provider. This should match the `viewType` from the + * `package.json` contributions. + * @param uri The document's resource. + * @param userData Custom data attached to the document. + * @param capabilities Controls the editing functionality of a webview editor. This allows the webview + * editor to hook into standard editor events such as `undo` or `save`. + * + * WebviewEditors that do not have `editingCapability` are considered to be readonly. Users can still interact + * with readonly editors, but these editors will not integrate with VS Code's standard editor functionality. + */ + export function createWebviewEditorCustomDocument( + viewType: string, + uri: Uri, + userData: UserDataType, + capabilities: WebviewCustomEditorCapabilities + ): WebviewEditorCustomDocument; } //#endregion diff --git a/src/vs/workbench/api/browser/mainThreadWebview.ts b/src/vs/workbench/api/browser/mainThreadWebview.ts index 6a006f007f1..8764b50ffe8 100644 --- a/src/vs/workbench/api/browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/browser/mainThreadWebview.ts @@ -21,7 +21,7 @@ import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol'; import { editorGroupToViewColumn, EditorViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/common/shared/editor'; import { IEditorInput } from 'vs/workbench/common/editor'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; -import { CustomFileEditorInput } from 'vs/workbench/contrib/customEditor/browser/customEditorInput'; +import { CustomEditorInput, ModelType } from 'vs/workbench/contrib/customEditor/browser/customEditorInput'; import { ICustomEditorModel, ICustomEditorService } from 'vs/workbench/contrib/customEditor/common/customEditor'; import { WebviewExtensionDescription } from 'vs/workbench/contrib/webview/browser/webview'; import { WebviewInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput'; @@ -97,7 +97,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma private readonly _webviewInputs = new WebviewInputStore(); private readonly _revivers = new Map(); private readonly _editorProviders = new Map(); - private readonly _customEditorModels = new Map(); + private readonly _customEditorModels = new Map(); constructor( context: extHostProtocol.IExtHostContext, @@ -121,7 +121,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma // This should trigger the real reviver to be registered from the extension host side. this._register(_webviewWorkbenchService.registerResolver({ canResolve: (webview: WebviewInput) => { - if (webview instanceof CustomFileEditorInput) { + if (webview instanceof CustomEditorInput) { extensionService.activateByEvent(`onWebviewEditor:${webview.viewType}`); return false; } @@ -256,7 +256,20 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma this._revivers.delete(viewType); } - public $registerEditorProvider(extensionData: extHostProtocol.WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions, capabilities: readonly extHostProtocol.WebviewEditorCapabilities[]): void { + public $registerTextEditorProvider(extensionData: extHostProtocol.WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions): void { + return this.registerEditorProvider(ModelType.Text, extensionData, viewType, options); + } + + public $registerCustomEditorProvider(extensionData: extHostProtocol.WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions): void { + return this.registerEditorProvider(ModelType.Custom, extensionData, viewType, options); + } + + public registerEditorProvider( + modelType: ModelType, + extensionData: extHostProtocol.WebviewExtensionDescription, + viewType: string, + options: modes.IWebviewPanelOptions, + ): void { if (this._editorProviders.has(viewType)) { throw new Error(`Provider for ${viewType} already registered`); } @@ -265,21 +278,25 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma this._editorProviders.set(viewType, this._webviewWorkbenchService.registerResolver({ canResolve: (webviewInput) => { - return webviewInput instanceof CustomFileEditorInput && webviewInput.viewType === viewType; + return webviewInput instanceof CustomEditorInput && webviewInput.viewType === viewType; }, - resolveWebview: async (webviewInput: CustomFileEditorInput) => { + resolveWebview: async (webviewInput: CustomEditorInput) => { const handle = webviewInput.id; this._webviewInputs.add(handle, webviewInput); this.hookupWebviewEventDelegate(handle, webviewInput); webviewInput.webview.options = options; webviewInput.webview.extension = extension; + webviewInput.modelType = modelType; + const resource = webviewInput.resource; - const model = await this.retainCustomEditorModel(webviewInput, resource, viewType, capabilities); - webviewInput.onDisposeWebview(() => { - this.releaseCustomEditorModel(model); - }); + if (modelType === ModelType.Custom) { + const model = await this.retainCustomEditorModel(webviewInput, resource, viewType); + webviewInput.onDisposeWebview(() => { + this.releaseCustomEditorModel(model); + }); + } try { await this._proxy.$resolveWebviewEditor( @@ -311,33 +328,26 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma this._customEditorService.models.disposeAllModelsForView(viewType); } - private async retainCustomEditorModel(webviewInput: WebviewInput, resource: URI, viewType: string, capabilities: readonly extHostProtocol.WebviewEditorCapabilities[]) { + private async retainCustomEditorModel(webviewInput: WebviewInput, resource: URI, viewType: string) { const model = await this._customEditorService.models.resolve(webviewInput.resource, webviewInput.viewType); - const existingEntry = this._customEditorModels.get(model); + const key = viewType + resource.toString(); + const existingEntry = this._customEditorModels.get(key); if (existingEntry) { ++existingEntry.referenceCount; // no need to hook up listeners again return model; } + this._customEditorModels.set(key, { referenceCount: 1 }); + const { editable } = await this._proxy.$createWebviewCustomEditorDocument(resource, viewType); - this._customEditorModels.set(model, { referenceCount: 1 }); - - const capabilitiesSet = new Set(capabilities); - const isEditable = capabilitiesSet.has(extHostProtocol.WebviewEditorCapabilities.Editable); - if (isEditable) { - model.onUndo(e => { - this._proxy.$undoEdits(resource, viewType, e.edits); + if (editable) { + model.onUndo(() => { + this._proxy.$undo(resource, viewType); }); - model.onDisposeEdits(e => { - this._proxy.$disposeEdits(e.edits); - }); - - model.onApplyEdit(e => { - if (e.trigger !== model) { - this._proxy.$applyEdits(resource, viewType, e.edits); - } + model.onRedo(() => { + this._proxy.$redo(resource, viewType); }); model.onWillSave(e => { @@ -347,7 +357,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma // Save as should always be implemented even if the model is readonly model.onWillSaveAs(e => { - if (isEditable) { + if (editable) { e.waitUntil(this._proxy.$onSaveAs(e.resource.toJSON(), viewType, e.targetResource.toJSON())); } else { // Since the editor is readonly, just copy the file over @@ -355,36 +365,37 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma } }); - if (capabilitiesSet.has(extHostProtocol.WebviewEditorCapabilities.SupportsHotExit)) { - model.onBackup(() => { - return createCancelablePromise(token => - this._proxy.$backup(model.resource.toJSON(), viewType, token)); - }); - } + model.onBackup(() => { + return createCancelablePromise(token => + this._proxy.$backup(model.resource.toJSON(), viewType, token)); + }); return model; } private async releaseCustomEditorModel(model: ICustomEditorModel) { - const entry = this._customEditorModels.get(model); + const key = model.viewType + model.resource; + const entry = this._customEditorModels.get(key); if (!entry) { - return; + throw new Error('Model not found'); } --entry.referenceCount; if (entry.referenceCount <= 0) { + this._proxy.$disposeWebviewCustomEditorDocument(model.resource, model.viewType); this._customEditorService.models.disposeModel(model); - this._customEditorModels.delete(model); + this._customEditorModels.delete(key); } } - public $onEdit(resource: UriComponents, viewType: string, editId: number): void { + + + public $onDidChangeCustomDocumentState(resource: UriComponents, viewType: string, state: { dirty: boolean }) { const model = this._customEditorService.models.get(URI.revive(resource), viewType); if (!model) { throw new Error('Could not find model for webview editor'); } - - model.pushEdit(editId, model); + model.setDirty(state.dirty); } private hookupWebviewEventDelegate(handle: extHostProtocol.WebviewPanelHandle, input: WebviewInput) { diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 15054a83171..534d36a30b8 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -113,7 +113,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const extHostOutputService = rpcProtocol.set(ExtHostContext.ExtHostOutputService, accessor.get(IExtHostOutputService)); // manually create and register addressable instances - const extHostWebviews = rpcProtocol.set(ExtHostContext.ExtHostWebviews, new ExtHostWebviews(rpcProtocol, initData.environment, extHostWorkspace, extHostLogService, extHostApiDeprecation)); const extHostUrls = rpcProtocol.set(ExtHostContext.ExtHostUrls, new ExtHostUrls(rpcProtocol)); const extHostDocuments = rpcProtocol.set(ExtHostContext.ExtHostDocuments, new ExtHostDocuments(rpcProtocol, extHostDocumentsAndEditors)); const extHostDocumentContentProviders = rpcProtocol.set(ExtHostContext.ExtHostDocumentContentProviders, new ExtHostDocumentContentProvider(rpcProtocol, extHostDocumentsAndEditors, extHostLogService)); @@ -134,6 +133,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const extHostTheming = rpcProtocol.set(ExtHostContext.ExtHostTheming, new ExtHostTheming(rpcProtocol)); const extHostAuthentication = rpcProtocol.set(ExtHostContext.ExtHostAuthentication, new ExtHostAuthentication(rpcProtocol)); const extHostTimeline = rpcProtocol.set(ExtHostContext.ExtHostTimeline, new ExtHostTimeline(rpcProtocol, extHostCommands)); + const extHostWebviews = rpcProtocol.set(ExtHostContext.ExtHostWebviews, new ExtHostWebviews(rpcProtocol, initData.environment, extHostWorkspace, extHostLogService, extHostApiDeprecation, extHostDocuments)); // Check that no named customers are missing const expected: ProxyIdentifier[] = values(ExtHostContext); @@ -561,10 +561,18 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I registerWebviewPanelSerializer: (viewType: string, serializer: vscode.WebviewPanelSerializer) => { return extHostWebviews.registerWebviewPanelSerializer(extension, viewType, serializer); }, + registerWebviewTextEditorProvider: (viewType: string, provider: vscode.WebviewTextEditorProvider, options?: vscode.WebviewPanelOptions) => { + checkProposedApiEnabled(extension); + return extHostWebviews.registerWebviewTextEditorProvider(extension, viewType, provider, options); + }, registerWebviewCustomEditorProvider: (viewType: string, provider: vscode.WebviewCustomEditorProvider, options?: vscode.WebviewPanelOptions) => { checkProposedApiEnabled(extension); return extHostWebviews.registerWebviewCustomEditorProvider(extension, viewType, provider, options); }, + createWebviewEditorCustomDocument: (viewType: string, resource: vscode.Uri, userData: UserDataType, capabilities: vscode.WebviewCustomEditorCapabilities) => { + checkProposedApiEnabled(extension); + return extHostWebviews.createWebviewEditorCustomDocument(viewType, resource, userData, capabilities); + }, registerDecorationProvider(provider: vscode.DecorationProvider) { checkProposedApiEnabled(extension); return extHostDecorations.registerDecorationProvider(provider, extension.identifier); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 951127eec0a..9a28df76850 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -572,11 +572,6 @@ export interface WebviewExtensionDescription { readonly location: UriComponents; } -export enum WebviewEditorCapabilities { - Editable, - SupportsHotExit, -} - export interface MainThreadWebviewsShape extends IDisposable { $createWebviewPanel(extension: WebviewExtensionDescription, handle: WebviewPanelHandle, viewType: string, title: string, showOptions: WebviewPanelShowOptions, options: modes.IWebviewPanelOptions & modes.IWebviewOptions): void; $disposeWebview(handle: WebviewPanelHandle): void; @@ -592,10 +587,11 @@ export interface MainThreadWebviewsShape extends IDisposable { $registerSerializer(viewType: string): void; $unregisterSerializer(viewType: string): void; - $registerEditorProvider(extension: WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions, capabilities: readonly WebviewEditorCapabilities[]): void; + $registerTextEditorProvider(extension: WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions): void; + $registerCustomEditorProvider(extension: WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions): void; $unregisterEditorProvider(viewType: string): void; - $onEdit(resource: UriComponents, viewType: string, editId: number): void; + $onDidChangeCustomDocumentState(resource: UriComponents, viewType: string, state: { dirty: boolean }): void; } export interface WebviewPanelViewStateData { @@ -613,12 +609,14 @@ export interface ExtHostWebviewsShape { $onDidDisposeWebviewPanel(handle: WebviewPanelHandle): Promise; $deserializeWebviewPanel(newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, state: any, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise; + $resolveWebviewEditor(resource: UriComponents, newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise; + $createWebviewCustomEditorDocument(resource: UriComponents, viewType: string): Promise<{ editable: boolean }>; + $disposeWebviewCustomEditorDocument(resource: UriComponents, viewType: string): Promise; - $undoEdits(resource: UriComponents, viewType: string, editIds: readonly number[]): void; - $applyEdits(resource: UriComponents, viewType: string, editIds: readonly number[]): void; - $disposeEdits(editIds: readonly number[]): void; - + $undo(resource: UriComponents, viewType: string): void; + $redo(resource: UriComponents, viewType: string): void; + $revert(resource: UriComponents, viewType: string): void; $onSave(resource: UriComponents, viewType: string): Promise; $onSaveAs(resource: UriComponents, viewType: string, targetResource: UriComponents): Promise; diff --git a/src/vs/workbench/api/common/extHostWebview.ts b/src/vs/workbench/api/common/extHostWebview.ts index 4eed027e83e..fb648338fe6 100644 --- a/src/vs/workbench/api/common/extHostWebview.ts +++ b/src/vs/workbench/api/common/extHostWebview.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { CancellationToken } from 'vs/base/common/cancellation'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { URI, UriComponents } from 'vs/base/common/uri'; @@ -10,16 +11,15 @@ import { generateUuid } from 'vs/base/common/uuid'; import * as modes from 'vs/editor/common/modes'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { ILogService } from 'vs/platform/log/common/log'; +import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService'; +import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments'; import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters'; import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor'; import { asWebviewUri, WebviewInitData } from 'vs/workbench/api/common/shared/webview'; import type * as vscode from 'vscode'; -import { Cache } from './cache'; -import { ExtHostWebviewsShape, IMainContext, MainContext, MainThreadWebviewsShape, WebviewEditorCapabilities, WebviewPanelHandle, WebviewPanelViewStateData } from './extHost.protocol'; +import { ExtHostWebviewsShape, IMainContext, MainContext, MainThreadWebviewsShape, WebviewExtensionDescription, WebviewPanelHandle, WebviewPanelViewStateData } from './extHost.protocol'; import { Disposable as VSCodeDisposable } from './extHostTypes'; -import { CancellationToken } from 'vs/base/common/cancellation'; -import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService'; type IconPath = URI | { light: URI, dark: URI }; @@ -245,6 +245,193 @@ export class ExtHostWebviewEditor extends Disposable implements vscode.WebviewPa } } +type EditType = unknown; + +class WebviewEditorCustomDocument extends Disposable implements vscode.WebviewEditorCustomDocument { + private _currentEditIndex: number = -1; + private _savePoint: number = -1; + private readonly _edits: Array = []; + + constructor( + private readonly _proxy: MainThreadWebviewsShape, + public readonly viewType: string, + public readonly uri: vscode.Uri, + public readonly userData: unknown, + public readonly _capabilities: vscode.WebviewCustomEditorCapabilities, + ) { + super(); + + // Hook up events + _capabilities.editing?.onDidEdit(edit => { + this.pushEdit(edit, this); + }); + } + + //#region Public API + + #_onDidDispose = this._register(new Emitter()); + public readonly onDidDispose = this.#_onDidDispose.event; + + //#endregion + + dispose() { + this.#_onDidDispose.fire(); + super.dispose(); + } + + private pushEdit(edit: EditType, trigger: any) { + this.spliceEdits(edit); + + this._currentEditIndex = this._edits.length - 1; + this.updateState(); + // this._onApplyEdit.fire({ edits: [edit], trigger }); + } + + private updateState() { + const dirty = this._edits.length > 0 && this._savePoint !== this._currentEditIndex; + this._proxy.$onDidChangeCustomDocumentState(this.uri, this.viewType, { dirty }); + } + + private spliceEdits(editToInsert?: EditType) { + const start = this._currentEditIndex + 1; + const toRemove = this._edits.length - this._currentEditIndex; + + editToInsert + ? this._edits.splice(start, toRemove, editToInsert) + : this._edits.splice(start, toRemove); + } + + revert() { + const editing = this.getEditingCapability(); + if (this._currentEditIndex === this._savePoint) { + return true; + } + + if (this._currentEditIndex >= this._savePoint) { + const editsToUndo = this._edits.slice(this._savePoint, this._currentEditIndex); + editing.undoEdits(editsToUndo.reverse()); + } else if (this._currentEditIndex < this._savePoint) { + const editsToRedo = this._edits.slice(this._currentEditIndex, this._savePoint); + editing.applyEdits(editsToRedo); + } + + this._currentEditIndex = this._savePoint; + this.spliceEdits(); + + this.updateState(); + return true; + } + + undo() { + const editing = this.getEditingCapability(); + if (this._currentEditIndex < 0) { + // nothing to undo + return; + } + + const undoneEdit = this._edits[this._currentEditIndex]; + --this._currentEditIndex; + editing.undoEdits([undoneEdit]); + this.updateState(); + } + + redo() { + const editing = this.getEditingCapability(); + if (this._currentEditIndex >= this._edits.length - 1) { + // nothing to redo + return; + } + + ++this._currentEditIndex; + const redoneEdit = this._edits[this._currentEditIndex]; + editing.applyEdits([redoneEdit]); + this.updateState(); + } + + save() { + return this.getEditingCapability().save(); + } + + saveAs(target: vscode.Uri) { + return this.getEditingCapability().saveAs(target); + } + + backup(cancellation: CancellationToken): boolean | PromiseLike { + throw new Error('Method not implemented.'); + } + + private getEditingCapability(): vscode.WebviewCustomEditorEditingCapability { + if (!this._capabilities.editing) { + throw new Error('Document is not editable'); + } + return this._capabilities.editing; + } +} + +class WebviewDocumentStore { + private readonly _documents = new Map(); + + public get(viewType: string, resource: vscode.Uri): WebviewEditorCustomDocument | undefined { + return this._documents.get(this.key(viewType, resource)); + } + + public add(document: WebviewEditorCustomDocument) { + const key = this.key(document.viewType, document.uri); + if (this._documents.has(key)) { + throw new Error(`Document already exists for viewType:${document.viewType} resource:${document.uri}`); + } + this._documents.set(key, document); + } + + public delete(document: WebviewEditorCustomDocument) { + const key = this.key(document.viewType, document.uri); + this._documents.delete(key); + } + + private key(viewType: string, resource: vscode.Uri): string { + return `${viewType}@@@${resource.toString}`; + } +} + +const enum WebviewEditorType { + Text, + Custom +} + +type ProviderEntry = { + readonly extension: IExtensionDescription; + readonly type: WebviewEditorType.Text; + readonly provider: vscode.WebviewTextEditorProvider; +} | { + readonly extension: IExtensionDescription; + readonly type: WebviewEditorType.Custom; + readonly provider: vscode.WebviewCustomEditorProvider; +}; + +class EditorProviderStore { + private readonly _providers = new Map(); + + public addTextProvider(viewType: string, extension: IExtensionDescription, provider: vscode.WebviewTextEditorProvider): vscode.Disposable { + return this.add(WebviewEditorType.Text, viewType, extension, provider); + } + + public addCustomProvider(viewType: string, extension: IExtensionDescription, provider: vscode.WebviewCustomEditorProvider): vscode.Disposable { + return this.add(WebviewEditorType.Custom, viewType, extension, provider); + } + + public get(viewType: string): ProviderEntry | undefined { + return this._providers.get(viewType); + } + + private add(type: WebviewEditorType, viewType: string, extension: IExtensionDescription, provider: vscode.WebviewTextEditorProvider | vscode.WebviewCustomEditorProvider): vscode.Disposable { + if (this._providers.has(viewType)) { + throw new Error(`Provider for viewType:${viewType} already registered`); + } + this._providers.set(viewType, { type, extension, provider } as ProviderEntry); + return new VSCodeDisposable(() => this._providers.delete(viewType)); + } +} + export class ExtHostWebviews implements ExtHostWebviewsShape { private static newHandle(): WebviewPanelHandle { @@ -259,12 +446,9 @@ export class ExtHostWebviews implements ExtHostWebviewsShape { readonly extension: IExtensionDescription; }>(); - private readonly _editorProviders = new Map(); + private readonly _editorProviders = new EditorProviderStore(); - private readonly _edits = new Cache('edits'); + private readonly _documents = new WebviewDocumentStore(); constructor( mainContext: IMainContext, @@ -272,6 +456,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape { private readonly workspace: IExtHostWorkspace | undefined, private readonly _logService: ILogService, private readonly _deprecationService: IExtHostApiDeprecationService, + private readonly _extHostDocuments: ExtHostDocuments, ) { this._proxy = mainContext.getProxy(MainContext.MainThreadWebviews); } @@ -290,7 +475,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape { }; const handle = ExtHostWebviews.newHandle(); - this._proxy.$createWebviewPanel({ id: extension.identifier, location: extension.extensionLocation }, handle, viewType, title, webviewShowOptions, convertWebviewOptions(extension, this.workspace, options)); + this._proxy.$createWebviewPanel(toExtensionData(extension), handle, viewType, title, webviewShowOptions, convertWebviewOptions(extension, this.workspace, options)); const webview = new ExtHostWebview(handle, this._proxy, options, this.initData, this.workspace, extension, this._deprecationService); const panel = new ExtHostWebviewEditor(handle, this._proxy, viewType, title, viewColumn, options, webview); @@ -316,31 +501,45 @@ export class ExtHostWebviews implements ExtHostWebviewsShape { }); } + public registerWebviewTextEditorProvider( + extension: IExtensionDescription, + viewType: string, + provider: vscode.WebviewTextEditorProvider, + options: vscode.WebviewPanelOptions | undefined = {} + ): vscode.Disposable { + const unregisterProvider = this._editorProviders.addTextProvider(viewType, extension, provider); + this._proxy.$registerTextEditorProvider(toExtensionData(extension), viewType, options); + + return new VSCodeDisposable(() => { + unregisterProvider.dispose(); + this._proxy.$unregisterEditorProvider(viewType); + }); + } + public registerWebviewCustomEditorProvider( extension: IExtensionDescription, viewType: string, provider: vscode.WebviewCustomEditorProvider, - options?: vscode.WebviewPanelOptions, + options: vscode.WebviewPanelOptions | undefined = {}, ): vscode.Disposable { - if (this._editorProviders.has(viewType)) { - throw new Error(`Editor provider for '${viewType}' already registered`); - } - this._editorProviders.set(viewType, { extension, provider, }); - - this._proxy.$registerEditorProvider({ id: extension.identifier, location: extension.extensionLocation }, viewType, options || {}, this.getCapabilites(provider)); - - // Hook up events - provider?.editingDelegate?.onEdit(({ edit, resource }) => { - const id = this._edits.add([edit]); - this._proxy.$onEdit(resource, viewType, id); - }); + const unregisterProvider = this._editorProviders.addCustomProvider(viewType, extension, provider); + this._proxy.$registerCustomEditorProvider(toExtensionData(extension), viewType, options); return new VSCodeDisposable(() => { - this._editorProviders.delete(viewType); + unregisterProvider.dispose(); this._proxy.$unregisterEditorProvider(viewType); }); } + public createWebviewEditorCustomDocument( + viewType: string, + resource: vscode.Uri, + userData: UserDataType, + capabilities: vscode.WebviewCustomEditorCapabilities, + ): vscode.WebviewEditorCustomDocument { + return Object.seal(new WebviewEditorCustomDocument(this._proxy, viewType, resource, userData, capabilities) as vscode.WebviewEditorCustomDocument); + } + public $onMessage( handle: WebviewPanelHandle, message: any @@ -421,6 +620,40 @@ export class ExtHostWebviews implements ExtHostWebviewsShape { await serializer.deserializeWebviewPanel(revivedPanel, state); } + async $createWebviewCustomEditorDocument(resource: UriComponents, viewType: string) { + const entry = this._editorProviders.get(viewType); + if (!entry) { + throw new Error(`No provider found for '${viewType}'`); + } + + if (entry.type !== WebviewEditorType.Custom) { + throw new Error(`Invalid provide type for '${viewType}'`); + } + + const revivedResource = URI.revive(resource); + const document = await entry.provider.provideWebviewCustomEditorDocument(revivedResource) as WebviewEditorCustomDocument; + this._documents.add(document); + return { + editable: !!document._capabilities.editing + }; + } + + async $disposeWebviewCustomEditorDocument(resource: UriComponents, viewType: string): Promise { + const entry = this._editorProviders.get(viewType); + if (!entry) { + throw new Error(`No provider found for '${viewType}'`); + } + + if (entry.type !== WebviewEditorType.Custom) { + throw new Error(`Invalid provider type for '${viewType}'`); + } + + const revivedResource = URI.revive(resource); + const document = this.getDocument(viewType, revivedResource); + this._documents.delete(document); + document.dispose(); + } + async $resolveWebviewEditor( resource: UriComponents, handle: WebviewPanelHandle, @@ -431,81 +664,79 @@ export class ExtHostWebviews implements ExtHostWebviewsShape { ): Promise { const entry = this._editorProviders.get(viewType); if (!entry) { - return Promise.reject(new Error(`No provider found for '${viewType}'`)); + throw new Error(`No provider found for '${viewType}'`); } - const { provider, extension } = entry; - const webview = new ExtHostWebview(handle, this._proxy, options, this.initData, this.workspace, extension, this._deprecationService); + const webview = new ExtHostWebview(handle, this._proxy, options, this.initData, this.workspace, entry.extension, this._deprecationService); const revivedPanel = new ExtHostWebviewEditor(handle, this._proxy, viewType, title, typeof position === 'number' && position >= 0 ? typeConverters.ViewColumn.to(position) : undefined, options, webview); this._webviewPanels.set(handle, revivedPanel); + const revivedResource = URI.revive(resource); - await provider.resolveWebviewEditor(revivedResource, revivedPanel); - } - $undoEdits(resourceComponents: UriComponents, viewType: string, editIds: readonly number[]): void { - const provider = this.getEditorProvider(viewType); - if (!provider?.editingDelegate) { - return; - } - - const resource = URI.revive(resourceComponents); - const edits = editIds.map(id => this._edits.get(id, 0)); - provider.editingDelegate.undoEdits(resource, edits); - } - - $applyEdits(resourceComponents: UriComponents, viewType: string, editIds: readonly number[]): void { - const provider = this.getEditorProvider(viewType); - if (!provider?.editingDelegate) { - return; - } - - const resource = URI.revive(resourceComponents); - const edits = editIds.map(id => this._edits.get(id, 0)); - provider.editingDelegate.applyEdits(resource, edits); - } - - $disposeEdits(editIds: readonly number[]): void { - for (const edit of editIds) { - this._edits.delete(edit); + switch (entry.type) { + case WebviewEditorType.Custom: + { + const document = this.getDocument(viewType, revivedResource); + return entry.provider.resolveWebviewCustomEditor(document, revivedPanel); + } + case WebviewEditorType.Text: + { + await this._extHostDocuments.ensureDocumentData(revivedResource); + const document = this._extHostDocuments.getDocument(revivedResource); + return entry.provider.resolveWebviewTextEditor(document, revivedPanel); + } + default: + { + throw new Error('Unknown webview provider type'); + } } } - async $onSave(resource: UriComponents, viewType: string): Promise { - const provider = this.getEditorProvider(viewType); - return provider?.editingDelegate?.save(URI.revive(resource)); + async $undo(resourceComponents: UriComponents, viewType: string): Promise { + const document = this.getDocument(viewType, resourceComponents); + document.undo(); } - async $onSaveAs(resource: UriComponents, viewType: string, targetResource: UriComponents): Promise { - const provider = this.getEditorProvider(viewType); - return provider?.editingDelegate?.saveAs(URI.revive(resource), URI.revive(targetResource)); + async $redo(resourceComponents: UriComponents, viewType: string): Promise { + const document = this.getDocument(viewType, resourceComponents); + document.redo(); } - async $backup(resource: UriComponents, viewType: string, cancellation: CancellationToken): Promise { - const provider = this.getEditorProvider(viewType); - if (!provider?.editingDelegate?.backup) { - return false; - } - return provider.editingDelegate.backup(URI.revive(resource), cancellation); + async $revert(resourceComponents: UriComponents, viewType: string): Promise { + const document = this.getDocument(viewType, resourceComponents); + document.revert(); + } + + async $onSave(resourceComponents: UriComponents, viewType: string): Promise { + const document = this.getDocument(viewType, resourceComponents); + document.save(); + } + + async $onSaveAs(resourceComponents: UriComponents, viewType: string, targetResource: UriComponents): Promise { + const document = this.getDocument(viewType, resourceComponents); + return document.saveAs(URI.revive(targetResource)); + } + + async $backup(resourceComponents: UriComponents, viewType: string, cancellation: CancellationToken): Promise { + const document = this.getDocument(viewType, resourceComponents); + return document.backup(cancellation); } private getWebviewPanel(handle: WebviewPanelHandle): ExtHostWebviewEditor | undefined { return this._webviewPanels.get(handle); } - private getEditorProvider(viewType: string): vscode.WebviewCustomEditorProvider | undefined { - return this._editorProviders.get(viewType)?.provider; + private getDocument(viewType: string, resource: UriComponents): WebviewEditorCustomDocument { + const document = this._documents.get(viewType, URI.revive(resource)); + if (!document) { + throw new Error('No webview editor custom document found'); + } + return document; } +} - private getCapabilites(capabilities: vscode.WebviewCustomEditorProvider) { - const declaredCapabilites: WebviewEditorCapabilities[] = []; - if (capabilities.editingDelegate) { - declaredCapabilites.push(WebviewEditorCapabilities.Editable); - } - if (capabilities.editingDelegate?.backup) { - declaredCapabilites.push(WebviewEditorCapabilities.SupportsHotExit); - } - return declaredCapabilites; - } +function toExtensionData(extension: IExtensionDescription): WebviewExtensionDescription { + return { id: extension.identifier, location: extension.extensionLocation }; } function convertWebviewOptions( diff --git a/src/vs/workbench/contrib/customEditor/browser/commands.ts b/src/vs/workbench/contrib/customEditor/browser/commands.ts index 2d2f7825ecc..87c12f0e206 100644 --- a/src/vs/workbench/contrib/customEditor/browser/commands.ts +++ b/src/vs/workbench/contrib/customEditor/browser/commands.ts @@ -15,7 +15,7 @@ import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { EditorViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/common/shared/editor'; import { IEditorCommandsContext } from 'vs/workbench/common/editor'; -import { CustomFileEditorInput } from 'vs/workbench/contrib/customEditor/browser/customEditorInput'; +import { CustomEditorInput } from 'vs/workbench/contrib/customEditor/browser/customEditorInput'; import { defaultEditorId } from 'vs/workbench/contrib/customEditor/browser/customEditors'; import { CONTEXT_FOCUSED_CUSTOM_EDITOR_IS_EDITABLE, CONTEXT_HAS_CUSTOM_EDITORS, ICustomEditorService } from 'vs/workbench/contrib/customEditor/common/customEditor'; import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; @@ -114,19 +114,11 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { } public runCommand(accessor: ServicesAccessor): void { - const customEditorService = accessor.get(ICustomEditorService); - - const activeCustomEditor = customEditorService.activeCustomEditor; - if (!activeCustomEditor) { - return; + const editorService = accessor.get(IEditorService); + const activeInput = editorService.activeControl?.input; + if (activeInput instanceof CustomEditorInput) { + activeInput.undo(); } - - const model = customEditorService.models.get(activeCustomEditor.resource, activeCustomEditor.viewType); - if (!model) { - return; - } - - model.undo(); } }).register(); @@ -149,19 +141,11 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { } public runCommand(accessor: ServicesAccessor): void { - const customEditorService = accessor.get(ICustomEditorService); - - const activeCustomEditor = customEditorService.activeCustomEditor; - if (!activeCustomEditor) { - return; + const editorService = accessor.get(IEditorService); + const activeInput = editorService.activeControl?.input; + if (activeInput instanceof CustomEditorInput) { + activeInput.redo(); } - - const model = customEditorService.models.get(activeCustomEditor.resource, activeCustomEditor.viewType); - if (!model) { - return; - } - - model.redo(); } }).register(); @@ -193,7 +177,7 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { const customEditorService = accessor.get(ICustomEditorService); let toggleView = defaultEditorId; - if (!(activeEditor instanceof CustomFileEditorInput)) { + if (!(activeEditor instanceof CustomEditorInput)) { const bestAvailableEditor = customEditorService.getContributedCustomEditors(targetResource).bestAvailableEditor; if (bestAvailableEditor) { toggleView = bestAvailableEditor.id; diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts b/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts index 43e67a608d3..82ee3c38afa 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts @@ -16,20 +16,26 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { ILabelService } from 'vs/platform/label/common/label'; import { GroupIdentifier, IEditorInput, IRevertOptions, ISaveOptions, Verbosity } from 'vs/workbench/common/editor'; import { ICustomEditorModel, ICustomEditorService } from 'vs/workbench/contrib/customEditor/common/customEditor'; -import { WebviewEditorOverlay, IWebviewService } from 'vs/workbench/contrib/webview/browser/webview'; +import { IWebviewService, WebviewEditorOverlay } from 'vs/workbench/contrib/webview/browser/webview'; import { IWebviewWorkbenchService, LazilyResolvedWebviewEditorInput } from 'vs/workbench/contrib/webview/browser/webviewWorkbenchService'; -import { AutoSaveMode, IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { AutoSaveMode, IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; + +export const enum ModelType { + Custom = 'custom', + Text = 'text', +} + +export class CustomEditorInput extends LazilyResolvedWebviewEditorInput { -export class CustomFileEditorInput extends LazilyResolvedWebviewEditorInput { public static typeId = 'workbench.editors.webviewEditor'; private readonly _editorResource: URI; get resource() { return this._editorResource; } - private _model?: ICustomEditorModel; - + private _model?: { readonly type: ModelType.Custom, readonly model: ICustomEditorModel } | { readonly type: ModelType.Text }; constructor( resource: URI, @@ -43,14 +49,18 @@ export class CustomFileEditorInput extends LazilyResolvedWebviewEditorInput { @ICustomEditorService private readonly customEditorService: ICustomEditorService, @IFileDialogService private readonly fileDialogService: IFileDialogService, @IFilesConfigurationService private readonly filesConfigurationService: IFilesConfigurationService, - @IEditorService private readonly editorService: IEditorService + @IEditorService private readonly editorService: IEditorService, + @ITextFileService private readonly textFileService: ITextFileService, + ) { super(id, viewType, '', webview, webviewService, webviewWorkbenchService); this._editorResource = resource; } + public modelType?: ModelType; + public getTypeId(): string { - return CustomFileEditorInput.typeId; + return CustomEditorInput.typeId; } public supportsSplitEditor() { @@ -62,13 +72,8 @@ export class CustomFileEditorInput extends LazilyResolvedWebviewEditorInput { return basename(this.labelService.getUriLabel(this.resource)); } - @memoize - getDescription(): string | undefined { - return super.getDescription(); - } - matches(other: IEditorInput): boolean { - return this === other || (other instanceof CustomFileEditorInput + return this === other || (other instanceof CustomEditorInput && this.viewType === other.viewType && isEqual(this.resource, other.resource)); } @@ -101,11 +106,24 @@ export class CustomFileEditorInput extends LazilyResolvedWebviewEditorInput { } public isReadonly(): boolean { - return false; + return false; // TODO } public isDirty(): boolean { - return this._model ? this._model.isDirty() : false; + if (!this._model) { + return false; + } + + switch (this._model.type) { + case ModelType.Text: + return this.textFileService.isDirty(this.resource); + + case ModelType.Custom: + return this._model.model.isDirty(); + + default: + throw new Error('Unknown model type'); + } } public isSaving(): boolean { @@ -125,12 +143,20 @@ export class CustomFileEditorInput extends LazilyResolvedWebviewEditorInput { return undefined; } - const result = await this._model.save(options); - if (!result) { - return undefined; + switch (this._model.type) { + case ModelType.Text: + { + const result = await this.textFileService.save(this.resource, options); + return result ? this : undefined; + } + case ModelType.Custom: + { + const result = await this._model.model.save(options); + return result ? this : undefined; + } + default: + throw new Error('Unknown model type'); } - - return this; } public async saveAs(groupId: GroupIdentifier, options?: ISaveOptions): Promise { @@ -144,31 +170,81 @@ export class CustomFileEditorInput extends LazilyResolvedWebviewEditorInput { return undefined; // save cancelled } - if (!await this._model.saveAs(this._editorResource, target, options)) { - return undefined; + switch (this._model.type) { + case ModelType.Text: + if (!await this.textFileService.saveAs(this.resource, target, options)) { + return undefined; + } + break; + + case ModelType.Custom: + if (!await this._model.model.saveAs(this._editorResource, target, options)) { + return undefined; + } + break; + + default: + throw new Error('Unknown model type'); } return this.handleMove(groupId, target) || this.editorService.createInput({ resource: target, forceFile: true }); } - public revert(group: GroupIdentifier, options?: IRevertOptions): Promise { - return this._model ? this._model.revert(options) : Promise.resolve(false); + public async revert(group: GroupIdentifier, options?: IRevertOptions): Promise { + if (!this._model) { + return false; + } + + switch (this._model.type) { + case ModelType.Text: + return this.textFileService.revert(this.resource, options); + + case ModelType.Custom: + return this._model.model.revert(options); + + default: + throw new Error('Unknown model type'); + } } public async resolve(): Promise { - this._model = await this.customEditorService.models.resolve(this.resource, this.viewType); - this._register(this._model.onDidChangeDirty(() => this._onDidChangeDirty.fire())); + const editorModel = await super.resolve(); + if (!this._model) { + switch (this.modelType) { + case ModelType.Custom: + const model = await this.customEditorService.models.resolve(this.resource, this.viewType); + this._model = { type: ModelType.Custom, model }; + this._register(model.onDidChangeDirty(() => this._onDidChangeDirty.fire())); + + break; + + case ModelType.Text: + this._model = { type: ModelType.Text, }; + this.textFileService.files.onDidChangeDirty(e => { + if (isEqual(this.resource, e.resource)) { + this._onDidChangeDirty.fire(); + } + }); + + break; + + default: + throw new Error('Unknown model type'); + } + } + if (this.isDirty()) { this._onDidChangeDirty.fire(); } - return await super.resolve(); + + return editorModel; } public handleMove(groupId: GroupIdentifier, uri: URI, options?: ITextEditorOptions): IEditorInput | undefined { const editorInfo = this.customEditorService.getCustomEditor(this.viewType); if (editorInfo?.matches(uri)) { const webview = assertIsDefined(this.takeOwnershipOfWebview()); - const newInput = this.instantiationService.createInstance(CustomFileEditorInput, + const newInput = this.instantiationService.createInstance(CustomEditorInput, uri, this.viewType, generateUuid(), @@ -178,4 +254,42 @@ export class CustomFileEditorInput extends LazilyResolvedWebviewEditorInput { } return undefined; } + + public undo(): void { + if (!this._model) { + return; + } + + switch (this._model.type) { + case ModelType.Custom: + this._model.model.undo(); + return; + + case ModelType.Text: + this.textFileService.files.get(this.resource)?.textEditorModel?.undo(); + return; + + default: + throw new Error('Unknown model type'); + } + } + + public redo(): void { + if (!this._model) { + return; + } + + switch (this._model.type) { + case ModelType.Custom: + this._model.model.redo(); + return; + + case ModelType.Text: + this.textFileService.files.get(this.resource)?.textEditorModel?.redo(); + return; + + default: + throw new Error('Unknown model type'); + } + } } diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditorInputFactory.ts b/src/vs/workbench/contrib/customEditor/browser/customEditorInputFactory.ts index a19bf46c58f..558dc3d380d 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditorInputFactory.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditorInputFactory.ts @@ -6,14 +6,14 @@ import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { CustomFileEditorInput } from 'vs/workbench/contrib/customEditor/browser/customEditorInput'; +import { CustomEditorInput } from 'vs/workbench/contrib/customEditor/browser/customEditorInput'; import { WebviewEditorInputFactory } from 'vs/workbench/contrib/webview/browser/webviewEditorInputFactory'; import { IWebviewWorkbenchService } from 'vs/workbench/contrib/webview/browser/webviewWorkbenchService'; import { Lazy } from 'vs/base/common/lazy'; export class CustomEditorInputFactory extends WebviewEditorInputFactory { - public static readonly ID = CustomFileEditorInput.typeId; + public static readonly ID = CustomEditorInput.typeId; public constructor( @IInstantiationService private readonly _instantiationService: IInstantiationService, @@ -22,10 +22,11 @@ export class CustomEditorInputFactory extends WebviewEditorInputFactory { super(webviewWorkbenchService); } - public serialize(input: CustomFileEditorInput): string | undefined { + public serialize(input: CustomEditorInput): string | undefined { const data = { ...this.toJson(input), - editorResource: input.resource.toJSON() + editorResource: input.resource.toJSON(), + modelType: input.modelType }; try { @@ -38,7 +39,7 @@ export class CustomEditorInputFactory extends WebviewEditorInputFactory { public deserialize( _instantiationService: IInstantiationService, serializedEditorInput: string - ): CustomFileEditorInput { + ): CustomEditorInput { const data = this.fromJson(serializedEditorInput); const id = data.id || generateUuid(); @@ -50,10 +51,13 @@ export class CustomEditorInputFactory extends WebviewEditorInputFactory { return webviewInput.webview; }); - const customInput = this._instantiationService.createInstance(CustomFileEditorInput, URI.from((data as any).editorResource), data.viewType, id, webview); + const customInput = this._instantiationService.createInstance(CustomEditorInput, URI.from((data as any).editorResource), data.viewType, id, webview); if (typeof data.group === 'number') { customInput.updateGroup(data.group); } + if ((data as any).modelType) { + customInput.modelType = (data as any).modelType; + } return customInput; } } diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts index f9e9490b1b5..7ce0a933cb7 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts @@ -7,7 +7,7 @@ import { coalesce } from 'vs/base/common/arrays'; import { Emitter } from 'vs/base/common/event'; import { Lazy } from 'vs/base/common/lazy'; import { Disposable } from 'vs/base/common/lifecycle'; -import { basename, isEqual, extname } from 'vs/base/common/resources'; +import { basename, extname, isEqual } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; import * as nls from 'vs/nls'; @@ -31,7 +31,7 @@ import { IWebviewService, webviewHasOwnEditFunctionsContext } from 'vs/workbench import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService, IOpenEditorOverride } from 'vs/workbench/services/editor/common/editorService'; import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; -import { CustomFileEditorInput } from './customEditorInput'; +import { CustomEditorInput } from './customEditorInput'; export const defaultEditorId = 'default'; @@ -138,7 +138,7 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ public get activeCustomEditor(): ICustomEditor | undefined { const activeInput = this.editorService.activeControl?.input; - if (!(activeInput instanceof CustomFileEditorInput)) { + if (!(activeInput instanceof CustomEditorInput)) { return undefined; } const resource = activeInput.resource; @@ -175,7 +175,7 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ let currentlyOpenedEditorType: undefined | string; for (const editor of group ? group.editors : []) { if (editor.resource && isEqual(editor.resource, resource)) { - currentlyOpenedEditorType = editor instanceof CustomFileEditorInput ? editor.viewType : defaultEditorId; + currentlyOpenedEditorType = editor instanceof CustomEditorInput ? editor.viewType : defaultEditorId; break; } } @@ -271,7 +271,7 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ const webview = new Lazy(() => { return this.webviewService.createWebviewEditorOverlay(id, { customClasses: options?.customClasses }, {}); }); - const input = this.instantiationService.createInstance(CustomFileEditorInput, resource, viewType, id, webview); + const input = this.instantiationService.createInstance(CustomEditorInput, resource, viewType, id, webview); if (group) { input.updateGroup(group.id); } @@ -297,7 +297,7 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ options: options ? EditorOptions.create(options) : undefined, }], targetGroup); - if (existing instanceof CustomFileEditorInput) { + if (existing instanceof CustomEditorInput) { existing.dispose(); } } @@ -321,14 +321,14 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ ...this.getUserConfiguredCustomEditors(resource).allEditors, ]; this._hasCustomEditor.set(possibleEditors.length > 0); - this._focusedCustomEditorIsEditable.set(activeControl?.input instanceof CustomFileEditorInput); + this._focusedCustomEditorIsEditable.set(activeControl?.input instanceof CustomEditorInput); this._webviewHasOwnEditFunctions.set(possibleEditors.length > 0); } private handleMovedFileInOpenedFileEditors(oldResource: URI, newResource: URI): void { for (const group of this.editorGroupService.groups) { for (const editor of group.editors) { - if (!(editor instanceof CustomFileEditorInput)) { + if (!(editor instanceof CustomEditorInput)) { continue; } @@ -371,7 +371,7 @@ export class CustomEditorContribution extends Disposable implements IWorkbenchCo })); this._register(this.editorService.onDidCloseEditor(({ editor }) => { - if (!(editor instanceof CustomFileEditorInput)) { + if (!(editor instanceof CustomEditorInput)) { return; } @@ -386,7 +386,7 @@ export class CustomEditorContribution extends Disposable implements IWorkbenchCo options: ITextEditorOptions | undefined, group: IEditorGroup ): IOpenEditorOverride | undefined { - if (editor instanceof CustomFileEditorInput) { + if (editor instanceof CustomEditorInput) { if (editor.group === group.id) { // No need to do anything return undefined; @@ -483,7 +483,7 @@ export class CustomEditorContribution extends Disposable implements IWorkbenchCo group: IEditorGroup ): IOpenEditorOverride | undefined { const getCustomEditorOverrideForSubInput = (subInput: IEditorInput, customClasses: string): EditorInput | undefined => { - if (subInput instanceof CustomFileEditorInput) { + if (subInput instanceof CustomEditorInput) { return undefined; } const resource = subInput.resource; diff --git a/src/vs/workbench/contrib/customEditor/browser/webviewEditor.contribution.ts b/src/vs/workbench/contrib/customEditor/browser/webviewEditor.contribution.ts index 663521b2042..00f28e3ecd9 100644 --- a/src/vs/workbench/contrib/customEditor/browser/webviewEditor.contribution.ts +++ b/src/vs/workbench/contrib/customEditor/browser/webviewEditor.contribution.ts @@ -17,7 +17,7 @@ import { CustomEditorInputFactory } from 'vs/workbench/contrib/customEditor/brow import { ICustomEditorService } from 'vs/workbench/contrib/customEditor/common/customEditor'; import { WebviewEditor } from 'vs/workbench/contrib/webview/browser/webviewEditor'; import './commands'; -import { CustomFileEditorInput } from './customEditorInput'; +import { CustomEditorInput } from './customEditorInput'; import { CustomEditorContribution, customEditorsAssociationsKey, CustomEditorService } from './customEditors'; registerSingleton(ICustomEditorService, CustomEditorService); @@ -31,7 +31,7 @@ Registry.as(EditorExtensions.Editors).registerEditor( WebviewEditor.ID, 'Webview Editor', ), [ - new SyncDescriptor(CustomFileEditorInput) + new SyncDescriptor(CustomEditorInput) ]); Registry.as(EditorInputExtensions.EditorInputFactories).registerEditorInputFactory( diff --git a/src/vs/workbench/contrib/customEditor/common/customEditor.ts b/src/vs/workbench/contrib/customEditor/common/customEditor.ts index e7b613ef6fc..96247937c20 100644 --- a/src/vs/workbench/contrib/customEditor/common/customEditor.ts +++ b/src/vs/workbench/contrib/customEditor/common/customEditor.ts @@ -43,8 +43,6 @@ export interface ICustomEditorService { promptOpenWith(resource: URI, options?: ITextEditorOptions, group?: IEditorGroup): Promise; } -export type CustomEditorEdit = number; - export interface ICustomEditorModelManager { get(resource: URI, viewType: string): ICustomEditorModel | undefined; @@ -69,23 +67,22 @@ export interface CustomEditorSaveAsEvent { export interface ICustomEditorModel extends IWorkingCopy { readonly viewType: string; - readonly onUndo: Event<{ edits: readonly CustomEditorEdit[], trigger: any | undefined }>; - readonly onApplyEdit: Event<{ edits: readonly CustomEditorEdit[], trigger: any | undefined }>; - readonly onDisposeEdits: Event<{ edits: readonly CustomEditorEdit[] }>; + readonly onUndo: Event; + readonly onRedo: Event; + readonly onRevert: Event; readonly onWillSave: Event; readonly onWillSaveAs: Event; onBackup(f: () => CancelablePromise): void; + setDirty(dirty: boolean): void; undo(): void; redo(): void; revert(options?: IRevertOptions): Promise; save(options?: ISaveOptions): Promise; saveAs(resource: URI, targetResource: URI, currentOptions?: ISaveOptions): Promise; - - pushEdit(edit: CustomEditorEdit, trigger: any): void; } export const enum CustomEditorPriority { diff --git a/src/vs/workbench/contrib/customEditor/common/customEditorModel.ts b/src/vs/workbench/contrib/customEditor/common/customEditorModel.ts index 6448fbf2a63..6adac4c6199 100644 --- a/src/vs/workbench/contrib/customEditor/common/customEditorModel.ts +++ b/src/vs/workbench/contrib/customEditor/common/customEditorModel.ts @@ -8,7 +8,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { IRevertOptions, ISaveOptions } from 'vs/workbench/common/editor'; -import { CustomEditorEdit, CustomEditorSaveAsEvent, CustomEditorSaveEvent, ICustomEditorModel } from 'vs/workbench/contrib/customEditor/common/customEditor'; +import { CustomEditorSaveAsEvent, CustomEditorSaveEvent, ICustomEditorModel } from 'vs/workbench/contrib/customEditor/common/customEditor'; import { IWorkingCopyBackup, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { ILabelService } from 'vs/platform/label/common/label'; import { basename } from 'vs/base/common/path'; @@ -38,10 +38,8 @@ namespace HotExitState { export class CustomEditorModel extends Disposable implements ICustomEditorModel { - private _currentEditIndex: number = -1; - private _savePoint: number = -1; - private readonly _edits: Array = []; private _hotExitState: HotExitState.State = HotExitState.NotSupported; + private _dirty = false; constructor( public readonly viewType: string, @@ -51,11 +49,6 @@ export class CustomEditorModel extends Disposable implements ICustomEditorModel super(); } - dispose() { - this._onDisposeEdits.fire({ edits: this._edits }); - super.dispose(); - } - //#region IWorkingCopy public get resource() { @@ -71,31 +64,31 @@ export class CustomEditorModel extends Disposable implements ICustomEditorModel } public isDirty(): boolean { - return this._edits.length > 0 && this._savePoint !== this._currentEditIndex; + return this._dirty; } - protected readonly _onDidChangeDirty: Emitter = this._register(new Emitter()); + private readonly _onDidChangeDirty: Emitter = this._register(new Emitter()); readonly onDidChangeDirty: Event = this._onDidChangeDirty.event; - protected readonly _onDidChangeContent: Emitter = this._register(new Emitter()); + private readonly _onDidChangeContent: Emitter = this._register(new Emitter()); readonly onDidChangeContent: Event = this._onDidChangeContent.event; //#endregion - protected readonly _onUndo = this._register(new Emitter<{ edits: readonly CustomEditorEdit[], trigger: any | undefined }>()); - readonly onUndo = this._onUndo.event; + private readonly _onUndo = this._register(new Emitter()); + public readonly onUndo = this._onUndo.event; - protected readonly _onApplyEdit = this._register(new Emitter<{ edits: readonly CustomEditorEdit[], trigger: any | undefined }>()); - readonly onApplyEdit = this._onApplyEdit.event; + private readonly _onRedo = this._register(new Emitter()); + public readonly onRedo = this._onRedo.event; - protected readonly _onDisposeEdits = this._register(new Emitter<{ edits: readonly CustomEditorEdit[] }>()); - readonly onDisposeEdits = this._onDisposeEdits.event; + private readonly _onRevert = this._register(new Emitter()); + public readonly onRevert = this._onRevert.event; - protected readonly _onWillSave = this._register(new Emitter()); - readonly onWillSave = this._onWillSave.event; + private readonly _onWillSave = this._register(new Emitter()); + public readonly onWillSave = this._onWillSave.event; - protected readonly _onWillSaveAs = this._register(new Emitter()); - readonly onWillSaveAs = this._onWillSaveAs.event; + private readonly _onWillSaveAs = this._register(new Emitter()); + public readonly onWillSaveAs = this._onWillSaveAs.event; private _onBackup: undefined | (() => CancelablePromise); @@ -110,38 +103,30 @@ export class CustomEditorModel extends Disposable implements ICustomEditorModel } } - public pushEdit(edit: CustomEditorEdit, trigger: any) { - this.spliceEdits(edit); + public setDirty(dirty: boolean): void { + this._onDidChangeContent.fire(); - this._currentEditIndex = this._edits.length - 1; - this.updateDirty(); - this._onApplyEdit.fire({ edits: [edit], trigger }); - this.updateContentChanged(); - } - - private spliceEdits(editToInsert?: CustomEditorEdit) { - const start = this._currentEditIndex + 1; - const toRemove = this._edits.length - this._currentEditIndex; - - const removedEdits = editToInsert - ? this._edits.splice(start, toRemove, editToInsert) - : this._edits.splice(start, toRemove); - - if (removedEdits.length) { - this._onDisposeEdits.fire({ edits: removedEdits }); + if (this._dirty !== dirty) { + this._dirty = dirty; + this._onDidChangeDirty.fire(); } } - private updateDirty() { - // TODO@matt this should to be more fine grained and avoid - // emitting events if there was no change actually - this._onDidChangeDirty.fire(); + public async revert(_options?: IRevertOptions) { + if (!this._dirty) { + return true; + } + + this._onRevert.fire(); + return true; } - private updateContentChanged() { - // TODO@matt revisit that this method is being called correctly - // on each case of content change within the custom editor - this._onDidChangeContent.fire(); + public undo() { + this._onUndo.fire(); + } + + public redo() { + this._onRedo.fire(); } public async save(_options?: ISaveOptions): Promise { @@ -158,8 +143,7 @@ export class CustomEditorModel extends Disposable implements ICustomEditorModel return false; } - this._savePoint = this._currentEditIndex; - this.updateDirty(); + this.setDirty(false); return true; } @@ -179,62 +163,11 @@ export class CustomEditorModel extends Disposable implements ICustomEditorModel return false; } - this._savePoint = this._currentEditIndex; - this.updateDirty(); + this.setDirty(false); return true; } - public async revert(_options?: IRevertOptions) { - if (this._currentEditIndex === this._savePoint) { - return true; - } - - if (this._currentEditIndex >= this._savePoint) { - const editsToUndo = this._edits.slice(this._savePoint, this._currentEditIndex); - this._onUndo.fire({ edits: editsToUndo.reverse(), trigger: undefined }); - } else if (this._currentEditIndex < this._savePoint) { - const editsToRedo = this._edits.slice(this._currentEditIndex, this._savePoint); - this._onApplyEdit.fire({ edits: editsToRedo, trigger: undefined }); - } - - this._currentEditIndex = this._savePoint; - this.spliceEdits(); - - this.updateDirty(); - this.updateContentChanged(); - return true; - } - - public undo() { - if (this._currentEditIndex < 0) { - // nothing to undo - return; - } - - const undoneEdit = this._edits[this._currentEditIndex]; - --this._currentEditIndex; - this._onUndo.fire({ edits: [undoneEdit], trigger: undefined }); - - this.updateDirty(); - this.updateContentChanged(); - } - - public redo() { - if (this._currentEditIndex >= this._edits.length - 1) { - // nothing to redo - return; - } - - ++this._currentEditIndex; - const redoneEdit = this._edits[this._currentEditIndex]; - - this._onApplyEdit.fire({ edits: [redoneEdit], trigger: undefined }); - - this.updateDirty(); - this.updateContentChanged(); - } - public async backup(): Promise { if (this._hotExitState === HotExitState.NotSupported) { throw new Error('Not supported'); diff --git a/src/vs/workbench/test/browser/api/extHostWebview.test.ts b/src/vs/workbench/test/browser/api/extHostWebview.test.ts index ed88cc6018e..c3e830dd4b9 100644 --- a/src/vs/workbench/test/browser/api/extHostWebview.test.ts +++ b/src/vs/workbench/test/browser/api/extHostWebview.test.ts @@ -13,19 +13,33 @@ import { ExtHostWebviews } from 'vs/workbench/api/common/extHostWebview'; import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor'; import { mock } from 'vs/workbench/test/browser/api/mock'; import { SingleProxyRPCProtocol } from './testRPCProtocol'; +import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; +import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments'; +import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; +import { IExtHostContext } from 'vs/workbench/api/common/extHost.protocol'; import { NullApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService'; suite('ExtHostWebview', () => { + let rpcProtocol: (IExtHostRpcService & IExtHostContext) | undefined; + let extHostDocuments: ExtHostDocuments | undefined; + + setup(() => { + const shape = createNoopMainThreadWebviews(); + rpcProtocol = SingleProxyRPCProtocol(shape); + + const extHostDocumentsAndEditors = new ExtHostDocumentsAndEditors(rpcProtocol, new NullLogService()); + extHostDocuments = new ExtHostDocuments(rpcProtocol, extHostDocumentsAndEditors); + }); + test('Cannot register multiple serializers for the same view type', async () => { const viewType = 'view.type'; - const shape = createNoopMainThreadWebviews(); - const extHostWebviews = new ExtHostWebviews(SingleProxyRPCProtocol(shape), { + const extHostWebviews = new ExtHostWebviews(rpcProtocol!, { webviewCspSource: '', webviewResourceRoot: '', isExtensionDevelopmentDebug: false, - }, undefined, new NullLogService(), NullApiDeprecationService); + }, undefined, new NullLogService(), NullApiDeprecationService, extHostDocuments!); let lastInvokedDeserializer: vscode.WebviewPanelSerializer | undefined = undefined; @@ -58,12 +72,11 @@ suite('ExtHostWebview', () => { }); test('asWebviewUri for desktop vscode-resource scheme', () => { - const shape = createNoopMainThreadWebviews(); - const extHostWebviews = new ExtHostWebviews(SingleProxyRPCProtocol(shape), { + const extHostWebviews = new ExtHostWebviews(rpcProtocol!, { webviewCspSource: '', webviewResourceRoot: 'vscode-resource://{{resource}}', isExtensionDevelopmentDebug: false, - }, undefined, new NullLogService(), NullApiDeprecationService); + }, undefined, new NullLogService(), NullApiDeprecationService, extHostDocuments!); const webview = extHostWebviews.createWebviewPanel({} as any, 'type', 'title', 1, {}); assert.strictEqual( @@ -98,13 +111,11 @@ suite('ExtHostWebview', () => { }); test('asWebviewUri for web endpoint', () => { - const shape = createNoopMainThreadWebviews(); - - const extHostWebviews = new ExtHostWebviews(SingleProxyRPCProtocol(shape), { + const extHostWebviews = new ExtHostWebviews(rpcProtocol!, { webviewCspSource: '', webviewResourceRoot: `https://{{uuid}}.webview.contoso.com/commit/{{resource}}`, isExtensionDevelopmentDebug: false, - }, undefined, new NullLogService(), NullApiDeprecationService); + }, undefined, new NullLogService(), NullApiDeprecationService, extHostDocuments!); const webview = extHostWebviews.createWebviewPanel({} as any, 'type', 'title', 1, {}); function stripEndpointUuid(input: string) { From 2402d70ae9682a5e7857b31d049ee2db29522cf1 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Fri, 21 Feb 2020 16:32:45 -0800 Subject: [PATCH 051/200] Take search editor out of preview. Closes #23931. Closes #16488. --- .../search/browser/search.contribution.ts | 16 +++------- .../contrib/search/browser/searchView.ts | 31 +++++++------------ .../contrib/searchEditor/browser/constants.ts | 1 - .../browser/searchEditor.contribution.ts | 25 +++------------ .../searchEditor/browser/searchEditor.ts | 2 +- .../browser/searchEditorActions.ts | 8 ++--- .../services/search/common/search.ts | 4 +-- 7 files changed, 25 insertions(+), 62 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/search.contribution.ts b/src/vs/workbench/contrib/search/browser/search.contribution.ts index 97da9089fd0..8d5a04998d9 100644 --- a/src/vs/workbench/contrib/search/browser/search.contribution.ts +++ b/src/vs/workbench/contrib/search/browser/search.contribution.ts @@ -54,7 +54,6 @@ import { ExplorerViewPaneContainer } from 'vs/workbench/contrib/files/browser/ex import { assertType, assertIsDefined } from 'vs/base/common/types'; import { SearchViewPaneContainer } from 'vs/workbench/contrib/search/browser/searchViewlet'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; -import product from 'vs/platform/product/common/product'; import { SearchEditor } from 'vs/workbench/contrib/searchEditor/browser/searchEditor'; registerSingleton(ISearchWorkbenchService, SearchWorkbenchService, true); @@ -775,21 +774,16 @@ configurationRegistry.registerConfiguration({ default: 300, markdownDescription: nls.localize('search.searchOnTypeDebouncePeriod', "When `#search.searchOnType#` is enabled, controls the timeout in milliseconds between a character being typed and the search starting. Has no effect when `search.searchOnType` is disabled.") }, - 'search.enableSearchEditorPreview': { - type: 'boolean', - default: product.quality !== 'stable', - description: nls.localize('search.enableSearchEditorPreview', "Experimental: When enabled, allows opening workspace search results in an editor.") - }, - 'search.searchEditorPreview.doubleClickBehaviour': { + 'search.searchEditor.doubleClickBehaviour': { type: 'string', enum: ['selectWord', 'goToLocation', 'openLocationToSide'], default: 'goToLocation', enumDescriptions: [ - nls.localize('search.searchEditorPreview.doubleClickBehaviour.selectWord', "Double clicking selects the word under the cursor."), - nls.localize('search.searchEditorPreview.doubleClickBehaviour.goToLocation', "Double clicking opens the result in the active editor group."), - nls.localize('search.searchEditorPreview.doubleClickBehaviour.openLocationToSide', "Double clicking opens the result in the editor group to the side, creating one if it does not yet exist."), + nls.localize('search.searchEditor.doubleClickBehaviour.selectWord', "Double clicking selects the word under the cursor."), + nls.localize('search.searchEditor.doubleClickBehaviour.goToLocation', "Double clicking opens the result in the active editor group."), + nls.localize('search.searchEditor.doubleClickBehaviour.openLocationToSide', "Double clicking opens the result in the editor group to the side, creating one if it does not yet exist."), ], - markdownDescription: nls.localize('search.searchEditorPreview.doubleClickBehaviour', "Configure effect of double clicking a result in a Search Editor.\n\n `#search.enableSearchEditorPreview#` must be enabled for this setting to have an effect.") + markdownDescription: nls.localize('search.searchEditor.doubleClickBehaviour', "Configure effect of double clicking a result in a search editor.") }, 'search.sortOrder': { 'type': 'string', diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index ec2e7031e6d..67ce6b13534 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -233,14 +233,9 @@ export class SearchView extends ViewPane { this.actions = [ this._register(this.instantiationService.createInstance(ClearSearchResultsAction, ClearSearchResultsAction.ID, ClearSearchResultsAction.LABEL)), + this._register(this.instantiationService.createInstance(OpenSearchEditorAction, OpenSearchEditorAction.ID, OpenSearchEditorAction.LABEL)) ]; - if (this.searchConfig.enableSearchEditorPreview) { - this.actions.push( - this._register(this.instantiationService.createInstance(OpenSearchEditorAction, OpenSearchEditorAction.ID, OpenSearchEditorAction.LABEL)) - ); - } - this.refreshAction = this._register(this.instantiationService.createInstance(RefreshAction, RefreshAction.ID, RefreshAction.LABEL)); this.cancelAction = this._register(this.instantiationService.createInstance(CancelSearchAction, CancelSearchAction.ID, CancelSearchAction.LABEL)); this.toggleCollapseAction = this._register(this.instantiationService.createInstance(ToggleCollapseAndExpandAction, ToggleCollapseAndExpandAction.ID, ToggleCollapseAndExpandAction.LABEL, collapseDeepestExpandedLevelAction, expandAllAction)); @@ -1561,23 +1556,19 @@ export class SearchView extends ViewPane { resultMsg += nls.localize('useIgnoresAndExcludesDisabled', " - exclude settings and ignore files are disabled"); } - if (this.searchConfig.enableSearchEditorPreview) { - dom.append(messageEl, $('span', undefined, resultMsg + ' - ')); - const span = dom.append(messageEl, $('span')); - const openInEditorLink = dom.append(span, $('a.pointer.prominent', undefined, nls.localize('openInEditor.message', "Open in editor"))); + dom.append(messageEl, $('span', undefined, resultMsg + ' - ')); + const span = dom.append(messageEl, $('span')); + const openInEditorLink = dom.append(span, $('a.pointer.prominent', undefined, nls.localize('openInEditor.message', "Open in editor"))); - openInEditorLink.title = appendKeyBindingLabel( - nls.localize('openInEditor.tooltip', "Copy current search results to an editor"), - this.keybindingService.lookupKeybinding(Constants.OpenInEditorCommandId), this.keybindingService); + openInEditorLink.title = appendKeyBindingLabel( + nls.localize('openInEditor.tooltip', "Copy current search results to an editor"), + this.keybindingService.lookupKeybinding(Constants.OpenInEditorCommandId), this.keybindingService); - this.messageDisposables.push(dom.addDisposableListener(openInEditorLink, dom.EventType.CLICK, (e: MouseEvent) => { - dom.EventHelper.stop(e, false); - this.instantiationService.invokeFunction(createEditorFromSearchResult, this.searchResult, this.searchIncludePattern.getValue(), this.searchExcludePattern.getValue()); - })); + this.messageDisposables.push(dom.addDisposableListener(openInEditorLink, dom.EventType.CLICK, (e: MouseEvent) => { + dom.EventHelper.stop(e, false); + this.instantiationService.invokeFunction(createEditorFromSearchResult, this.searchResult, this.searchIncludePattern.getValue(), this.searchExcludePattern.getValue()); + })); - } else { - dom.append(messageEl, $('span', undefined, resultMsg)); - } this.reLayout(); } else if (!msgWasHidden) { dom.hide(this.messagesElement); diff --git a/src/vs/workbench/contrib/searchEditor/browser/constants.ts b/src/vs/workbench/contrib/searchEditor/browser/constants.ts index f9e5cec7945..05d0fd65dd0 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/constants.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/constants.ts @@ -13,7 +13,6 @@ export const ToggleSearchEditorWholeWordCommandId = 'toggleSearchEditorWholeWord export const ToggleSearchEditorRegexCommandId = 'toggleSearchEditorRegex'; export const ToggleSearchEditorContextLinesCommandId = 'toggleSearchEditorContextLines'; -export const EnableSearchEditorPreview = new RawContextKey('previewSearchEditor', false); export const InSearchEditor = new RawContextKey('inSearchEditor', false); export const SearchEditorScheme = 'search-editor'; diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts index d88a977a7b5..2f769407368 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts @@ -10,7 +10,6 @@ import { URI } from 'vs/base/common/uri'; import { ToggleCaseSensitiveKeybinding, ToggleRegexKeybinding, ToggleWholeWordKeybinding } from 'vs/editor/contrib/find/findModel'; import { localize } from 'vs/nls'; import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -29,7 +28,8 @@ import { SearchEditor } from 'vs/workbench/contrib/searchEditor/browser/searchEd import { OpenResultsInEditorAction, OpenSearchEditorAction, toggleSearchEditorCaseSensitiveCommand, toggleSearchEditorContextLinesCommand, toggleSearchEditorRegexCommand, toggleSearchEditorWholeWordCommand } from 'vs/workbench/contrib/searchEditor/browser/searchEditorActions'; import { getOrMakeSearchEditorInput, SearchEditorInput } from 'vs/workbench/contrib/searchEditor/browser/searchEditorInput'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { ISearchConfigurationProperties } from 'vs/workbench/services/search/common/search'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; //#region Editor Descriptior Registry.as(EditorExtensions.Editors).registerEditor( @@ -51,16 +51,7 @@ class SearchEditorContribution implements IWorkbenchContribution { @IInstantiationService protected readonly instantiationService: IInstantiationService, @ITelemetryService protected readonly telemetryService: ITelemetryService, @IContextKeyService protected readonly contextKeyService: IContextKeyService, - @IConfigurationService private readonly configurationService: IConfigurationService, ) { - const enableSearchEditorPreview = SearchEditorConstants.EnableSearchEditorPreview.bindTo(this.contextKeyService); - - enableSearchEditorPreview.set(this.searchConfig.enableSearchEditorPreview); - configurationService.onDidChangeConfiguration(e => { - if (e.affectsConfiguration('search.previewSearchEditor')) { - enableSearchEditorPreview.set(this.searchConfig.enableSearchEditorPreview); - } - }); this.editorService.overrideOpenEditor((editor, options, group) => { const resource = editor.resource; @@ -83,10 +74,6 @@ class SearchEditorContribution implements IWorkbenchContribution { return { override: Promise.resolve(opened) }; }); } - - private get searchConfig(): ISearchConfigurationProperties { - return this.configurationService.getValue('search'); - } } const workbenchContributionsRegistry = Registry.as(WorkbenchExtensions.Workbench); @@ -167,12 +154,10 @@ const category = localize('search', "Search Editor"); registry.registerWorkbenchAction( SyncActionDescriptor.create(OpenResultsInEditorAction, OpenResultsInEditorAction.ID, OpenResultsInEditorAction.LABEL, { mac: { primary: KeyMod.CtrlCmd | KeyCode.Enter } }, - ContextKeyExpr.and(SearchConstants.HasSearchResults, SearchConstants.SearchViewFocusedKey, SearchEditorConstants.EnableSearchEditorPreview)), - 'Search Editor: Open Results in Editor', category, - ContextKeyExpr.and(SearchEditorConstants.EnableSearchEditorPreview)); + ContextKeyExpr.and(SearchConstants.HasSearchResults, SearchConstants.SearchViewFocusedKey)), + 'Search Editor: Open Results in Editor', category); registry.registerWorkbenchAction( SyncActionDescriptor.create(OpenSearchEditorAction, OpenSearchEditorAction.ID, OpenSearchEditorAction.LABEL), - 'Search Editor: Open New Search Editor', category, - ContextKeyExpr.and(SearchEditorConstants.EnableSearchEditorPreview)); + 'Search Editor: Open New Search Editor', category); //#endregion diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts index cdf0e788d75..edabee77d45 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts @@ -203,7 +203,7 @@ export class SearchEditor extends BaseTextEditor { this.searchResultEditor = super.getControl() as CodeEditorWidget; this.searchResultEditor.onMouseUp(e => { if (e.event.detail === 2) { - const behaviour = this.configurationService.getValue('search').searchEditorPreview.doubleClickBehaviour; + const behaviour = this.configurationService.getValue('search').searchEditor.doubleClickBehaviour; const position = e.target.position; if (position && behaviour !== 'selectWord') { const line = this.searchResultEditor.getModel()?.getLineContent(position.lineNumber) ?? ''; diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions.ts index 7f1b02bff84..30dcf6a88a3 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions.ts @@ -61,7 +61,6 @@ export class OpenSearchEditorAction extends Action { static readonly LABEL = localize('search.openNewEditor', "Open New Search Editor"); constructor(id: string, label: string, - @IConfigurationService private configurationService: IConfigurationService, @IInstantiationService private readonly instantiationService: IInstantiationService, ) { super(id, label, 'codicon-new-file'); @@ -76,9 +75,7 @@ export class OpenSearchEditorAction extends Action { } async run() { - if (this.configurationService.getValue('search').enableSearchEditorPreview) { - await this.instantiationService.invokeFunction(openNewSearchEditor); - } + await this.instantiationService.invokeFunction(openNewSearchEditor); } } @@ -89,7 +86,6 @@ export class OpenResultsInEditorAction extends Action { constructor(id: string, label: string, @IViewsService private viewsService: IViewsService, - @IConfigurationService private configurationService: IConfigurationService, @IInstantiationService private readonly instantiationService: IInstantiationService, ) { super(id, label, 'codicon-go-to-file'); @@ -106,7 +102,7 @@ export class OpenResultsInEditorAction extends Action { async run() { const searchView = getSearchView(this.viewsService); - if (searchView && this.configurationService.getValue('search').enableSearchEditorPreview) { + if (searchView) { await this.instantiationService.invokeFunction(createEditorFromSearchResult, searchView.searchResult, searchView.searchIncludePattern.getValue(), searchView.searchExcludePattern.getValue()); } } diff --git a/src/vs/workbench/services/search/common/search.ts b/src/vs/workbench/services/search/common/search.ts index f8a834f47fe..e646364a885 100644 --- a/src/vs/workbench/services/search/common/search.ts +++ b/src/vs/workbench/services/search/common/search.ts @@ -340,9 +340,7 @@ export interface ISearchConfigurationProperties { collapseResults: 'auto' | 'alwaysCollapse' | 'alwaysExpand'; searchOnType: boolean; searchOnTypeDebouncePeriod: number; - enableSearchEditorPreview: boolean; - searchEditorPreview: { doubleClickBehaviour: 'selectWord' | 'goToLocation' | 'openLocationToSide' }; - searchEditorPreviewForceAbsolutePaths: boolean; + searchEditor: { doubleClickBehaviour: 'selectWord' | 'goToLocation' | 'openLocationToSide' }; sortOrder: SearchSortOrder; } From d9eaddaee18a2a6356679a19905ab09d8890a18e Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Fri, 21 Feb 2020 16:34:56 -0800 Subject: [PATCH 052/200] Add command to rerun search editor query --- .../workbench/contrib/searchEditor/browser/constants.ts | 1 + .../searchEditor/browser/searchEditor.contribution.ts | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/src/vs/workbench/contrib/searchEditor/browser/constants.ts b/src/vs/workbench/contrib/searchEditor/browser/constants.ts index 05d0fd65dd0..890ea78e730 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/constants.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/constants.ts @@ -12,6 +12,7 @@ export const ToggleSearchEditorCaseSensitiveCommandId = 'toggleSearchEditorCaseS export const ToggleSearchEditorWholeWordCommandId = 'toggleSearchEditorWholeWord'; export const ToggleSearchEditorRegexCommandId = 'toggleSearchEditorRegex'; export const ToggleSearchEditorContextLinesCommandId = 'toggleSearchEditorContextLines'; +export const RerunSearchEditorSearchCommandId = 'rerunSearchEditorSearch'; export const InSearchEditor = new RawContextKey('inSearchEditor', false); diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts index 2f769407368..9f3e023d83c 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts @@ -145,6 +145,15 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ primary: KeyMod.Alt | KeyCode.KEY_L, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_L } }); + +CommandsRegistry.registerCommand( + SearchEditorConstants.RerunSearchEditorSearchCommandId, + (accessor: ServicesAccessor) => { + const activeControl = accessor.get(IEditorService).activeControl; + if (activeControl instanceof SearchEditor) { + activeControl.triggerSearch({ resetCursor: false }); + } + }); //#endregion //#region Actions From 33a65245075e4d18908652865a79cf5489c30f40 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Fri, 21 Feb 2020 18:58:09 -0800 Subject: [PATCH 053/200] view dnd improvements --- .../workbench/browser/parts/compositeBar.ts | 31 +++++++++++++++---- .../workbench/browser/parts/compositePart.ts | 2 ++ .../browser/parts/sidebar/sidebarPart.ts | 26 ++++++++++++++++ 3 files changed, 53 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/browser/parts/compositeBar.ts b/src/vs/workbench/browser/parts/compositeBar.ts index 3d4807ced1d..5c19e2a5b99 100644 --- a/src/vs/workbench/browser/parts/compositeBar.ts +++ b/src/vs/workbench/browser/parts/compositeBar.ts @@ -59,8 +59,16 @@ export class CompositeDragAndDrop implements ICompositeDragAndDrop { } else { this.moveComposite(dragData.id, targetCompositeId); } + } else { + const draggedViews = this.viewDescriptorService.getViewDescriptors(currentContainer).allViewDescriptors; + if (draggedViews.length === 1 && draggedViews[0].canMoveView) { + dragData.type = 'view'; + dragData.id = draggedViews[0].id; + } } - } else { + } + + if (dragData.type === 'view') { const viewDescriptor = this.viewDescriptorService.getViewDescriptor(dragData.id); if (viewDescriptor && viewDescriptor.canMoveView) { if (targetCompositeId) { @@ -105,7 +113,21 @@ export class CompositeDragAndDrop implements ICompositeDragAndDrop { // ... across view containers but without a destination composite if (!targetCompositeId) { - return false; + const draggedViews = this.viewDescriptorService.getViewDescriptors(currentContainer)!.allViewDescriptors; + if (draggedViews.some(vd => !vd.canMoveView)) { + return false; + } + + if (draggedViews.length !== 1) { + return false; + } + + const defaultLocation = viewContainerRegistry.getViewContainerLocation(this.viewDescriptorService.getDefaultContainer(draggedViews[0].id)!); + if (this.targetContainerLocation === ViewContainerLocation.Sidebar && this.targetContainerLocation !== defaultLocation) { + return false; + } + + return true; } // ... from panel to the sidebar @@ -241,10 +263,7 @@ export class CompositeBar extends Widget implements ICompositeBar { const draggedCompositeId = data[0].id; this.compositeTransfer.clearData(DraggedCompositeIdentifier.prototype); - const targetItem = this.model.visibleItems[this.model.visibleItems.length - 1]; - if (targetItem && targetItem.id !== draggedCompositeId) { - this.move(draggedCompositeId, targetItem.id); - } + this.options.dndHandler.drop(new CompositeDragAndDropData('composite', draggedCompositeId), undefined, e); } } diff --git a/src/vs/workbench/browser/parts/compositePart.ts b/src/vs/workbench/browser/parts/compositePart.ts index b46b49c60ff..27db5400aa3 100644 --- a/src/vs/workbench/browser/parts/compositePart.ts +++ b/src/vs/workbench/browser/parts/compositePart.ts @@ -59,6 +59,7 @@ export abstract class CompositePart extends Part { protected readonly onDidCompositeClose = this._register(new Emitter()); protected toolBar: ToolBar | undefined; + protected titleLabelElement: HTMLElement | undefined; private mapCompositeToCompositeContainer = new Map(); private mapActionsBindingToComposite = new Map void>(); @@ -402,6 +403,7 @@ export abstract class CompositePart extends Part { protected createTitleLabel(parent: HTMLElement): ICompositeTitleLabel { const titleContainer = append(parent, $('.title-label')); const titleLabel = append(titleContainer, $('h2')); + this.titleLabelElement = titleLabel; const $this = this; return { diff --git a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts index 72ec6b5eb82..02a5769f116 100644 --- a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts +++ b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts @@ -33,6 +33,9 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { LayoutPriority } from 'vs/base/browser/ui/grid/grid'; import { assertIsDefined } from 'vs/base/common/types'; +import { LocalSelectionTransfer } from 'vs/workbench/browser/dnd'; +import { DraggedViewIdentifier } from 'vs/workbench/browser/parts/views/viewPaneContainer'; +import { DraggedCompositeIdentifier } from 'vs/workbench/browser/parts/compositeBarActions'; export class SidebarPart extends CompositePart implements IViewletService { @@ -163,6 +166,29 @@ export class SidebarPart extends CompositePart implements IViewletServi this.onTitleAreaContextMenu(new StandardMouseEvent(e)); })); + this.titleLabelElement!.draggable = true; + this._register(addDisposableListener(this.titleLabelElement!, EventType.DRAG_START, e => { + const activeViewlet = this.getActiveViewlet(); + if (activeViewlet) { + const visibleViews = activeViewlet.getViewPaneContainer().views.filter(v => v.isVisible()); + if (visibleViews.length === 1) { + LocalSelectionTransfer.getInstance().setData([new DraggedViewIdentifier(visibleViews[0].id)], DraggedViewIdentifier.prototype); + } else { + LocalSelectionTransfer.getInstance().setData([new DraggedCompositeIdentifier(activeViewlet.getId())], DraggedCompositeIdentifier.prototype); + } + } + })); + + this._register(addDisposableListener(this.titleLabelElement!, EventType.DRAG_END, e => { + if (LocalSelectionTransfer.getInstance().hasData(DraggedViewIdentifier.prototype)) { + LocalSelectionTransfer.getInstance().clearData(DraggedViewIdentifier.prototype); + } + + if (LocalSelectionTransfer.getInstance().hasData(DraggedCompositeIdentifier.prototype)) { + LocalSelectionTransfer.getInstance().clearData(DraggedCompositeIdentifier.prototype); + } + })); + return titleArea; } From 8d8a0ca03a97425b791dae6e2939dc91852d7a1c Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Sat, 22 Feb 2020 14:46:34 +0100 Subject: [PATCH 054/200] Switch to run on Linux because it is faster --- .github/workflows/ci.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c88e4255aff..59cde5076c8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -118,12 +118,21 @@ jobs: name: Run Integration Tests (Electron) monaco: - runs-on: macos-latest + runs-on: ubuntu-latest env: CHILD_CONCURRENCY: "1" GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - uses: actions/checkout@v1 + # TODO: rename azure-pipelines/linux/xvfb.init to github-actions + - run: | + sudo apt-get update + sudo apt-get install -y libxkbfile-dev pkg-config libsecret-1-dev libxss1 dbus xvfb libgtk-3-0 libgbm1 + sudo cp build/azure-pipelines/linux/xvfb.init /etc/init.d/xvfb + sudo chmod +x /etc/init.d/xvfb + sudo update-rc.d xvfb defaults + sudo service xvfb start + name: Setup Build Environment - uses: actions/setup-node@v1 with: node-version: 10 From 4f21fd58a8c34a6ef8dfa0ec03266bddf6875c6c Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Sat, 22 Feb 2020 15:01:58 +0100 Subject: [PATCH 055/200] Move gulp specific code to gulpfile.editor.js --- build/gulpfile.editor.js | 28 +++++++++++++++++++++++++++- build/lib/standalone.js | 24 ------------------------ build/lib/standalone.ts | 31 ------------------------------- 3 files changed, 27 insertions(+), 56 deletions(-) diff --git a/build/gulpfile.editor.js b/build/gulpfile.editor.js index 5f112e7ef06..2f5d8b8d8a2 100644 --- a/build/gulpfile.editor.js +++ b/build/gulpfile.editor.js @@ -16,6 +16,8 @@ const cp = require('child_process'); const compilation = require('./lib/compilation'); const monacoapi = require('./monaco/api'); const fs = require('fs'); +const webpack = require('webpack'); +const webpackGulp = require('webpack-stream'); let root = path.dirname(__dirname); let sha1 = util.getVersion(root); @@ -359,7 +361,31 @@ gulp.task('editor-distro', ); const bundleEditorESMTask = task.define('editor-esm-bundle-webpack', () => { - return standalone.bundleESM(); + const result = es.through(); + + const webpackConfigPath = path.join(root, 'build/monaco/monaco.webpack.config.js'); + + const webpackConfig = { + ...require(webpackConfigPath), + ...{ mode: 'production' } + }; + + const webpackDone = (err, stats) => { + if (err) { + result.emit('error', err); + return; + } + const { compilation } = stats; + if (compilation.errors.length > 0) { + result.emit('error', compilation.errors.join('\n')); + } + if (compilation.warnings.length > 0) { + result.emit('data', compilation.warnings.join('\n')); + } + }; + + return webpackGulp(webpackConfig, webpack, webpackDone) + .pipe(gulp.dest('out-editor-esm-bundle')); }); gulp.task('editor-esm-bundle', diff --git a/build/lib/standalone.js b/build/lib/standalone.js index 0783ca06ffd..ccfd7b45d23 100644 --- a/build/lib/standalone.js +++ b/build/lib/standalone.js @@ -5,13 +5,9 @@ *--------------------------------------------------------------------------------------------*/ Object.defineProperty(exports, "__esModule", { value: true }); const ts = require("typescript"); -const es = require("event-stream"); const fs = require("fs"); const path = require("path"); const tss = require("./treeshaking"); -const gulp = require("gulp"); -const webpack = require("webpack"); -const webpackGulp = require('webpack-stream'); const REPO_ROOT = path.join(__dirname, '../../'); const SRC_DIR = path.join(REPO_ROOT, 'src'); let dirCache = {}; @@ -315,23 +311,3 @@ function transportCSS(module, enqueue, write) { return haystack.length >= needle.length && haystack.substr(0, needle.length) === needle; } } -function bundleESM() { - const result = es.through(); - const webpackConfigPath = path.join(__dirname, '../../build/monaco/monaco.webpack.config.js'); - const webpackConfig = Object.assign(Object.assign({}, require(webpackConfigPath)), { mode: 'production' }); - const webpackDone = (err, stats) => { - if (err) { - result.emit('error', err); - } - const { compilation } = stats; - if (compilation.errors.length > 0) { - result.emit('error', compilation.errors.join('\n')); - } - if (compilation.warnings.length > 0) { - result.emit('data', compilation.warnings.join('\n')); - } - }; - return webpackGulp(webpackConfig, webpack, webpackDone) - .pipe(gulp.dest('out-editor-esm-bundle')); -} -exports.bundleESM = bundleESM; diff --git a/build/lib/standalone.ts b/build/lib/standalone.ts index 8ba25265059..f80c611e6fc 100644 --- a/build/lib/standalone.ts +++ b/build/lib/standalone.ts @@ -4,13 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import * as ts from 'typescript'; -import * as es from 'event-stream'; import * as fs from 'fs'; import * as path from 'path'; import * as tss from './treeshaking'; -import * as gulp from 'gulp'; -import * as webpack from 'webpack'; -const webpackGulp = require('webpack-stream'); const REPO_ROOT = path.join(__dirname, '../../'); const SRC_DIR = path.join(REPO_ROOT, 'src'); @@ -364,30 +360,3 @@ function transportCSS(module: string, enqueue: (module: string) => void, write: return haystack.length >= needle.length && haystack.substr(0, needle.length) === needle; } } - -export function bundleESM() { - const result = es.through(); - - const webpackConfigPath = path.join(__dirname, '../../build/monaco/monaco.webpack.config.js'); - - const webpackConfig = { - ...require(webpackConfigPath), - ...{ mode: 'production' } - }; - - const webpackDone = (err: any, stats: any) => { - if (err) { - result.emit('error', err); - } - const { compilation } = stats; - if (compilation.errors.length > 0) { - result.emit('error', compilation.errors.join('\n')); - } - if (compilation.warnings.length > 0) { - result.emit('data', compilation.warnings.join('\n')); - } - }; - - return webpackGulp(webpackConfig, webpack, webpackDone) - .pipe(gulp.dest('out-editor-esm-bundle')); -} From c95e2e4bf2ded3709c7353794939f9312b688021 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Sat, 22 Feb 2020 15:40:20 +0100 Subject: [PATCH 056/200] Support members with dots in their name --- build/monaco/api.js | 5 +++-- build/monaco/api.ts | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/build/monaco/api.js b/build/monaco/api.js index 0fbaf7335b7..bd656f2bcd1 100644 --- a/build/monaco/api.js +++ b/build/monaco/api.js @@ -135,11 +135,12 @@ function getMassagedTopLevelDeclarationText(sourceFile, declaration, importName, } else { const memberName = member.name.text; + const memberAccess = (memberName.indexOf('.') >= 0 ? `['${memberName}']` : `.${memberName}`); if (isStatic(member)) { - usage.push(`a = ${staticTypeName}.${memberName};`); + usage.push(`a = ${staticTypeName}${memberAccess};`); } else { - usage.push(`a = (<${instanceTypeName}>b).${memberName};`); + usage.push(`a = (<${instanceTypeName}>b)${memberAccess};`); } } } diff --git a/build/monaco/api.ts b/build/monaco/api.ts index 511768ee64b..f3542988a4b 100644 --- a/build/monaco/api.ts +++ b/build/monaco/api.ts @@ -167,10 +167,11 @@ function getMassagedTopLevelDeclarationText(sourceFile: ts.SourceFile, declarati result = result.replace(memberText, ''); } else { const memberName = (member.name).text; + const memberAccess = (memberName.indexOf('.') >= 0 ? `['${memberName}']` : `.${memberName}`); if (isStatic(member)) { - usage.push(`a = ${staticTypeName}.${memberName};`); + usage.push(`a = ${staticTypeName}${memberAccess};`); } else { - usage.push(`a = (<${instanceTypeName}>b).${memberName};`); + usage.push(`a = (<${instanceTypeName}>b)${memberAccess};`); } } } catch (err) { From 4d92b1d1f8a43465c50e82d26d432b101283b142 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Sat, 22 Feb 2020 16:02:23 +0100 Subject: [PATCH 057/200] Fix minor issues --- build/gulpfile.editor.js | 5 +---- build/monaco/esm.core.js | 2 +- build/monaco/monaco.webpack.config.js | 29 +++++++++------------------ 3 files changed, 11 insertions(+), 25 deletions(-) diff --git a/build/gulpfile.editor.js b/build/gulpfile.editor.js index 2f5d8b8d8a2..9278f689ec3 100644 --- a/build/gulpfile.editor.js +++ b/build/gulpfile.editor.js @@ -392,17 +392,14 @@ gulp.task('editor-esm-bundle', task.series( task.parallel( util.rimraf('out-editor-src'), - util.rimraf('out-editor-build'), util.rimraf('out-editor-esm'), util.rimraf('out-monaco-editor-core'), - util.rimraf('out-editor'), - util.rimraf('out-editor-min') + util.rimraf('out-editor-esm-bundle'), ), extractEditorSrcTask, createESMSourcesAndResourcesTask, compileEditorESMTask, bundleEditorESMTask, - finalEditorResourcesTask ) ); diff --git a/build/monaco/esm.core.js b/build/monaco/esm.core.js index d0cc7e9290a..b84b5fb4f41 100644 --- a/build/monaco/esm.core.js +++ b/build/monaco/esm.core.js @@ -11,7 +11,7 @@ self.MonacoEnvironment = { getWorkerUrl: function (moduleId, label) { return './editor.worker.bundle.js'; } -} +}; monaco.editor.create(document.getElementById('container'), { value: [ diff --git a/build/monaco/monaco.webpack.config.js b/build/monaco/monaco.webpack.config.js index fe2067648d4..974a341a197 100644 --- a/build/monaco/monaco.webpack.config.js +++ b/build/monaco/monaco.webpack.config.js @@ -8,8 +8,8 @@ const path = require('path'); module.exports = { mode: 'production', entry: { - "core": './build/monaco/esm.core.js', - "editor.worker": './out-monaco-editor-core/esm/vs/editor/editor.worker.js' + 'core': './build/monaco/esm.core.js', + 'editor.worker': './out-monaco-editor-core/esm/vs/editor/editor.worker.js' }, output: { globalObject: 'self', @@ -17,24 +17,13 @@ module.exports = { path: path.resolve(__dirname, 'dist') }, module: { - rules: [ - { - test: /\.css$/, - use: ['style-loader', 'css-loader'], - }, - { - test: /\.(woff(2)?|ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/, - use: [ - { - loader: 'file-loader', - options: { - name: '[name].[ext]', - outputPath: 'fonts/' - } - } - ] - } - ] + rules: [{ + test: /\.css$/, + use: ['style-loader', 'css-loader'] + }, { + test: /\.ttf$/, + use: ['file-loader'] + }] }, resolve: { alias: { From 8c2f36036bfc9acf7de6f4e5cd76b7515ad6e600 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Sat, 22 Feb 2020 18:48:20 +0100 Subject: [PATCH 058/200] untitled - proper dirty reporting even when not resolved --- .../untitled/common/untitledTextEditorModel.ts | 2 +- .../test/browser/untitledTextEditor.test.ts | 17 +++++++---------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/services/untitled/common/untitledTextEditorModel.ts b/src/vs/workbench/services/untitled/common/untitledTextEditorModel.ts index d520cd9674b..e42c09b2e33 100644 --- a/src/vs/workbench/services/untitled/common/untitledTextEditorModel.ts +++ b/src/vs/workbench/services/untitled/common/untitledTextEditorModel.ts @@ -100,7 +100,7 @@ export class UntitledTextEditorModel extends BaseTextEditorModel implements IUnt return this.labelService.getUriBasenameLabel(this.resource); } - private dirty = false; + private dirty = this.hasAssociatedFilePath || !!this.initialValue; private ignoreDirtyOnModelContentChange = false; private versionId = 0; diff --git a/src/vs/workbench/services/untitled/test/browser/untitledTextEditor.test.ts b/src/vs/workbench/services/untitled/test/browser/untitledTextEditor.test.ts index 1e44a563f4c..9b2b8015372 100644 --- a/src/vs/workbench/services/untitled/test/browser/untitledTextEditor.test.ts +++ b/src/vs/workbench/services/untitled/test/browser/untitledTextEditor.test.ts @@ -11,7 +11,6 @@ import { IUntitledTextEditorService, UntitledTextEditorService } from 'vs/workbe import { workbenchInstantiationService, TestServiceAccessor } from 'vs/workbench/test/browser/workbenchTestServices'; import { snapshotToString } from 'vs/workbench/services/textfile/common/textfiles'; import { ModesRegistry, PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry'; -import { IWorkingCopy } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; import { Range } from 'vs/editor/common/core/range'; import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput'; @@ -120,9 +119,13 @@ suite('Untitled text editors', () => { test('associated resource is dirty', async () => { const service = accessor.untitledTextEditorService; const file = URI.file(join('C:\\', '/foo/file.txt')); - const untitled = await service.resolve({ associatedResource: file }); - assert.ok(untitled.hasAssociatedFilePath); + const untitled = instantiationService.createInstance(UntitledTextEditorInput, service.create({ associatedResource: file })); + assert.ok(untitled.isDirty()); + + const model = await untitled.resolve(); + + assert.ok(model.hasAssociatedFilePath); assert.equal(untitled.isDirty(), true); untitled.dispose(); @@ -197,20 +200,14 @@ suite('Untitled text editors', () => { const workingCopyService = accessor.workingCopyService; const untitled = instantiationService.createInstance(UntitledTextEditorInput, service.create({ initialValue: 'Hello World' })); - - let onDidChangeDirty: IWorkingCopy | undefined = undefined; - const listener = workingCopyService.onDidChangeDirty(copy => { - onDidChangeDirty = copy; - }); + assert.ok(untitled.isDirty()); // dirty const model = await untitled.resolve(); assert.ok(model.isDirty()); assert.equal(workingCopyService.dirtyCount, 1); - assert.equal(onDidChangeDirty, model); untitled.dispose(); - listener.dispose(); model.dispose(); }); From 3cf529d506f977a4b7a7941175da90d190fed14e Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Sat, 22 Feb 2020 19:02:55 +0100 Subject: [PATCH 059/200] remove unused code --- src/vs/base/node/encoding.ts | 4 --- src/vs/base/node/pfs.ts | 16 +-------- src/vs/base/node/stream.ts | 64 ------------------------------------ 3 files changed, 1 insertion(+), 83 deletions(-) diff --git a/src/vs/base/node/encoding.ts b/src/vs/base/node/encoding.ts index 4a9462c646a..7cf834ece2f 100644 --- a/src/vs/base/node/encoding.ts +++ b/src/vs/base/node/encoding.ts @@ -142,10 +142,6 @@ export function decode(buffer: Buffer, encoding: string): string { return iconv.decode(buffer, toNodeEncoding(encoding)); } -export function encode(content: string | Buffer, encoding: string, options?: { addBOM?: boolean }): Buffer { - return iconv.encode(content as string /* TODO report into upstream typings */, toNodeEncoding(encoding), options); -} - export function encodingExists(encoding: string): boolean { return iconv.encodingExists(toNodeEncoding(encoding)); } diff --git a/src/vs/base/node/pfs.ts b/src/vs/base/node/pfs.ts index 6cf71160c50..491c760df4b 100644 --- a/src/vs/base/node/pfs.ts +++ b/src/vs/base/node/pfs.ts @@ -14,7 +14,6 @@ import { promisify } from 'util'; import { isRootOrDriveLetter } from 'vs/base/common/extpath'; import { generateUuid } from 'vs/base/common/uuid'; import { normalizeNFC } from 'vs/base/common/normalization'; -import { encode } from 'vs/base/node/encoding'; // See https://github.com/Microsoft/vscode/issues/30180 const WIN32_MAX_FILE_SIZE = 300 * 1024 * 1024; // 300 MB @@ -320,10 +319,6 @@ function ensureWriteFileQueue(queueKey: string): Queue { export interface IWriteFileOptions { mode?: number; flag?: string; - encoding?: { - charset: string; - addBOM: boolean; - }; } interface IEnsuredWriteFileOptions extends IWriteFileOptions { @@ -339,10 +334,6 @@ let canFlush = true; // // See https://github.com/nodejs/node/blob/v5.10.0/lib/fs.js#L1194 function doWriteFileAndFlush(path: string, data: string | Buffer | Uint8Array, options: IEnsuredWriteFileOptions, callback: (error: Error | null) => void): void { - if (options.encoding) { - data = encode(data instanceof Uint8Array ? Buffer.from(data) : data, options.encoding.charset, { addBOM: options.encoding.addBOM }); - } - if (!canFlush) { return fs.writeFile(path, data, { mode: options.mode, flag: options.flag }, callback); } @@ -378,10 +369,6 @@ function doWriteFileAndFlush(path: string, data: string | Buffer | Uint8Array, o export function writeFileSync(path: string, data: string | Buffer, options?: IWriteFileOptions): void { const ensuredOptions = ensureWriteOptions(options); - if (ensuredOptions.encoding) { - data = encode(data, ensuredOptions.encoding.charset, { addBOM: ensuredOptions.encoding.addBOM }); - } - if (!canFlush) { return fs.writeFileSync(path, data, { mode: ensuredOptions.mode, flag: ensuredOptions.flag }); } @@ -413,8 +400,7 @@ function ensureWriteOptions(options?: IWriteFileOptions): IEnsuredWriteFileOptio return { mode: typeof options.mode === 'number' ? options.mode : 0o666, - flag: typeof options.flag === 'string' ? options.flag : 'w', - encoding: options.encoding + flag: typeof options.flag === 'string' ? options.flag : 'w' }; } diff --git a/src/vs/base/node/stream.ts b/src/vs/base/node/stream.ts index 16b73835bd1..12ba5e5d929 100644 --- a/src/vs/base/node/stream.ts +++ b/src/vs/base/node/stream.ts @@ -3,75 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as fs from 'fs'; import { VSBufferReadableStream, VSBufferReadable, VSBuffer } from 'vs/base/common/buffer'; import { Readable } from 'stream'; import { isUndefinedOrNull } from 'vs/base/common/types'; import { UTF8, UTF8_with_bom, UTF8_BOM, UTF16be, UTF16le_BOM, UTF16be_BOM, UTF16le, UTF_ENCODING } from 'vs/base/node/encoding'; -/** - * Reads a file until a matching string is found. - * - * @param file The file to read. - * @param matchingString The string to search for. - * @param chunkBytes The number of bytes to read each iteration. - * @param maximumBytesToRead The maximum number of bytes to read before giving up. - * @param callback The finished callback. - */ -export function readToMatchingString(file: string, matchingString: string, chunkBytes: number, maximumBytesToRead: number): Promise { - return new Promise((resolve, reject) => - fs.open(file, 'r', null, (err, fd) => { - if (err) { - return reject(err); - } - - function end(err: Error | null, result: string | null): void { - fs.close(fd, closeError => { - if (closeError) { - return reject(closeError); - } - - if (err && (err).code === 'EISDIR') { - return reject(err); // we want to bubble this error up (file is actually a folder) - } - - return resolve(result); - }); - } - - const buffer = Buffer.allocUnsafe(maximumBytesToRead); - let offset = 0; - - function readChunk(): void { - fs.read(fd, buffer, offset, chunkBytes, null, (err, bytesRead) => { - if (err) { - return end(err, null); - } - - if (bytesRead === 0) { - return end(null, null); - } - - offset += bytesRead; - - const newLineIndex = buffer.indexOf(matchingString); - if (newLineIndex >= 0) { - return end(null, buffer.toString('utf8').substr(0, newLineIndex)); - } - - if (offset >= maximumBytesToRead) { - return end(new Error(`Could not find ${matchingString} in first ${maximumBytesToRead} bytes of ${file}`), null); - } - - return readChunk(); - }); - } - - readChunk(); - }) - ); -} - export function streamToNodeReadable(stream: VSBufferReadableStream): Readable { return new class extends Readable { private listening = false; From 1a558781ea0ee172536538c39ed630ca83de0782 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Sat, 22 Feb 2020 19:19:49 +0100 Subject: [PATCH 060/200] Editor icon does not change when changing from plain text (fix #91205) --- src/vs/workbench/browser/labels.ts | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/browser/labels.ts b/src/vs/workbench/browser/labels.ts index b92199f1f86..8f88e4acdee 100644 --- a/src/vs/workbench/browser/labels.ts +++ b/src/vs/workbench/browser/labels.ts @@ -9,14 +9,13 @@ import { dirname, isEqual, basenameOrAuthority } from 'vs/base/common/resources' import { IconLabel, IIconLabelValueOptions, IIconLabelCreationOptions } from 'vs/base/browser/ui/iconLabel/iconLabel'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IModeService } from 'vs/editor/common/services/modeService'; -import { PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IModelService } from 'vs/editor/common/services/modelService'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IDecorationsService, IResourceDecorationChangeEvent } from 'vs/workbench/services/decorations/browser/decorations'; import { Schemas } from 'vs/base/common/network'; -import { FileKind, FILES_ASSOCIATIONS_CONFIG, IFileService } from 'vs/platform/files/common/files'; +import { FileKind, FILES_ASSOCIATIONS_CONFIG } from 'vs/platform/files/common/files'; import { ITextModel } from 'vs/editor/common/model'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { Event, Emitter } from 'vs/base/common/event'; @@ -89,7 +88,6 @@ export class ResourceLabels extends Disposable { @IModelService private readonly modelService: IModelService, @IDecorationsService private readonly decorationsService: IDecorationsService, @IThemeService private readonly themeService: IThemeService, - @IFileService private readonly fileService: IFileService, @ILabelService private readonly labelService: ILabelService, @ITextFileService private readonly textFileService: ITextFileService ) { @@ -114,10 +112,6 @@ export class ResourceLabels extends Disposable { return; // we need the resource to compare } - if (this.fileService.canHandleResource(e.model.uri) && e.oldModeId === PLAINTEXT_MODE_ID) { - return; // ignore transitions in files from no mode to specific mode because this happens each time a model is created - } - this._widgets.forEach(widget => widget.notifyModelModeChanged(e.model)); })); @@ -218,11 +212,10 @@ export class ResourceLabel extends ResourceLabels { @IModelService modelService: IModelService, @IDecorationsService decorationsService: IDecorationsService, @IThemeService themeService: IThemeService, - @IFileService fileService: IFileService, @ILabelService labelService: ILabelService, @ITextFileService textFileService: ITextFileService ) { - super(DEFAULT_LABELS_CONTAINER, instantiationService, extensionService, configurationService, modelService, decorationsService, themeService, fileService, labelService, textFileService); + super(DEFAULT_LABELS_CONTAINER, instantiationService, extensionService, configurationService, modelService, decorationsService, themeService, labelService, textFileService); this._label = this._register(this.create(container, options)); } From 7fc5d9150569247b3494eb3715a078bf7f8e9272 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Sat, 22 Feb 2020 10:21:09 -0800 Subject: [PATCH 061/200] Add "@tag:sync" to settings GUI Fix #91194 --- src/vs/platform/userDataSync/common/userDataSync.ts | 7 +++++-- .../contrib/preferences/browser/settingsEditor2.ts | 2 +- .../workbench/contrib/userDataSync/browser/userDataSync.ts | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/vs/platform/userDataSync/common/userDataSync.ts b/src/vs/platform/userDataSync/common/userDataSync.ts index 69585e9d79f..aed5a8bc650 100644 --- a/src/vs/platform/userDataSync/common/userDataSync.ts +++ b/src/vs/platform/userDataSync/common/userDataSync.ts @@ -87,6 +87,7 @@ export function registerConfiguration(): IDisposable { description: localize('sync.keybindingsPerPlatform', "Synchronize keybindings per platform."), default: true, scope: ConfigurationScope.APPLICATION, + tags: ['sync'] }, 'sync.ignoredExtensions': { 'type': 'array', @@ -95,7 +96,8 @@ export function registerConfiguration(): IDisposable { 'default': [], 'scope': ConfigurationScope.APPLICATION, uniqueItems: true, - disallowSyncIgnore: true + disallowSyncIgnore: true, + tags: ['sync'] }, 'sync.ignoredSettings': { 'type': 'array', @@ -105,7 +107,8 @@ export function registerConfiguration(): IDisposable { $ref: ignoredSettingsSchemaId, additionalProperties: true, uniqueItems: true, - disallowSyncIgnore: true + disallowSyncIgnore: true, + tags: ['sync'] } } }); diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index 392db681d09..30eb7a13953 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -77,7 +77,7 @@ export class SettingsEditor2 extends BaseEditor { private static CONFIG_SCHEMA_UPDATE_DELAYER = 500; private static readonly SUGGESTIONS: string[] = [ - `@${MODIFIED_SETTING_TAG}`, '@tag:usesOnlineServices', `@${EXTENSION_SETTING_TAG}` + `@${MODIFIED_SETTING_TAG}`, '@tag:usesOnlineServices', '@tag:sync', `@${EXTENSION_SETTING_TAG}` ]; private static shouldSettingUpdateFast(type: SettingValueType | SettingValueType[]): boolean { diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index 1ec84ca0d66..f35a821a002 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -965,7 +965,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo }); } run(accessor: ServicesAccessor): any { - accessor.get(IPreferencesService).openGlobalSettings(false, { query: 'sync:' }); + accessor.get(IPreferencesService).openGlobalSettings(false, { query: '@tag:sync' }); } }); } From b91e4c95d4ec5a001c18f14126509dd0e9a99ae8 Mon Sep 17 00:00:00 2001 From: nrayburn Date: Sat, 22 Feb 2020 21:39:20 -0600 Subject: [PATCH 062/200] Add passive false to touch events --- src/vs/base/browser/touch.ts | 4 ++-- src/vs/editor/browser/viewParts/minimap/minimap.ts | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vs/base/browser/touch.ts b/src/vs/base/browser/touch.ts index 9956bc9112b..f8b32d9cb76 100644 --- a/src/vs/base/browser/touch.ts +++ b/src/vs/base/browser/touch.ts @@ -90,9 +90,9 @@ export class Gesture extends Disposable { this.targets = []; this.ignoreTargets = []; this._lastSetTapCountTime = 0; - this._register(DomUtils.addDisposableListener(document, 'touchstart', (e: TouchEvent) => this.onTouchStart(e))); + this._register(DomUtils.addDisposableListener(document, 'touchstart', (e: TouchEvent) => this.onTouchStart(e), { passive: false })); this._register(DomUtils.addDisposableListener(document, 'touchend', (e: TouchEvent) => this.onTouchEnd(e))); - this._register(DomUtils.addDisposableListener(document, 'touchmove', (e: TouchEvent) => this.onTouchMove(e))); + this._register(DomUtils.addDisposableListener(document, 'touchmove', (e: TouchEvent) => this.onTouchMove(e), { passive: false })); } public static addTarget(element: HTMLElement): IDisposable { diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts index 5f5c5cadd9f..d5aa95d0611 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimap.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts @@ -1149,15 +1149,15 @@ class InnerMinimap extends Disposable { this._gestureInProgress = true; this.scrollDueToTouchEvent(e); } - }); + }, { passive: false }); - this._sliderTouchMoveListener = dom.addStandardDisposableListener(this._domNode.domNode, EventType.Change, (e: GestureEvent) => { + this._sliderTouchMoveListener = dom.addDisposableListener(this._domNode.domNode, EventType.Change, (e: GestureEvent) => { e.preventDefault(); e.stopPropagation(); if (this._lastRenderData && this._gestureInProgress) { this.scrollDueToTouchEvent(e); } - }); + }, { passive: false }); this._sliderTouchEndListener = dom.addStandardDisposableListener(this._domNode.domNode, EventType.End, (e: GestureEvent) => { e.preventDefault(); From edb6387b0ce57784d6e21b8ad6ecf4b8d2f0948d Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Sun, 23 Feb 2020 11:22:30 +0100 Subject: [PATCH 063/200] Losing "Recently Opened" quickopen entries (fix #89257) --- .../services/history/browser/history.ts | 129 ++++++++---------- 1 file changed, 58 insertions(+), 71 deletions(-) diff --git a/src/vs/workbench/services/history/browser/history.ts b/src/vs/workbench/services/history/browser/history.ts index 667422f2e44..8f081c5e22e 100644 --- a/src/vs/workbench/services/history/browser/history.ts +++ b/src/vs/workbench/services/history/browser/history.ts @@ -34,7 +34,6 @@ import { addDisposableListener, EventType, EventHelper } from 'vs/base/browser/d import { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; import { Schemas } from 'vs/base/common/network'; import { isEqual } from 'vs/base/common/resources'; -import { ILogService } from 'vs/platform/log/common/log'; /** * Stores the selection & view state of an editor and allows to compare it to other selection states. @@ -112,8 +111,7 @@ export class HistoryService extends Disposable implements IHistoryService { @IWorkspacesService private readonly workspacesService: IWorkspacesService, @IInstantiationService private readonly instantiationService: IInstantiationService, @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService, - @IContextKeyService private readonly contextKeyService: IContextKeyService, - @ILogService private readonly logService: ILogService + @IContextKeyService private readonly contextKeyService: IContextKeyService ) { super(); @@ -277,7 +275,9 @@ export class HistoryService extends Disposable implements IHistoryService { } clear(): void { - this.ensureHistoryLoaded(); + + // History + this.clearRecentlyOpened(); // Navigation (next, previous) this.navigationStackIndex = -1; @@ -289,9 +289,6 @@ export class HistoryService extends Disposable implements IHistoryService { // Closed files this.recentlyClosedFiles = []; - // History - this.clearRecentlyOpened(); - // Context Keys this.updateContextKeys(); } @@ -719,8 +716,8 @@ export class HistoryService extends Disposable implements IHistoryService { private static readonly MAX_HISTORY_ITEMS = 200; private static readonly HISTORY_STORAGE_KEY = 'history.entries'; - private history: Array = []; - private loaded = false; + private history: Array | undefined = undefined; + private readonly resourceFilter = this._register(this.instantiationService.createInstance( ResourceGlobMatcher, (root?: URI) => this.getExcludes(root), @@ -741,11 +738,10 @@ export class HistoryService extends Disposable implements IHistoryService { return; } - this.ensureHistoryLoaded(); - const historyInput = this.preferResourceInput(input); // Remove any existing entry and add to the beginning + this.ensureHistoryLoaded(this.history); this.removeFromHistory(input); this.history.unshift(historyInput); @@ -772,7 +768,7 @@ export class HistoryService extends Disposable implements IHistoryService { } private removeExcludedFromHistory(): void { - this.ensureHistoryLoaded(); + this.ensureHistoryLoaded(this.history); this.history = this.history.filter(e => { const include = this.include(e); @@ -787,7 +783,7 @@ export class HistoryService extends Disposable implements IHistoryService { } private removeFromHistory(arg1: IEditorInput | IResourceInput | FileChangesEvent): void { - this.ensureHistoryLoaded(); + this.ensureHistoryLoaded(this.history); this.history = this.history.filter(e => { const matches = this.matches(arg1, e); @@ -809,17 +805,59 @@ export class HistoryService extends Disposable implements IHistoryService { } getHistory(): ReadonlyArray { - this.ensureHistoryLoaded(); + this.ensureHistoryLoaded(this.history); return this.history.slice(0); } - private ensureHistoryLoaded(): void { - if (!this.loaded) { - this.loadHistory(); + private ensureHistoryLoaded(history: Array | undefined): asserts history { + if (!this.history) { + this.history = this.loadHistory(); + } + } + + private loadHistory(): Array { + let entries: ISerializedEditorHistoryEntry[] = []; + + const entriesRaw = this.storageService.get(HistoryService.HISTORY_STORAGE_KEY, StorageScope.WORKSPACE); + if (entriesRaw) { + entries = coalesce(JSON.parse(entriesRaw)); } - this.loaded = true; + const registry = Registry.as(EditorExtensions.EditorInputFactories); + + return coalesce(entries.map(entry => { + try { + return this.safeLoadHistoryEntry(registry, entry); + } catch (error) { + return undefined; // https://github.com/Microsoft/vscode/issues/60960 + } + })); + } + + private safeLoadHistoryEntry(registry: IEditorInputFactoryRegistry, entry: ISerializedEditorHistoryEntry): IEditorInput | IResourceInput | undefined { + const serializedEditorHistoryEntry = entry; + + // File resource: via URI.revive() + if (serializedEditorHistoryEntry.resourceJSON) { + return { resource: URI.revive(serializedEditorHistoryEntry.resourceJSON) }; + } + + // Editor input: via factory + const { editorInputJSON } = serializedEditorHistoryEntry; + if (editorInputJSON?.deserialized) { + const factory = registry.getEditorInputFactory(editorInputJSON.typeId); + if (factory) { + const input = factory.deserialize(this.instantiationService, editorInputJSON.deserialized); + if (input) { + this.onEditorDispose(input, () => this.removeFromHistory(input), this.editorHistoryListeners); + } + + return withNullAsUndefined(input); + } + } + + return undefined; } private saveState(): void { @@ -850,58 +888,9 @@ export class HistoryService extends Disposable implements IHistoryService { return undefined; })); - this.logService.trace(`[editor history] saving ${entries.length} entries`); this.storageService.store(HistoryService.HISTORY_STORAGE_KEY, JSON.stringify(entries), StorageScope.WORKSPACE); } - private loadHistory(): void { - let entries: ISerializedEditorHistoryEntry[] = []; - - const entriesRaw = this.storageService.get(HistoryService.HISTORY_STORAGE_KEY, StorageScope.WORKSPACE); - if (entriesRaw) { - entries = coalesce(JSON.parse(entriesRaw)); - } - - const registry = Registry.as(EditorExtensions.EditorInputFactories); - - this.history = coalesce(entries.map(entry => { - try { - return this.safeLoadHistoryEntry(registry, entry); - } catch (error) { - this.logService.error(`[editor history] error loading one editor history entry: ${error.toString()}`); - - return undefined; // https://github.com/Microsoft/vscode/issues/60960 - } - })); - - this.logService.trace(`[editor history] loading ${this.history.length} entries`); - } - - private safeLoadHistoryEntry(registry: IEditorInputFactoryRegistry, entry: ISerializedEditorHistoryEntry): IEditorInput | IResourceInput | undefined { - const serializedEditorHistoryEntry = entry; - - // File resource: via URI.revive() - if (serializedEditorHistoryEntry.resourceJSON) { - return { resource: URI.revive(serializedEditorHistoryEntry.resourceJSON) }; - } - - // Editor input: via factory - const { editorInputJSON } = serializedEditorHistoryEntry; - if (editorInputJSON?.deserialized) { - const factory = registry.getEditorInputFactory(editorInputJSON.typeId); - if (factory) { - const input = factory.deserialize(this.instantiationService, editorInputJSON.deserialized); - if (input) { - this.onEditorDispose(input, () => this.removeFromHistory(input), this.editorHistoryListeners); - } - - return withNullAsUndefined(input); - } - } - - return undefined; - } - //#endregion //#region Last Active Workspace/File @@ -925,8 +914,7 @@ export class HistoryService extends Disposable implements IHistoryService { } // Multiple folders: find the last active one - const history = this.getHistory(); - for (const input of history) { + for (const input of this.getHistory()) { if (input instanceof EditorInput) { continue; } @@ -954,8 +942,7 @@ export class HistoryService extends Disposable implements IHistoryService { } getLastActiveFile(filterByScheme: string): URI | undefined { - const history = this.getHistory(); - for (const input of history) { + for (const input of this.getHistory()) { let resource: URI | undefined; if (input instanceof EditorInput) { resource = toResource(input, { filterByScheme }); From 1a03d6b5fca760ce5eaaf4fb9060e590395316bc Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Sun, 23 Feb 2020 11:48:25 +0100 Subject: [PATCH 064/200] Schema update (microsoft/vscode-remote-release#46) --- .../schemas/devContainer.schema.json | 60 ++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/extensions/configuration-editing/schemas/devContainer.schema.json b/extensions/configuration-editing/schemas/devContainer.schema.json index 0d47b27bec9..bfa1799e8ad 100644 --- a/extensions/configuration-editing/schemas/devContainer.schema.json +++ b/extensions/configuration-editing/schemas/devContainer.schema.json @@ -132,7 +132,7 @@ } } }, - "dockerFileContainer": { + "dockerFileAndContext": { "type": "object", "properties": { "dockerFile": { @@ -148,6 +148,64 @@ "dockerFile" ] }, + "dockerFileContainer": { + "oneOf": [ + { + "type": "object", + "properties": { + "build": { + "type": "object", + "description": "Docker build-related options.", + "allOf": [ + { + "$ref": "#/definitions/dockerFileAndContext" + }, + { + "$ref": "#/definitions/buildOptions" + } + ] + } + }, + "required": [ + "build" + ] + }, + { + "allOf": [ + { + "$ref": "#/definitions/dockerFileAndContext" + }, + { + "type": "object", + "properties": { + "build": { + "description": "Docker build-related options.", + "$ref": "#/definitions/buildOptions" + } + } + } + ] + } + ] + }, + "buildOptions": { + "type": "object", + "properties": { + "target": { + "type": "string", + "description": "Target stage in a multi-stage build." + }, + "args": { + "type": "object", + "additionalProperties": { + "type": [ + "string" + ] + }, + "description": "Build arguments." + } + } + }, "imageContainer": { "type": "object", "properties": { From e943540509e979dcff14b2ac00e6676004fe5e92 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Sun, 23 Feb 2020 11:51:55 +0100 Subject: [PATCH 065/200] Schema update (microsoft/vscode-remote-release#1045) --- .../schemas/devContainer.schema.json | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/extensions/configuration-editing/schemas/devContainer.schema.json b/extensions/configuration-editing/schemas/devContainer.schema.json index bfa1799e8ad..3cff1bd58f8 100644 --- a/extensions/configuration-editing/schemas/devContainer.schema.json +++ b/extensions/configuration-editing/schemas/devContainer.schema.json @@ -48,6 +48,16 @@ "type": "string", "description": "The user VS Code Server will be started with. The default is the same user as the container." }, + "initializeCommand": { + "type": [ + "string", + "array" + ], + "description": "A command to run locally before anything else. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.", + "items": { + "type": "string" + } + }, "postCreateCommand": { "type": [ "string", From b78d1853054bbb7c212380f4ff20782125404ef0 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Sun, 23 Feb 2020 12:59:57 +0100 Subject: [PATCH 066/200] add store property to product configuration --- src/vs/platform/product/common/productService.ts | 2 ++ src/vs/platform/userDataSync/common/userDataSync.ts | 5 +++-- .../platform/userDataSync/common/userDataSyncStoreService.ts | 4 +++- .../userDataSync/browser/userDataSync.contribution.ts | 4 +++- .../workbench/contrib/userDataSync/browser/userDataSync.ts | 2 +- 5 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/vs/platform/product/common/productService.ts b/src/vs/platform/product/common/productService.ts index 23026219040..798f9f42518 100644 --- a/src/vs/platform/product/common/productService.ts +++ b/src/vs/platform/product/common/productService.ts @@ -103,6 +103,8 @@ export interface IProductConfiguration { readonly msftInternalDomains?: string[]; readonly linkProtectionTrustedDomains?: readonly string[]; + + readonly 'configurationSync.store'?: { url: string, authenticationProviderId: string }; } export interface IExeBasedExtensionTip { diff --git a/src/vs/platform/userDataSync/common/userDataSync.ts b/src/vs/platform/userDataSync/common/userDataSync.ts index aed5a8bc650..5e511488ee4 100644 --- a/src/vs/platform/userDataSync/common/userDataSync.ts +++ b/src/vs/platform/userDataSync/common/userDataSync.ts @@ -20,6 +20,7 @@ import { FormattingOptions } from 'vs/base/common/jsonFormatter'; import { URI } from 'vs/base/common/uri'; import { isEqual, joinPath } from 'vs/base/common/resources'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IProductService } from 'vs/platform/product/common/productService'; export const CONFIGURATION_SYNC_STORE_KEY = 'configurationSync.store'; @@ -143,8 +144,8 @@ export interface IUserDataSyncStore { authenticationProviderId: string; } -export function getUserDataSyncStore(configurationService: IConfigurationService): IUserDataSyncStore | undefined { - const value = configurationService.getValue<{ url: string, authenticationProviderId: string }>(CONFIGURATION_SYNC_STORE_KEY); +export function getUserDataSyncStore(productService: IProductService, configurationService: IConfigurationService): IUserDataSyncStore | undefined { + const value = productService[CONFIGURATION_SYNC_STORE_KEY] || configurationService.getValue<{ url: string, authenticationProviderId: string }>(CONFIGURATION_SYNC_STORE_KEY); if (value && value.url && value.authenticationProviderId) { return { url: joinPath(URI.parse(value.url), 'v1'), diff --git a/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts index bc662099f33..2280e1acc28 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts @@ -11,6 +11,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { IHeaders, IRequestOptions, IRequestContext } from 'vs/base/parts/request/common/request'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IAuthenticationTokenService } from 'vs/platform/authentication/common/authentication'; +import { IProductService } from 'vs/platform/product/common/productService'; export class UserDataSyncStoreService extends Disposable implements IUserDataSyncStoreService { @@ -19,13 +20,14 @@ export class UserDataSyncStoreService extends Disposable implements IUserDataSyn readonly userDataSyncStore: IUserDataSyncStore | undefined; constructor( + @IProductService productService: IProductService, @IConfigurationService configurationService: IConfigurationService, @IRequestService private readonly requestService: IRequestService, @IAuthenticationTokenService private readonly authTokenService: IAuthenticationTokenService, @IUserDataSyncLogService private readonly logService: IUserDataSyncLogService, ) { super(); - this.userDataSyncStore = getUserDataSyncStore(configurationService); + this.userDataSyncStore = getUserDataSyncStore(productService, configurationService); } async read(key: string, oldValue: IUserData | null, source?: SyncSource): Promise { diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.contribution.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.contribution.ts index 36c2f28c7a1..dbe351f2e2e 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.contribution.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.contribution.ts @@ -9,14 +9,16 @@ import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { UserDataSyncWorkbenchContribution } from 'vs/workbench/contrib/userDataSync/browser/userDataSync'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { IUserDataSyncEnablementService, getUserDataSyncStore } from 'vs/platform/userDataSync/common/userDataSync'; +import { IProductService } from 'vs/platform/product/common/productService'; class UserDataSyncSettingsMigrationContribution implements IWorkbenchContribution { constructor( + @IProductService productService: IProductService, @IConfigurationService private readonly configurationService: IConfigurationService, @IUserDataSyncEnablementService userDataSyncEnablementService: IUserDataSyncEnablementService, ) { - if (getUserDataSyncStore(configurationService)) { + if (getUserDataSyncStore(productService, configurationService)) { if (!configurationService.getValue('sync.enableSettings')) { userDataSyncEnablementService.setResourceEnablement('settings', false); } diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index f35a821a002..0fcadd380a4 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -137,7 +137,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo @IProductService private readonly productService: IProductService, ) { super(); - this.userDataSyncStore = getUserDataSyncStore(configurationService); + this.userDataSyncStore = getUserDataSyncStore(productService, configurationService); this.syncEnablementContext = CONTEXT_SYNC_ENABLEMENT.bindTo(contextKeyService); this.syncStatusContext = CONTEXT_SYNC_STATE.bindTo(contextKeyService); this.authenticationState = CONTEXT_AUTH_TOKEN_STATE.bindTo(contextKeyService); From a8fec27182502d5174ebc5b62f6854bccaa264f1 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Sun, 23 Feb 2020 13:05:54 +0100 Subject: [PATCH 067/200] Just call Turn on Sync --- src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index 0fcadd380a4..2910ca794e8 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -396,7 +396,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo severity: Severity.Info, message: localize('turned off', "Sync was turned off from another device."), actions: { - primary: [new Action('turn on sync', localize('Turn sync back on', "Turn Sync Back On"), undefined, true, () => this.turnOn())] + primary: [new Action('turn on sync', localize('turn on sync', "Turn on Sync"), undefined, true, () => this.turnOn())] } }); return; From 17a59977bc73305b1eb94de6964a7e7564d8b68b Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Sun, 23 Feb 2020 13:10:30 +0100 Subject: [PATCH 068/200] make sync is on command simple --- .../userDataSync/browser/userDataSync.ts | 30 ++++++++----------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index 2910ca794e8..396364e6074 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -235,7 +235,6 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo } this.updateBadge(); - this.registerSyncStatusAction(); } private async onDidChangeSessions(providerId: string): Promise { @@ -746,7 +745,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo private registerSignInAction(): void { const that = this; - registerAction2(class StopSyncAction extends Action2 { + this._register(registerAction2(class StopSyncAction extends Action2 { constructor() { super({ id: signInCommand.id, @@ -766,7 +765,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo that.notificationService.error(e); } } - }); + })); } private registerShowSettingsConflictsAction(): void { @@ -824,17 +823,14 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo } - private readonly _syncStatusActionDisposable = this._register(new MutableDisposable()); private registerSyncStatusAction(): void { const that = this; const when = ContextKeyExpr.and(CONTEXT_SYNC_ENABLEMENT, CONTEXT_AUTH_TOKEN_STATE.isEqualTo(AuthStatus.SignedIn), CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized)); - this._syncStatusActionDisposable.value = registerAction2(class SyncStatusAction extends Action2 { + this._register(registerAction2(class SyncStatusAction extends Action2 { constructor() { super({ id: 'workbench.userData.actions.syncStatus', - get title() { - return getIdentityTitle(localize('sync is on', "Sync is on"), that.activeAccount); - }, + title: localize('sync is on', "Sync is on"), menu: [ { id: MenuId.GlobalActivity, @@ -890,12 +886,12 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo quickPick.show(); }); } - }); + })); } private registerTurnOffSyncAction(): void { const that = this; - registerAction2(class StopSyncAction extends Action2 { + this._register(registerAction2(class StopSyncAction extends Action2 { constructor() { super({ id: stopSyncCommand.id, @@ -915,12 +911,12 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo } } } - }); + })); } private registerConfigureSyncAction(): void { const that = this; - registerAction2(class ShowSyncActivityAction extends Action2 { + this._register(registerAction2(class ShowSyncActivityAction extends Action2 { constructor() { super({ id: configureSyncCommand.id, @@ -932,12 +928,12 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo }); } run(): any { return that.configureSyncOptions(); } - }); + })); } private registerShowActivityAction(): void { const that = this; - registerAction2(class ShowSyncActivityAction extends Action2 { + this._register(registerAction2(class ShowSyncActivityAction extends Action2 { constructor() { super({ id: showSyncActivityCommand.id, @@ -949,11 +945,11 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo }); } run(): any { return that.showSyncActivity(); } - }); + })); } private registerShowSettingsAction(): void { - registerAction2(class ShowSyncSettingsAction extends Action2 { + this._register(registerAction2(class ShowSyncSettingsAction extends Action2 { constructor() { super({ id: showSyncSettingsCommand.id, @@ -967,7 +963,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo run(accessor: ServicesAccessor): any { accessor.get(IPreferencesService).openGlobalSettings(false, { query: '@tag:sync' }); } - }); + })); } } From efe562c3df0bd8b68b5b887c56f3ed6f2802ba0c Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Sun, 23 Feb 2020 13:44:03 +0100 Subject: [PATCH 069/200] [css] update dependencies --- extensions/css-language-features/package.json | 2 +- .../css-language-features/server/package.json | 6 +-- .../css-language-features/server/yarn.lock | 44 +++++++++---------- extensions/css-language-features/yarn.lock | 18 ++++---- 4 files changed, 35 insertions(+), 35 deletions(-) diff --git a/extensions/css-language-features/package.json b/extensions/css-language-features/package.json index d10cbc6e10c..4d3cffac258 100644 --- a/extensions/css-language-features/package.json +++ b/extensions/css-language-features/package.json @@ -806,7 +806,7 @@ ] }, "dependencies": { - "vscode-languageclient": "^6.1.0", + "vscode-languageclient": "^6.1.1", "vscode-nls": "^4.1.1" }, "devDependencies": { diff --git a/extensions/css-language-features/server/package.json b/extensions/css-language-features/server/package.json index b13b8f650e1..7f439dd686a 100644 --- a/extensions/css-language-features/server/package.json +++ b/extensions/css-language-features/server/package.json @@ -9,11 +9,11 @@ }, "main": "./out/cssServerMain", "dependencies": { - "vscode-css-languageservice": "^4.0.3-next.27", - "vscode-languageserver": "^6.1.0" + "vscode-css-languageservice": "^4.1.0", + "vscode-languageserver": "^6.1.1" }, "devDependencies": { - "@types/mocha": "2.2.33", + "@types/mocha": "7.0.1", "@types/node": "^12.11.7", "glob": "^7.1.6", "mocha": "^7.0.1", diff --git a/extensions/css-language-features/server/yarn.lock b/extensions/css-language-features/server/yarn.lock index cb93fa97899..e57ff40f62e 100644 --- a/extensions/css-language-features/server/yarn.lock +++ b/extensions/css-language-features/server/yarn.lock @@ -2,10 +2,10 @@ # yarn lockfile v1 -"@types/mocha@2.2.33": - version "2.2.33" - resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-2.2.33.tgz#d79a0061ec270379f4d9e225f4096fb436669def" - integrity sha1-15oAYewnA3n02eIl9AlvtDZmne8= +"@types/mocha@7.0.1": + version "7.0.1" + resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-7.0.1.tgz#5d7ec2a789a1f77c59b7ad071b9d50bf1abbfc9e" + integrity sha512-L/Nw/2e5KUaprNJoRA33oly+M8X8n0K+FwLTbYqwTcR14wdPWeRkigBLfSFpN/Asf9ENZTMZwLxjtjeYucAA4Q== "@types/node@^12.11.7": version "12.11.7" @@ -689,12 +689,12 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -vscode-css-languageservice@^4.0.3-next.27: - version "4.0.3-next.27" - resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-4.0.3-next.27.tgz#04f52bfdd85ac7eed8d82a0133aaabb4c0e18f7f" - integrity sha512-MU8sHQABb1WnzOwgazIdO4lXG7cGzFKd7VKi5j67uWTNsqSrbAVSoKGjSyOLq/o6h1L5DGG1Og/7q403z6D04g== +vscode-css-languageservice@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-4.1.0.tgz#144c8274e0bf1719fa6f773ca684bd1c7ffd634f" + integrity sha512-iTX3dTp0Y0RFWhIux5jasI8r9swdiWVB1Z3OrZ10iDHxzkETjVPxAQ5BEQU4ag0Awc8TTg1C7sJriHQY2LO14g== dependencies: - vscode-languageserver-textdocument "^1.0.1-next.1" + vscode-languageserver-textdocument "^1.0.1" vscode-languageserver-types "^3.15.1" vscode-nls "^4.1.1" vscode-uri "^2.1.1" @@ -704,30 +704,30 @@ vscode-jsonrpc@^5.0.1: resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-5.0.1.tgz#9bab9c330d89f43fc8c1e8702b5c36e058a01794" integrity sha512-JvONPptw3GAQGXlVV2utDcHx0BiY34FupW/kI6mZ5x06ER5DdPG/tXWMVHjTNULF5uKPOUUD0SaXg5QaubJL0A== -vscode-languageserver-protocol@^3.15.2: - version "3.15.2" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.2.tgz#e52c62923140b2655ad2472f6f29cfb83bacf5b8" - integrity sha512-GdL05JKOgZ76RDg3suiGCl9enESM7iQgGw4x93ibTh4sldvZmakHmTeZ4iUApPPGKf6O3OVBtrsksBXnHYaxNg== +vscode-languageserver-protocol@^3.15.3: + version "3.15.3" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.3.tgz#3fa9a0702d742cf7883cb6182a6212fcd0a1d8bb" + integrity sha512-zrMuwHOAQRhjDSnflWdJG+O2ztMWss8GqUUB8dXLR/FPenwkiBNkMIJJYfSN6sgskvsF0rHAoBowNQfbyZnnvw== dependencies: vscode-jsonrpc "^5.0.1" vscode-languageserver-types "3.15.1" -vscode-languageserver-textdocument@^1.0.1-next.1: - version "1.0.1-next.1" - resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.1-next.1.tgz#c8f2f792c7c88d33ea8441ca04bfb8376896b671" - integrity sha512-Cmt0KsNxouns+d7/Kw/jWtWU9Z3h56z1qAA8utjDOEqrDcrTs2rDXv3EJRa99nuKM3wVf6DbWym1VqL9q71XPA== +vscode-languageserver-textdocument@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.1.tgz#178168e87efad6171b372add1dea34f53e5d330f" + integrity sha512-UIcJDjX7IFkck7cSkNNyzIz5FyvpQfY7sdzVy+wkKN/BLaD4DQ0ppXQrKePomCxTS7RrolK1I0pey0bG9eh8dA== vscode-languageserver-types@3.15.1, vscode-languageserver-types@^3.15.1: version "3.15.1" resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.15.1.tgz#17be71d78d2f6236d414f0001ce1ef4d23e6b6de" integrity sha512-+a9MPUQrNGRrGU630OGbYVQ+11iOIovjCkqxajPa9w57Sd5ruK8WQNsslzpa0x/QJqC8kRc2DUxWjIFwoNm4ZQ== -vscode-languageserver@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-6.1.0.tgz#f0ff149b06d1961f49ed03385ecc2a96bcaddcde" - integrity sha512-Q5kUJegYclTZMnKUaEcxJK41Ozp6qJhhoFJYj0w8y8j9JXdKT479LE945QCKRvSgWfsqTSUmgsozVTUIwQQxHw== +vscode-languageserver@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-6.1.1.tgz#d76afc68172c27d4327ee74332b468fbc740d762" + integrity sha512-DueEpkUAkD5XTR4MLYNr6bQIp/UFR0/IPApgXU3YfCBCB08u2sm9hRCs6DxYZELkk++STPjpcjksR2H8qI3cDQ== dependencies: - vscode-languageserver-protocol "^3.15.2" + vscode-languageserver-protocol "^3.15.3" vscode-nls@^4.1.1: version "4.1.1" diff --git a/extensions/css-language-features/yarn.lock b/extensions/css-language-features/yarn.lock index b90ab508aaa..4494f0038a2 100644 --- a/extensions/css-language-features/yarn.lock +++ b/extensions/css-language-features/yarn.lock @@ -640,18 +640,18 @@ vscode-jsonrpc@^5.0.1: resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-5.0.1.tgz#9bab9c330d89f43fc8c1e8702b5c36e058a01794" integrity sha512-JvONPptw3GAQGXlVV2utDcHx0BiY34FupW/kI6mZ5x06ER5DdPG/tXWMVHjTNULF5uKPOUUD0SaXg5QaubJL0A== -vscode-languageclient@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-6.1.0.tgz#ee67c0b7818c42ce0281572d05c89adfcc4f5a38" - integrity sha512-Tcp0VoOaa0YzxL4nEfK9tsmcy76Eo8jNLvFQZwh2c8oMm02luL8uGYPLQNAiZ3XGgegfcwiQFZMqbW7DNV0vxA== +vscode-languageclient@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-6.1.1.tgz#91b62e416c5abbf2013ae3726f314a19c22a8457" + integrity sha512-mB6d8Tg+82l8EFUfR+SBu0+lCshyKVgC5E5+MQ0/BJa+9AgeBjtG5npoGaCo4/VvWzK0ZRGm85zU5iRp1RYPIA== dependencies: semver "^6.3.0" - vscode-languageserver-protocol "^3.15.2" + vscode-languageserver-protocol "^3.15.3" -vscode-languageserver-protocol@^3.15.2: - version "3.15.2" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.2.tgz#e52c62923140b2655ad2472f6f29cfb83bacf5b8" - integrity sha512-GdL05JKOgZ76RDg3suiGCl9enESM7iQgGw4x93ibTh4sldvZmakHmTeZ4iUApPPGKf6O3OVBtrsksBXnHYaxNg== +vscode-languageserver-protocol@^3.15.3: + version "3.15.3" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.3.tgz#3fa9a0702d742cf7883cb6182a6212fcd0a1d8bb" + integrity sha512-zrMuwHOAQRhjDSnflWdJG+O2ztMWss8GqUUB8dXLR/FPenwkiBNkMIJJYfSN6sgskvsF0rHAoBowNQfbyZnnvw== dependencies: vscode-jsonrpc "^5.0.1" vscode-languageserver-types "3.15.1" From 75c47b75054dad52c6902fd9aa118de13366125b Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Sun, 23 Feb 2020 13:54:54 +0100 Subject: [PATCH 070/200] [html] update dependencies --- .../html-language-features/package.json | 2 +- .../server/package.json | 8 +-- .../html-language-features/server/yarn.lock | 49 ++++++++++--------- extensions/html-language-features/yarn.lock | 18 +++---- 4 files changed, 41 insertions(+), 36 deletions(-) diff --git a/extensions/html-language-features/package.json b/extensions/html-language-features/package.json index 526f54fcacb..00289a314a1 100644 --- a/extensions/html-language-features/package.json +++ b/extensions/html-language-features/package.json @@ -201,7 +201,7 @@ }, "dependencies": { "vscode-extension-telemetry": "0.1.1", - "vscode-languageclient": "^6.1.0", + "vscode-languageclient": "^6.1.1", "vscode-nls": "^4.1.1" }, "devDependencies": { diff --git a/extensions/html-language-features/server/package.json b/extensions/html-language-features/server/package.json index 0083f2b1bd5..5064259bfbf 100644 --- a/extensions/html-language-features/server/package.json +++ b/extensions/html-language-features/server/package.json @@ -9,14 +9,14 @@ }, "main": "./out/htmlServerMain", "dependencies": { - "vscode-css-languageservice": "^4.0.3-next.27", - "vscode-html-languageservice": "^3.0.4-next.14", - "vscode-languageserver": "^6.1.0", + "vscode-css-languageservice": "^4.1.0", + "vscode-html-languageservice": "3.0.4-next.15", + "vscode-languageserver": "^6.1.1", "vscode-nls": "^4.1.1", "vscode-uri": "^2.1.1" }, "devDependencies": { - "@types/mocha": "2.2.33", + "@types/mocha": "7.0.1", "@types/node": "^12.11.7", "glob": "^7.1.6", "mocha": "^7.0.1", diff --git a/extensions/html-language-features/server/yarn.lock b/extensions/html-language-features/server/yarn.lock index b2659f1c92d..f4ed7ba600d 100644 --- a/extensions/html-language-features/server/yarn.lock +++ b/extensions/html-language-features/server/yarn.lock @@ -2,10 +2,10 @@ # yarn lockfile v1 -"@types/mocha@2.2.33": - version "2.2.33" - resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-2.2.33.tgz#d79a0061ec270379f4d9e225f4096fb436669def" - integrity sha1-15oAYewnA3n02eIl9AlvtDZmne8= +"@types/mocha@7.0.1": + version "7.0.1" + resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-7.0.1.tgz#5d7ec2a789a1f77c59b7ad071b9d50bf1abbfc9e" + integrity sha512-L/Nw/2e5KUaprNJoRA33oly+M8X8n0K+FwLTbYqwTcR14wdPWeRkigBLfSFpN/Asf9ENZTMZwLxjtjeYucAA4Q== "@types/node@^12.11.7": version "12.11.7" @@ -714,20 +714,20 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -vscode-css-languageservice@^4.0.3-next.27: - version "4.0.3-next.27" - resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-4.0.3-next.27.tgz#04f52bfdd85ac7eed8d82a0133aaabb4c0e18f7f" - integrity sha512-MU8sHQABb1WnzOwgazIdO4lXG7cGzFKd7VKi5j67uWTNsqSrbAVSoKGjSyOLq/o6h1L5DGG1Og/7q403z6D04g== +vscode-css-languageservice@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-4.1.0.tgz#144c8274e0bf1719fa6f773ca684bd1c7ffd634f" + integrity sha512-iTX3dTp0Y0RFWhIux5jasI8r9swdiWVB1Z3OrZ10iDHxzkETjVPxAQ5BEQU4ag0Awc8TTg1C7sJriHQY2LO14g== dependencies: - vscode-languageserver-textdocument "^1.0.1-next.1" + vscode-languageserver-textdocument "^1.0.1" vscode-languageserver-types "^3.15.1" vscode-nls "^4.1.1" vscode-uri "^2.1.1" -vscode-html-languageservice@^3.0.4-next.14: - version "3.0.4-next.14" - resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-3.0.4-next.14.tgz#143cda8d1b032da3506de2ee7c352f50159edc7f" - integrity sha512-YQ1nYy3Yqai+mrvORFeJW1/CvkBd9y2kYDOvfTEmt5uCd7+7iMTQLdhPFWwlx/GKDXuvQJoJ0E3eNd/gGb3cvQ== +vscode-html-languageservice@3.0.4-next.15: + version "3.0.4-next.15" + resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-3.0.4-next.15.tgz#7214ccd9b4a06cf138b5945d9fd88285a0add490" + integrity sha512-UmUm3A1ZTj+BloVIyel+5pK/nfsqRfPLXzl8BA9O7v5Cj64vivddABvNf/rW1US8fzdikFNZNloC/4ooqxB2kw== dependencies: vscode-languageserver-textdocument "^1.0.1-next.1" vscode-languageserver-types "^3.15.1" @@ -739,14 +739,19 @@ vscode-jsonrpc@^5.0.1: resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-5.0.1.tgz#9bab9c330d89f43fc8c1e8702b5c36e058a01794" integrity sha512-JvONPptw3GAQGXlVV2utDcHx0BiY34FupW/kI6mZ5x06ER5DdPG/tXWMVHjTNULF5uKPOUUD0SaXg5QaubJL0A== -vscode-languageserver-protocol@^3.15.2: - version "3.15.2" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.2.tgz#e52c62923140b2655ad2472f6f29cfb83bacf5b8" - integrity sha512-GdL05JKOgZ76RDg3suiGCl9enESM7iQgGw4x93ibTh4sldvZmakHmTeZ4iUApPPGKf6O3OVBtrsksBXnHYaxNg== +vscode-languageserver-protocol@^3.15.3: + version "3.15.3" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.3.tgz#3fa9a0702d742cf7883cb6182a6212fcd0a1d8bb" + integrity sha512-zrMuwHOAQRhjDSnflWdJG+O2ztMWss8GqUUB8dXLR/FPenwkiBNkMIJJYfSN6sgskvsF0rHAoBowNQfbyZnnvw== dependencies: vscode-jsonrpc "^5.0.1" vscode-languageserver-types "3.15.1" +vscode-languageserver-textdocument@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.1.tgz#178168e87efad6171b372add1dea34f53e5d330f" + integrity sha512-UIcJDjX7IFkck7cSkNNyzIz5FyvpQfY7sdzVy+wkKN/BLaD4DQ0ppXQrKePomCxTS7RrolK1I0pey0bG9eh8dA== + vscode-languageserver-textdocument@^1.0.1-next.1: version "1.0.1-next.1" resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.1-next.1.tgz#c8f2f792c7c88d33ea8441ca04bfb8376896b671" @@ -757,12 +762,12 @@ vscode-languageserver-types@3.15.1, vscode-languageserver-types@^3.15.1: resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.15.1.tgz#17be71d78d2f6236d414f0001ce1ef4d23e6b6de" integrity sha512-+a9MPUQrNGRrGU630OGbYVQ+11iOIovjCkqxajPa9w57Sd5ruK8WQNsslzpa0x/QJqC8kRc2DUxWjIFwoNm4ZQ== -vscode-languageserver@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-6.1.0.tgz#f0ff149b06d1961f49ed03385ecc2a96bcaddcde" - integrity sha512-Q5kUJegYclTZMnKUaEcxJK41Ozp6qJhhoFJYj0w8y8j9JXdKT479LE945QCKRvSgWfsqTSUmgsozVTUIwQQxHw== +vscode-languageserver@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-6.1.1.tgz#d76afc68172c27d4327ee74332b468fbc740d762" + integrity sha512-DueEpkUAkD5XTR4MLYNr6bQIp/UFR0/IPApgXU3YfCBCB08u2sm9hRCs6DxYZELkk++STPjpcjksR2H8qI3cDQ== dependencies: - vscode-languageserver-protocol "^3.15.2" + vscode-languageserver-protocol "^3.15.3" vscode-nls@^4.1.1: version "4.1.1" diff --git a/extensions/html-language-features/yarn.lock b/extensions/html-language-features/yarn.lock index e91ad63907d..8dfd3e7e9d0 100644 --- a/extensions/html-language-features/yarn.lock +++ b/extensions/html-language-features/yarn.lock @@ -50,18 +50,18 @@ vscode-jsonrpc@^5.0.1: resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-5.0.1.tgz#9bab9c330d89f43fc8c1e8702b5c36e058a01794" integrity sha512-JvONPptw3GAQGXlVV2utDcHx0BiY34FupW/kI6mZ5x06ER5DdPG/tXWMVHjTNULF5uKPOUUD0SaXg5QaubJL0A== -vscode-languageclient@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-6.1.0.tgz#ee67c0b7818c42ce0281572d05c89adfcc4f5a38" - integrity sha512-Tcp0VoOaa0YzxL4nEfK9tsmcy76Eo8jNLvFQZwh2c8oMm02luL8uGYPLQNAiZ3XGgegfcwiQFZMqbW7DNV0vxA== +vscode-languageclient@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-6.1.1.tgz#91b62e416c5abbf2013ae3726f314a19c22a8457" + integrity sha512-mB6d8Tg+82l8EFUfR+SBu0+lCshyKVgC5E5+MQ0/BJa+9AgeBjtG5npoGaCo4/VvWzK0ZRGm85zU5iRp1RYPIA== dependencies: semver "^6.3.0" - vscode-languageserver-protocol "^3.15.2" + vscode-languageserver-protocol "^3.15.3" -vscode-languageserver-protocol@^3.15.2: - version "3.15.2" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.2.tgz#e52c62923140b2655ad2472f6f29cfb83bacf5b8" - integrity sha512-GdL05JKOgZ76RDg3suiGCl9enESM7iQgGw4x93ibTh4sldvZmakHmTeZ4iUApPPGKf6O3OVBtrsksBXnHYaxNg== +vscode-languageserver-protocol@^3.15.3: + version "3.15.3" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.3.tgz#3fa9a0702d742cf7883cb6182a6212fcd0a1d8bb" + integrity sha512-zrMuwHOAQRhjDSnflWdJG+O2ztMWss8GqUUB8dXLR/FPenwkiBNkMIJJYfSN6sgskvsF0rHAoBowNQfbyZnnvw== dependencies: vscode-jsonrpc "^5.0.1" vscode-languageserver-types "3.15.1" From 746afb03c953c123c547265593b47518f0cf85c4 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Sun, 23 Feb 2020 14:58:17 +0100 Subject: [PATCH 071/200] turn on sync preview confirmation --- .../userDataSync/browser/userDataSync.ts | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index 396364e6074..2a23e7d3e98 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -47,6 +47,8 @@ import { IPreferencesService } from 'vs/workbench/services/preferences/common/pr import { IAuthenticationTokenService } from 'vs/platform/authentication/common/authentication'; import { fromNow } from 'vs/base/common/date'; import { IProductService } from 'vs/platform/product/common/productService'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; const enum AuthStatus { Initializing = 'Initializing', @@ -135,6 +137,8 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo @ITelemetryService private readonly telemetryService: ITelemetryService, @IFileService private readonly fileService: IFileService, @IProductService private readonly productService: IProductService, + @IStorageService private readonly storageService: IStorageService, + @IOpenerService private readonly openerService: IOpenerService, ) { super(); this.userDataSyncStore = getUserDataSyncStore(productService, configurationService); @@ -483,6 +487,24 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo } private async turnOn(): Promise { + if (!this.storageService.getBoolean('sync.donotAskPreviewConfirmation', StorageScope.GLOBAL, false)) { + const result = await this.dialogService.show( + Severity.Info, + localize('sync preview message', "Synchronising your preferences is a preview feature and please read the documentation before turnin on."), + [ + localize('open doc', "Open Documentation"), + localize('confirm', "Continue"), + localize('cancel', "Cancel"), + ], + { + cancelId: 2 + } + ); + switch (result.choice) { + case 0: this.openerService.open(URI.parse('https://go.microsoft.com/fwlink/?LinkId=827846')); return; + case 2: return; + } + } return new Promise((c, e) => { const disposables: DisposableStore = new DisposableStore(); const quickPick = this.quickInputService.createQuickPick(); @@ -522,6 +544,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo await this.handleFirstTimeSync(); this.userDataSyncEnablementService.setEnablement(true); this.notificationService.info(localize('sync turned on', "Sync will happen automatically from now on.")); + this.storageService.store('sync.donotAskPreviewConfirmation', true, StorageScope.GLOBAL); } private getConfigureSyncQuickPickItems(): ConfigureSyncQuickPickItem[] { From 6f79071a2fe50a83545a10fc88b695d1e49a9676 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Sun, 23 Feb 2020 15:10:35 +0100 Subject: [PATCH 072/200] Fix #91193 --- src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index 2a23e7d3e98..c0edf981803 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -516,7 +516,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo quickPick.customLabel = localize('turn on', "Turn on"); } else { const displayName = this.authenticationService.getDisplayName(this.userDataSyncStore!.authenticationProviderId); - quickPick.description = localize('sign in and turn on sync detail', "Please sign in with your {0} account to synchronize your following data across all your devices.", displayName); + quickPick.description = localize('sign in and turn on sync detail', "Sign in with your {0} account to synchronize your data across devices.", displayName); quickPick.customLabel = localize('sign in and turn on sync', "Sign in & Turn on"); } quickPick.placeholder = localize('configure sync placeholder', "Choose what to sync"); From 646aaf67aa8912de96b836efa20dadc65d5fde60 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Sun, 23 Feb 2020 16:54:53 +0100 Subject: [PATCH 073/200] Cleanup open-url handling (#91234) * cleanup open-url handling * open url - allow to open a remote workpsace without opening a window first * help build * undo build change * support files too --- src/main.js | 6 +- src/vs/code/electron-main/app.ts | 179 +++++++++++------- src/vs/code/node/paths.ts | 2 +- .../url/electron-main/electronUrlListener.ts | 33 ++-- 4 files changed, 134 insertions(+), 86 deletions(-) diff --git a/src/main.js b/src/main.js index f8aa90f9c8c..bc268c8cd12 100644 --- a/src/main.js +++ b/src/main.js @@ -136,7 +136,7 @@ function configureCommandlineSwitchesSync(cliArgs) { // override for the color profile to use 'force-color-profile' ]; - + if (process.platform === 'linux') { SUPPORTED_ELECTRON_SWITCHES.push('force-renderer-accessibility'); } @@ -344,7 +344,7 @@ function setCurrentWorkingDirectory() { function registerListeners() { /** - * Mac: when someone drops a file to the not-yet running VSCode, the open-file event fires even before + * macOS: when someone drops a file to the not-yet running VSCode, the open-file event fires even before * the app-ready event. We listen very early for open-file and remember this upon startup as path to open. * * @type {string[]} @@ -356,7 +356,7 @@ function registerListeners() { }); /** - * React to open-url requests. + * macOS: react to open-url requests. * * @type {string[]} */ diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index f6966b2772c..4ba8c87cb98 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -24,7 +24,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { IStateService } from 'vs/platform/state/node/state'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IURLService, IOpenURLOptions } from 'vs/platform/url/common/url'; +import { IURLService } from 'vs/platform/url/common/url'; import { URLHandlerChannelClient, URLHandlerRouter } from 'vs/platform/url/common/urlIpc'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService, combinedAppender, LogAppender } from 'vs/platform/telemetry/common/telemetryUtils'; @@ -73,10 +73,10 @@ import { IDiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsSer import { ExtensionHostDebugBroadcastChannel } from 'vs/platform/debug/common/extensionHostDebugIpc'; import { IElectronMainService, ElectronMainService } from 'vs/platform/electron/electron-main/electronMainService'; import { ISharedProcessMainService, SharedProcessMainService } from 'vs/platform/ipc/electron-main/sharedProcessMainService'; -import { assign } from 'vs/base/common/objects'; import { IDialogMainService, DialogMainService } from 'vs/platform/dialogs/electron-main/dialogs'; import { withNullAsUndefined } from 'vs/base/common/types'; import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv'; +import { coalesce } from 'vs/base/common/arrays'; export class CodeApplication extends Disposable { @@ -395,7 +395,7 @@ export class CodeApplication extends Disposable { const windows = appInstantiationService.invokeFunction(accessor => this.openFirstWindow(accessor, electronIpcServer, sharedProcessClient)); // Post Open Windows Tasks - appInstantiationService.invokeFunction(this.afterWindowOpen.bind(this)); + appInstantiationService.invokeFunction(accessor => this.afterWindowOpen(accessor)); // Tracing: Stop tracing after windows are ready if enabled if (this.environmentService.args.trace) { @@ -575,9 +575,8 @@ export class CodeApplication extends Disposable { electronIpcServer.registerChannel('logger', loggerChannel); sharedProcessClient.then(client => client.registerChannel('logger', loggerChannel)); - const windowsMainService = this.windowsMainService = accessor.get(IWindowsMainService); - // ExtensionHost Debug broadcast service + const windowsMainService = this.windowsMainService = accessor.get(IWindowsMainService); electronIpcServer.registerChannel(ExtensionHostDebugBroadcastChannel.ChannelName, new ElectronExtensionHostDebugBroadcastChannel(windowsMainService)); // Signal phase: ready (services set) @@ -586,47 +585,67 @@ export class CodeApplication extends Disposable { // Propagate to clients this.dialogMainService = accessor.get(IDialogMainService); - // Create a URL handler to open file URIs in the active window + // Check for initial URLs to handle from protocol link invocations const environmentService = accessor.get(IEnvironmentService); + const pendingWindowOpenablesFromProtocolLinks: IWindowOpenable[] = []; + const pendingProtocolLinksToHandle = coalesce([ + + // Windows/Linux: protocol handler invokes CLI with --open-url + ...environmentService.args['open-url'] ? environmentService.args._urls || [] : [], + + // macOS: open-url events + ...((global).getOpenUrls() || []) as string[] + ].map(pendingUrlToHandle => { + try { + return URI.parse(pendingUrlToHandle); + } catch (error) { + return undefined; + } + })).filter(pendingUriToHandle => { + // filter out any protocol link that wants to open as window so that + // we open the right set of windows on startup and not restore the + // previous workspace too. + const windowOpenable = this.getWindowOpenableFromProtocolLink(pendingUriToHandle); + if (windowOpenable) { + pendingWindowOpenablesFromProtocolLinks.push(windowOpenable); + + return false; + } + + return true; + }); + + // Create a URL handler to open file URIs in the active window + const app = this; urlService.registerHandler({ - async handleURL(uri: URI, options?: IOpenURLOptions): Promise { + async handleURL(uri: URI): Promise { - // Catch file/remote URLs - if ((uri.authority === Schemas.file || uri.authority === Schemas.vscodeRemote) && !!uri.path) { - const cli = assign(Object.create(null), environmentService.args); - const urisToOpen: IWindowOpenable[] = []; + // Check for URIs to open in window + const windowOpenableFromProtocolLink = app.getWindowOpenableFromProtocolLink(uri); + if (windowOpenableFromProtocolLink) { + windowsMainService.open({ + context: OpenContext.API, + cli: { ...environmentService.args }, + urisToOpen: [windowOpenableFromProtocolLink], + gotoLineMode: true + }); - // File path - if (uri.authority === Schemas.file) { - // we configure as fileUri, but later validation will - // make sure to open as folder or workspace if possible - urisToOpen.push({ fileUri: URI.file(uri.fsPath) }); - } + return true; + } - // Remote path - else { - // Example conversion: - // From: vscode://vscode-remote/wsl+ubuntu/mnt/c/GitDevelopment/monaco - // To: vscode-remote://wsl+ubuntu/mnt/c/GitDevelopment/monaco - const secondSlash = uri.path.indexOf(posix.sep, 1 /* skip over the leading slash */); - if (secondSlash !== -1) { - const authority = uri.path.substring(1, secondSlash); - const path = uri.path.substring(secondSlash); - const remoteUri = URI.from({ scheme: Schemas.vscodeRemote, authority, path, query: uri.query, fragment: uri.fragment }); + // If we have not yet handled the URI and we have no window opened (macOS only) + // we first open a window and then try to open that URI within that window + if (isMacintosh && windowsMainService.getWindowCount() === 0) { + const [window] = windowsMainService.open({ + context: OpenContext.API, + cli: { ...environmentService.args }, + forceEmpty: true, + gotoLineMode: true + }); - if (hasWorkspaceFileExtension(path)) { - urisToOpen.push({ workspaceUri: remoteUri }); - } else { - urisToOpen.push({ folderUri: remoteUri }); - } - } - } + await window.ready(); - if (urisToOpen.length > 0) { - windowsMainService.open({ context: OpenContext.API, cli, urisToOpen, gotoLineMode: true }); - - return true; - } + return urlService.open(uri); } return false; @@ -638,37 +657,13 @@ export class CodeApplication extends Disposable { const activeWindowRouter = new StaticRouter(ctx => activeWindowManager.getActiveClientId().then(id => ctx === id)); const urlHandlerRouter = new URLHandlerRouter(activeWindowRouter); const urlHandlerChannel = electronIpcServer.getChannel('urlHandler', urlHandlerRouter); - const multiplexURLHandler = new URLHandlerChannelClient(urlHandlerChannel); - - // On Mac, Code can be running without any open windows, so we must create a window to handle urls, - // if there is none - if (isMacintosh) { - urlService.registerHandler({ - async handleURL(uri: URI, options?: IOpenURLOptions): Promise { - if (windowsMainService.getWindowCount() === 0) { - const cli = { ...environmentService.args }; - const [window] = windowsMainService.open({ context: OpenContext.API, cli, forceEmpty: true, gotoLineMode: true }); - - await window.ready(); - - return urlService.open(uri); - } - - return false; - } - }); - } - - // Register the multiple URL handler - urlService.registerHandler(multiplexURLHandler); + urlService.registerHandler(new URLHandlerChannelClient(urlHandlerChannel)); // Watch Electron URLs and forward them to the UrlService - const args = this.environmentService.args; - const urls = args['open-url'] ? args._urls : []; - const urlListener = new ElectronURLListener(urls || [], urlService, windowsMainService, this.environmentService); - this._register(urlListener); + this._register(new ElectronURLListener(pendingProtocolLinksToHandle, urlService, windowsMainService, this.environmentService)); // Open our first window + const args = this.environmentService.args; const macOpenFiles: string[] = (global).macOpenFiles; const context = !!process.env['VSCODE_CLI'] ? OpenContext.CLI : OpenContext.DESKTOP; const hasCliArgs = args._.length; @@ -677,6 +672,19 @@ export class CodeApplication extends Disposable { const noRecentEntry = args['skip-add-to-recently-opened'] === true; const waitMarkerFileURI = args.wait && args.waitMarkerFilePath ? URI.file(args.waitMarkerFilePath) : undefined; + // check for a pending window to open from URI + // e.g. when running code with --open-uri from + // a protocol handler + if (pendingWindowOpenablesFromProtocolLinks.length > 0) { + return windowsMainService.open({ + context, + cli: args, + urisToOpen: pendingWindowOpenablesFromProtocolLinks, + gotoLineMode: true, + initialStartup: true + }); + } + // new window if "-n" or "--remote" was used without paths if ((args['new-window'] || args.remote) && !hasCliArgs && !hasFolderURIs && !hasFileURIs) { return windowsMainService.open({ @@ -698,7 +706,6 @@ export class CodeApplication extends Disposable { urisToOpen: macOpenFiles.map(file => this.getWindowOpenableFromPathSync(file)), noRecentEntry, waitMarkerFileURI, - gotoLineMode: false, initialStartup: true }); } @@ -716,6 +723,40 @@ export class CodeApplication extends Disposable { }); } + private getWindowOpenableFromProtocolLink(uri: URI): IWindowOpenable | undefined { + if (!uri.path) { + return undefined; + } + + // File path + if (uri.authority === Schemas.file) { + // we configure as fileUri, but later validation will + // make sure to open as folder or workspace if possible + return { fileUri: URI.file(uri.fsPath) }; + } + + // Remote path + else if (uri.authority === Schemas.vscodeRemote) { + // Example conversion: + // From: vscode://vscode-remote/wsl+ubuntu/mnt/c/GitDevelopment/monaco + // To: vscode-remote://wsl+ubuntu/mnt/c/GitDevelopment/monaco + const secondSlash = uri.path.indexOf(posix.sep, 1 /* skip over the leading slash */); + if (secondSlash !== -1) { + const authority = uri.path.substring(1, secondSlash); + const path = uri.path.substring(secondSlash); + const remoteUri = URI.from({ scheme: Schemas.vscodeRemote, authority, path, query: uri.query, fragment: uri.fragment }); + + if (hasWorkspaceFileExtension(path)) { + return { workspaceUri: remoteUri }; + } else { + return { folderUri: remoteUri }; + } + } + } + + return undefined; + } + private getWindowOpenableFromPathSync(path: string): IWindowOpenable { try { const fileStat = statSync(path); @@ -734,6 +775,7 @@ export class CodeApplication extends Disposable { } private afterWindowOpen(accessor: ServicesAccessor): void { + // Signal phase: after window open this.lifecycleMainService.phase = LifecycleMainPhase.AfterWindowOpen; @@ -763,7 +805,7 @@ class ElectronExtensionHostDebugBroadcastChannel extends ExtensionHost super(); } - call(ctx: TContext, command: string, arg?: any): Promise { + async call(ctx: TContext, command: string, arg?: any): Promise { if (command === 'openExtensionDevelopmentHostWindow') { const env = arg[1]; const pargs = parseArgs(arg[0], OPTIONS); @@ -775,7 +817,6 @@ class ElectronExtensionHostDebugBroadcastChannel extends ExtensionHost userEnv: Object.keys(env).length > 0 ? env : undefined }); } - return Promise.resolve(); } else { return super.call(ctx, command, arg); } diff --git a/src/vs/code/node/paths.ts b/src/vs/code/node/paths.ts index f605a4526f3..6bdcf79ed03 100644 --- a/src/vs/code/node/paths.ts +++ b/src/vs/code/node/paths.ts @@ -19,8 +19,8 @@ export function validatePaths(args: ParsedArgs): ParsedArgs { args._ = []; } + // Normalize paths and watch out for goto line mode if (!args['remote']) { - // Normalize paths and watch out for goto line mode const paths = doValidatePaths(args._, args.goto); args._ = paths; } diff --git a/src/vs/platform/url/electron-main/electronUrlListener.ts b/src/vs/platform/url/electron-main/electronUrlListener.ts index 6c979560016..2523ccb0c5f 100644 --- a/src/vs/platform/url/electron-main/electronUrlListener.ts +++ b/src/vs/platform/url/electron-main/electronUrlListener.ts @@ -12,7 +12,6 @@ import { URI } from 'vs/base/common/uri'; import { IDisposable, DisposableStore, Disposable } from 'vs/base/common/lifecycle'; import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows'; import { isWindows } from 'vs/base/common/platform'; -import { coalesce } from 'vs/base/common/arrays'; import { disposableTimeout } from 'vs/base/common/async'; function uriFromRawUrl(url: string): URI | null { @@ -23,6 +22,16 @@ function uriFromRawUrl(url: string): URI | null { } } +/** + * A listener for URLs that are opened from the OS and handled by VSCode. + * Depending on the platform, this works differently: + * - Windows: we use `app.setAsDefaultProtocolClient()` to register VSCode with the OS + * and additionally add the `open-url` command line argument to identify. + * - macOS: we rely on `app.on('open-url')` to be called by the OS + * - Linux: we have a special shortcut installed (`resources/linux/code-url-handler.desktop`) + * that calls VSCode with the `open-url` command line argument + * (https://github.com/microsoft/vscode/pull/56727) + */ export class ElectronURLListener { private uris: URI[] = []; @@ -31,36 +40,34 @@ export class ElectronURLListener { private disposables = new DisposableStore(); constructor( - initial: string | string[], - @IURLService private readonly urlService: IURLService, - @IWindowsMainService windowsMainService: IWindowsMainService, - @IEnvironmentService environmentService: IEnvironmentService + initialUrisToHandle: URI[], + private readonly urlService: IURLService, + windowsMainService: IWindowsMainService, + environmentService: IEnvironmentService ) { - const globalBuffer = ((global).getOpenUrls() || []) as string[]; - const rawBuffer = [ - ...(typeof initial === 'string' ? [initial] : initial), - ...globalBuffer - ]; - this.uris = coalesce(rawBuffer.map(uriFromRawUrl)); + // the initial set of URIs we need to handle once the window is ready + this.uris = initialUrisToHandle; + // Windows: install as protocol handler if (isWindows) { const windowsParameters = environmentService.isBuilt ? [] : [`"${environmentService.appRoot}"`]; windowsParameters.push('--open-url', '--'); app.setAsDefaultProtocolClient(product.urlProtocol, process.execPath, windowsParameters); } + // macOS: listen to `open-url` events from here on to handle const onOpenElectronUrl = Event.map( Event.fromNodeEventEmitter(app, 'open-url', (event: ElectronEvent, url: string) => ({ event, url })), ({ event, url }) => { - // always prevent default and return the url as string - event.preventDefault(); + event.preventDefault(); // always prevent default and return the url as string return url; }); const onOpenUrl = Event.filter(Event.map(onOpenElectronUrl, uriFromRawUrl), (uri): uri is URI => !!uri); onOpenUrl(this.urlService.open, this.urlService, this.disposables); + // Send initial links to the window once it has loaded const isWindowReady = windowsMainService.getWindows() .filter(w => w.isReady) .length > 0; From bc97567cf4159e062f4fdd7bf5130f17fc49a00d Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Sun, 23 Feb 2020 17:01:22 +0100 Subject: [PATCH 074/200] untitled - fix description not showing invalid paths --- src/vs/workbench/browser/labels.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/browser/labels.ts b/src/vs/workbench/browser/labels.ts index 8f88e4acdee..eece5353caa 100644 --- a/src/vs/workbench/browser/labels.ts +++ b/src/vs/workbench/browser/labels.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from 'vs/base/common/uri'; -import { posix } from 'vs/base/common/path'; import { dirname, isEqual, basenameOrAuthority } from 'vs/base/common/resources'; import { IconLabel, IIconLabelValueOptions, IIconLabelCreationOptions } from 'vs/base/browser/ui/iconLabel/iconLabel'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; @@ -365,8 +364,8 @@ class ResourceLabelWidget extends IconLabel { let untitledDescription = untitledModel.resource.path; if (label.name !== untitledDescription) { label.description = untitledDescription; - } else if (label.description === posix.sep) { - label.description = undefined; // unset showing just "/" for untitled without associated resource + } else { + label.description = undefined; } } From 127c34484bca06c46b0345eb03f87e0ca70eaddc Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Sun, 23 Feb 2020 17:04:38 +0100 Subject: [PATCH 075/200] fix tests --- .../platform/userDataSync/test/common/userDataSyncClient.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts b/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts index a050f896b78..74a42082391 100644 --- a/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts +++ b/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts @@ -34,6 +34,8 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { SettingsSynchroniser } from 'vs/platform/userDataSync/common/settingsSync'; import { Emitter } from 'vs/base/common/event'; import { IAuthenticationTokenService } from 'vs/platform/authentication/common/authentication'; +import product from 'vs/platform/product/common/product'; +import { IProductService } from 'vs/platform/product/common/productService'; export class UserDataSyncClient extends Disposable { @@ -59,6 +61,8 @@ export class UserDataSyncClient extends Disposable { const logService = new NullLogService(); this.instantiationService.stub(ILogService, logService); + this.instantiationService.stub(IProductService, { _serviceBrand: undefined, ...product }); + const fileService = this._register(new FileService(logService)); fileService.registerProvider(Schemas.inMemory, new InMemoryFileSystemProvider()); this.instantiationService.stub(IFileService, fileService); From fa9f4138de6c4240d3ed14d2338c91f945cc4c25 Mon Sep 17 00:00:00 2001 From: PM Extra Date: Mon, 24 Feb 2020 01:38:58 +0800 Subject: [PATCH 076/200] Fix a small bug of JsonValidation The origin condition expression `(!strings.startsWith(uri, 'https:/') && strings.startsWith(uri, 'https:/'))` is always false. According to the context code, it should be `(!strings.startsWith(uri, 'http://') && !strings.startsWith(uri, 'https://'))` instead. --- src/vs/workbench/api/common/jsonValidationExtensionPoint.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/api/common/jsonValidationExtensionPoint.ts b/src/vs/workbench/api/common/jsonValidationExtensionPoint.ts index 66b60ca3743..99c5c717bcc 100644 --- a/src/vs/workbench/api/common/jsonValidationExtensionPoint.ts +++ b/src/vs/workbench/api/common/jsonValidationExtensionPoint.ts @@ -69,7 +69,7 @@ export class JSONValidationExtensionPoint { } catch (e) { collector.error(nls.localize('invalid.url.fileschema', "'configuration.jsonValidation.url' is an invalid relative URL: {0}", e.message)); } - } else if (!strings.startsWith(uri, 'https:/') && strings.startsWith(uri, 'https:/')) { + } else if (!strings.startsWith(uri, 'http://') && !strings.startsWith(uri, 'https://')) { collector.error(nls.localize('invalid.url.schema', "'configuration.jsonValidation.url' must start with 'http:', 'https:' or './' to reference schemas located in the extension")); return; } From 97fc97c2f8cf75d5b1fd617c0ead5df1377cc407 Mon Sep 17 00:00:00 2001 From: nrayburn Date: Sun, 23 Feb 2020 11:58:00 -0600 Subject: [PATCH 077/200] Add padding above hover for second line --- .../editor/browser/viewParts/contentWidgets/contentWidgets.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts b/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts index c22e5e84ad0..7e1a22ab3ca 100644 --- a/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts +++ b/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts @@ -350,7 +350,7 @@ class Widget { const [belowLeft, absoluteBelowLeft] = this._layoutHorizontalSegmentInPage(windowSize, domNodePosition, bottomLeft.left - ctx.scrollLeft + this._contentLeft, width); // Leave some clearance to the top/bottom - const TOP_PADDING = 22; + const TOP_PADDING = 22 + (this._range?.startLineNumber === 2 ? this._lineHeight : 0); const BOTTOM_PADDING = 22; const fitsAbove = (absoluteAboveTop >= TOP_PADDING); From 067fbc80e37324644dabcfde2ea5d3f3cdedba48 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Sun, 23 Feb 2020 19:47:52 +0100 Subject: [PATCH 078/200] #90068 Adopt to revealPositionNearTop --- src/vs/editor/contrib/gotoError/gotoErrorWidget.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts b/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts index 68f5f944373..3f54fe3d786 100644 --- a/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts +++ b/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts @@ -316,7 +316,7 @@ export class MarkerNavigationWidget extends PeekViewWidget { } this._icon.className = `codicon ${SeverityIcon.className(MarkerSeverity.toSeverity(this._severity))}`; - this.editor.revealPositionInCenter(position, ScrollType.Smooth); + this.editor.revealPositionNearTop(position, ScrollType.Smooth); this.editor.focus(); } From d6e842b665990c663ef21718a65e73c9bcc2d730 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Sun, 23 Feb 2020 19:51:45 +0100 Subject: [PATCH 079/200] #90068 adopt to reval near top --- src/vs/editor/contrib/gotoError/gotoError.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/gotoError/gotoError.ts b/src/vs/editor/contrib/gotoError/gotoError.ts index 0de7e22fdb5..f1b1c298d1c 100644 --- a/src/vs/editor/contrib/gotoError/gotoError.ts +++ b/src/vs/editor/contrib/gotoError/gotoError.ts @@ -397,7 +397,7 @@ class MarkerNavigationAction extends EditorAction { return editorService.openCodeEditor({ resource: newMarker.resource, - options: { pinned: false, revealIfOpened: true, selectionRevealType: TextEditorSelectionRevealType.CenterIfOutsideViewport, selection: newMarker } + options: { pinned: false, revealIfOpened: true, selectionRevealType: TextEditorSelectionRevealType.NearTop, selection: newMarker } }, editor).then(editor => { if (!editor) { return undefined; From b893a201103dd5f090ab5ed5aaf5c6d5a1f31fe8 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Sun, 23 Feb 2020 20:26:38 +0100 Subject: [PATCH 080/200] Fix #89111 --- .../contrib/gotoError/gotoErrorWidget.ts | 34 +++++++++++++++++-- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts b/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts index 3f54fe3d786..6f33c54a4ab 100644 --- a/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts +++ b/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts @@ -52,11 +52,11 @@ class MessageWidget { const domNode = document.createElement('div'); domNode.className = 'descriptioncontainer'; - domNode.setAttribute('aria-live', 'assertive'); - domNode.setAttribute('role', 'alert'); this._messageBlock = document.createElement('div'); dom.addClass(this._messageBlock, 'message'); + this._messageBlock.setAttribute('aria-live', 'assertive'); + this._messageBlock.setAttribute('role', 'alert'); domNode.appendChild(this._messageBlock); this._relatedBlock = document.createElement('div'); @@ -88,7 +88,8 @@ class MessageWidget { dispose(this._disposables); } - update({ source, message, relatedInformation, code }: IMarker): void { + update(marker: IMarker): void { + const { source, message, relatedInformation, code } = marker; let sourceAndCodeLength = (source?.length || 0) + '()'.length; if (code) { if (typeof code === 'string') { @@ -106,6 +107,7 @@ class MessageWidget { } dom.clearNode(this._messageBlock); + this._messageBlock.setAttribute('aria-label', this.getAriaLabel(marker)); this._editor.applyFontInfo(this._messageBlock); let lastLineElement = this._messageBlock; for (const line of lines) { @@ -192,6 +194,32 @@ class MessageWidget { getHeightInLines(): number { return Math.min(17, this._lines); } + + private getAriaLabel(marker: IMarker): string { + let severityLabel = ''; + switch (marker.severity) { + case MarkerSeverity.Error: + severityLabel = nls.localize('Error', "Error"); + break; + case MarkerSeverity.Warning: + severityLabel = nls.localize('Warning', "Warning"); + break; + case MarkerSeverity.Info: + severityLabel = nls.localize('Info', "Info"); + break; + case MarkerSeverity.Hint: + severityLabel = nls.localize('Hint', "Hint"); + break; + } + + let ariaLabel = nls.localize('marker aria', "{0} at {1}. ", severityLabel, marker.startLineNumber + ':' + marker.startColumn); + const model = this._editor.getModel(); + if (model && (marker.startLineNumber <= model.getLineCount()) && (marker.startLineNumber >= 1)) { + const lineContent = model.getLineContent(marker.startLineNumber); + ariaLabel = `${lineContent}, ${ariaLabel}`; + } + return ariaLabel; + } } export class MarkerNavigationWidget extends PeekViewWidget { From 03b30ff3cceedb2f82718fecfba58d8a4cf4e71c Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Sun, 23 Feb 2020 21:15:08 +0100 Subject: [PATCH 081/200] [folding] add setting to allow clicking in empty space to unfold. Fixes #88522 --- src/vs/editor/common/config/editorOptions.ts | 10 ++ .../common/standalone/standaloneEnums.ts | 159 ++++++++--------- src/vs/editor/contrib/folding/folding.ts | 16 +- src/vs/monaco.d.ts | 165 +++++++++--------- 4 files changed, 191 insertions(+), 159 deletions(-) diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index eb7a44e4089..de910398d63 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -506,6 +506,11 @@ export interface IEditorOptions { * Defaults to 'mouseover'. */ showFoldingControls?: 'always' | 'mouseover'; + /** + * Controls whether clicking on the empty content after a folded line will unfold the line. + * Defaults to false. + */ + unfoldOnClickInEmptyContent?: boolean; /** * Enable highlighting of matching brackets. * Defaults to 'always'. @@ -3320,6 +3325,7 @@ export const enum EditorOption { folding, foldingStrategy, foldingHighlight, + unfoldOnClickInEmptyContent, fontFamily, fontInfo, fontLigatures, @@ -3619,6 +3625,10 @@ export const EditorOptions = { EditorOption.foldingHighlight, 'foldingHighlight', true, { description: nls.localize('foldingHighlight', "Controls whether the editor should highlight folded ranges.") } )), + unfoldOnClickInEmptyContent: register(new EditorBooleanOption( + EditorOption.unfoldOnClickInEmptyContent, 'unfoldOnClickInEmptyContent', false, + { description: nls.localize('unfoldOnClickInEmptyContent', "Controls whether clicking on the empty content after a folded line will unfold the line.") } + )), fontFamily: register(new EditorStringOption( EditorOption.fontFamily, 'fontFamily', EDITOR_FONT_DEFAULTS.fontFamily, { description: nls.localize('fontFamily', "Controls the font family.") } diff --git a/src/vs/editor/common/standalone/standaloneEnums.ts b/src/vs/editor/common/standalone/standaloneEnums.ts index 14e4cfb12fc..458faca8bf2 100644 --- a/src/vs/editor/common/standalone/standaloneEnums.ts +++ b/src/vs/editor/common/standalone/standaloneEnums.ts @@ -199,85 +199,86 @@ export enum EditorOption { folding = 31, foldingStrategy = 32, foldingHighlight = 33, - fontFamily = 34, - fontInfo = 35, - fontLigatures = 36, - fontSize = 37, - fontWeight = 38, - formatOnPaste = 39, - formatOnType = 40, - glyphMargin = 41, - gotoLocation = 42, - hideCursorInOverviewRuler = 43, - highlightActiveIndentGuide = 44, - hover = 45, - inDiffEditor = 46, - letterSpacing = 47, - lightbulb = 48, - lineDecorationsWidth = 49, - lineHeight = 50, - lineNumbers = 51, - lineNumbersMinChars = 52, - links = 53, - matchBrackets = 54, - minimap = 55, - mouseStyle = 56, - mouseWheelScrollSensitivity = 57, - mouseWheelZoom = 58, - multiCursorMergeOverlapping = 59, - multiCursorModifier = 60, - multiCursorPaste = 61, - occurrencesHighlight = 62, - overviewRulerBorder = 63, - overviewRulerLanes = 64, - padding = 65, - parameterHints = 66, - peekWidgetDefaultFocus = 67, - definitionLinkOpensInPeek = 68, - quickSuggestions = 69, - quickSuggestionsDelay = 70, - readOnly = 71, - renderControlCharacters = 72, - renderIndentGuides = 73, - renderFinalNewline = 74, - renderLineHighlight = 75, - renderValidationDecorations = 76, - renderWhitespace = 77, - revealHorizontalRightPadding = 78, - roundedSelection = 79, - rulers = 80, - scrollbar = 81, - scrollBeyondLastColumn = 82, - scrollBeyondLastLine = 83, - scrollPredominantAxis = 84, - selectionClipboard = 85, - selectionHighlight = 86, - selectOnLineNumbers = 87, - showFoldingControls = 88, - showUnused = 89, - snippetSuggestions = 90, - smoothScrolling = 91, - stopRenderingLineAfter = 92, - suggest = 93, - suggestFontSize = 94, - suggestLineHeight = 95, - suggestOnTriggerCharacters = 96, - suggestSelection = 97, - tabCompletion = 98, - useTabStops = 99, - wordSeparators = 100, - wordWrap = 101, - wordWrapBreakAfterCharacters = 102, - wordWrapBreakBeforeCharacters = 103, - wordWrapColumn = 104, - wordWrapMinified = 105, - wrappingIndent = 106, - wrappingStrategy = 107, - editorClassName = 108, - pixelRatio = 109, - tabFocusMode = 110, - layoutInfo = 111, - wrappingInfo = 112 + unfoldOnClickInEmptyContent = 34, + fontFamily = 35, + fontInfo = 36, + fontLigatures = 37, + fontSize = 38, + fontWeight = 39, + formatOnPaste = 40, + formatOnType = 41, + glyphMargin = 42, + gotoLocation = 43, + hideCursorInOverviewRuler = 44, + highlightActiveIndentGuide = 45, + hover = 46, + inDiffEditor = 47, + letterSpacing = 48, + lightbulb = 49, + lineDecorationsWidth = 50, + lineHeight = 51, + lineNumbers = 52, + lineNumbersMinChars = 53, + links = 54, + matchBrackets = 55, + minimap = 56, + mouseStyle = 57, + mouseWheelScrollSensitivity = 58, + mouseWheelZoom = 59, + multiCursorMergeOverlapping = 60, + multiCursorModifier = 61, + multiCursorPaste = 62, + occurrencesHighlight = 63, + overviewRulerBorder = 64, + overviewRulerLanes = 65, + padding = 66, + parameterHints = 67, + peekWidgetDefaultFocus = 68, + definitionLinkOpensInPeek = 69, + quickSuggestions = 70, + quickSuggestionsDelay = 71, + readOnly = 72, + renderControlCharacters = 73, + renderIndentGuides = 74, + renderFinalNewline = 75, + renderLineHighlight = 76, + renderValidationDecorations = 77, + renderWhitespace = 78, + revealHorizontalRightPadding = 79, + roundedSelection = 80, + rulers = 81, + scrollbar = 82, + scrollBeyondLastColumn = 83, + scrollBeyondLastLine = 84, + scrollPredominantAxis = 85, + selectionClipboard = 86, + selectionHighlight = 87, + selectOnLineNumbers = 88, + showFoldingControls = 89, + showUnused = 90, + snippetSuggestions = 91, + smoothScrolling = 92, + stopRenderingLineAfter = 93, + suggest = 94, + suggestFontSize = 95, + suggestLineHeight = 96, + suggestOnTriggerCharacters = 97, + suggestSelection = 98, + tabCompletion = 99, + useTabStops = 100, + wordSeparators = 101, + wordWrap = 102, + wordWrapBreakAfterCharacters = 103, + wordWrapBreakBeforeCharacters = 104, + wordWrapColumn = 105, + wordWrapMinified = 106, + wrappingIndent = 107, + wrappingStrategy = 108, + editorClassName = 109, + pixelRatio = 110, + tabFocusMode = 111, + layoutInfo = 112, + wrappingInfo = 113 } /** diff --git a/src/vs/editor/contrib/folding/folding.ts b/src/vs/editor/contrib/folding/folding.ts index bae5307d991..c2d01a5ce0e 100644 --- a/src/vs/editor/contrib/folding/folding.ts +++ b/src/vs/editor/contrib/folding/folding.ts @@ -19,7 +19,7 @@ import { FoldingDecorationProvider } from './foldingDecorations'; import { FoldingRegions, FoldingRegion } from './foldingRanges'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config/editorOptions'; -import { IMarginData } from 'vs/editor/browser/controller/mouseTarget'; +import { IMarginData, IEmptyContentData } from 'vs/editor/browser/controller/mouseTarget'; import { HiddenRangeModel } from 'vs/editor/contrib/folding/hiddenRangeModel'; import { IRange } from 'vs/editor/common/core/range'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; @@ -62,6 +62,7 @@ export class FoldingController extends Disposable implements IEditorContribution private readonly editor: ICodeEditor; private _isEnabled: boolean; private _useFoldingProviders: boolean; + private _unfoldOnClickInEmptyContent: boolean; private readonly foldingDecorationProvider: FoldingDecorationProvider; @@ -91,6 +92,7 @@ export class FoldingController extends Disposable implements IEditorContribution const options = this.editor.getOptions(); this._isEnabled = options.get(EditorOption.folding); this._useFoldingProviders = options.get(EditorOption.foldingStrategy) !== 'indentation'; + this._unfoldOnClickInEmptyContent = options.get(EditorOption.unfoldOnClickInEmptyContent); this.foldingModel = null; this.hiddenRangeModel = null; @@ -128,6 +130,9 @@ export class FoldingController extends Disposable implements IEditorContribution this._useFoldingProviders = options.get(EditorOption.foldingStrategy) !== 'indentation'; this.onFoldingStrategyChanged(); } + if (e.hasChanged(EditorOption.unfoldOnClickInEmptyContent)) { + this._unfoldOnClickInEmptyContent = options.get(EditorOption.unfoldOnClickInEmptyContent); + } })); this.onModelChanged(); } @@ -364,6 +369,15 @@ export class FoldingController extends Disposable implements IEditorContribution iconClicked = true; break; + case MouseTargetType.CONTENT_EMPTY: { + if (this._unfoldOnClickInEmptyContent && this.hiddenRangeModel.hasRanges()) { + const data = e.target.detail as IEmptyContentData; + if (!data.isAfterLines) { + break; + } + } + return; + } case MouseTargetType.CONTENT_TEXT: { if (this.hiddenRangeModel.hasRanges()) { let model = this.editor.getModel(); diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 4ca0cb14c12..65f7b8f4e4f 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -3031,6 +3031,11 @@ declare namespace monaco.editor { * Defaults to 'mouseover'. */ showFoldingControls?: 'always' | 'mouseover'; + /** + * Controls whether clicking on the empty content after a folded line will unfold the line. + * Defaults to false. + */ + unfoldOnClickInEmptyContent?: boolean; /** * Enable highlighting of matching brackets. * Defaults to 'always'. @@ -3820,85 +3825,86 @@ declare namespace monaco.editor { folding = 31, foldingStrategy = 32, foldingHighlight = 33, - fontFamily = 34, - fontInfo = 35, - fontLigatures = 36, - fontSize = 37, - fontWeight = 38, - formatOnPaste = 39, - formatOnType = 40, - glyphMargin = 41, - gotoLocation = 42, - hideCursorInOverviewRuler = 43, - highlightActiveIndentGuide = 44, - hover = 45, - inDiffEditor = 46, - letterSpacing = 47, - lightbulb = 48, - lineDecorationsWidth = 49, - lineHeight = 50, - lineNumbers = 51, - lineNumbersMinChars = 52, - links = 53, - matchBrackets = 54, - minimap = 55, - mouseStyle = 56, - mouseWheelScrollSensitivity = 57, - mouseWheelZoom = 58, - multiCursorMergeOverlapping = 59, - multiCursorModifier = 60, - multiCursorPaste = 61, - occurrencesHighlight = 62, - overviewRulerBorder = 63, - overviewRulerLanes = 64, - padding = 65, - parameterHints = 66, - peekWidgetDefaultFocus = 67, - definitionLinkOpensInPeek = 68, - quickSuggestions = 69, - quickSuggestionsDelay = 70, - readOnly = 71, - renderControlCharacters = 72, - renderIndentGuides = 73, - renderFinalNewline = 74, - renderLineHighlight = 75, - renderValidationDecorations = 76, - renderWhitespace = 77, - revealHorizontalRightPadding = 78, - roundedSelection = 79, - rulers = 80, - scrollbar = 81, - scrollBeyondLastColumn = 82, - scrollBeyondLastLine = 83, - scrollPredominantAxis = 84, - selectionClipboard = 85, - selectionHighlight = 86, - selectOnLineNumbers = 87, - showFoldingControls = 88, - showUnused = 89, - snippetSuggestions = 90, - smoothScrolling = 91, - stopRenderingLineAfter = 92, - suggest = 93, - suggestFontSize = 94, - suggestLineHeight = 95, - suggestOnTriggerCharacters = 96, - suggestSelection = 97, - tabCompletion = 98, - useTabStops = 99, - wordSeparators = 100, - wordWrap = 101, - wordWrapBreakAfterCharacters = 102, - wordWrapBreakBeforeCharacters = 103, - wordWrapColumn = 104, - wordWrapMinified = 105, - wrappingIndent = 106, - wrappingStrategy = 107, - editorClassName = 108, - pixelRatio = 109, - tabFocusMode = 110, - layoutInfo = 111, - wrappingInfo = 112 + unfoldOnClickInEmptyContent = 34, + fontFamily = 35, + fontInfo = 36, + fontLigatures = 37, + fontSize = 38, + fontWeight = 39, + formatOnPaste = 40, + formatOnType = 41, + glyphMargin = 42, + gotoLocation = 43, + hideCursorInOverviewRuler = 44, + highlightActiveIndentGuide = 45, + hover = 46, + inDiffEditor = 47, + letterSpacing = 48, + lightbulb = 49, + lineDecorationsWidth = 50, + lineHeight = 51, + lineNumbers = 52, + lineNumbersMinChars = 53, + links = 54, + matchBrackets = 55, + minimap = 56, + mouseStyle = 57, + mouseWheelScrollSensitivity = 58, + mouseWheelZoom = 59, + multiCursorMergeOverlapping = 60, + multiCursorModifier = 61, + multiCursorPaste = 62, + occurrencesHighlight = 63, + overviewRulerBorder = 64, + overviewRulerLanes = 65, + padding = 66, + parameterHints = 67, + peekWidgetDefaultFocus = 68, + definitionLinkOpensInPeek = 69, + quickSuggestions = 70, + quickSuggestionsDelay = 71, + readOnly = 72, + renderControlCharacters = 73, + renderIndentGuides = 74, + renderFinalNewline = 75, + renderLineHighlight = 76, + renderValidationDecorations = 77, + renderWhitespace = 78, + revealHorizontalRightPadding = 79, + roundedSelection = 80, + rulers = 81, + scrollbar = 82, + scrollBeyondLastColumn = 83, + scrollBeyondLastLine = 84, + scrollPredominantAxis = 85, + selectionClipboard = 86, + selectionHighlight = 87, + selectOnLineNumbers = 88, + showFoldingControls = 89, + showUnused = 90, + snippetSuggestions = 91, + smoothScrolling = 92, + stopRenderingLineAfter = 93, + suggest = 94, + suggestFontSize = 95, + suggestLineHeight = 96, + suggestOnTriggerCharacters = 97, + suggestSelection = 98, + tabCompletion = 99, + useTabStops = 100, + wordSeparators = 101, + wordWrap = 102, + wordWrapBreakAfterCharacters = 103, + wordWrapBreakBeforeCharacters = 104, + wordWrapColumn = 105, + wordWrapMinified = 106, + wrappingIndent = 107, + wrappingStrategy = 108, + editorClassName = 109, + pixelRatio = 110, + tabFocusMode = 111, + layoutInfo = 112, + wrappingInfo = 113 } export const EditorOptions: { acceptSuggestionOnCommitCharacter: IEditorOption; @@ -3935,6 +3941,7 @@ declare namespace monaco.editor { folding: IEditorOption; foldingStrategy: IEditorOption; foldingHighlight: IEditorOption; + unfoldOnClickInEmptyContent: IEditorOption; fontFamily: IEditorOption; fontInfo: IEditorOption; fontLigatures2: IEditorOption; From 4c0a06c7ea619f1564bcfeecf1ba7111f6e0fe6d Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Sat, 22 Feb 2020 23:29:32 +0100 Subject: [PATCH 082/200] let -> const --- .../common/viewLayout/lineDecorations.ts | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/vs/editor/common/viewLayout/lineDecorations.ts b/src/vs/editor/common/viewLayout/lineDecorations.ts index 367abd68f82..074dbea4045 100644 --- a/src/vs/editor/common/viewLayout/lineDecorations.ts +++ b/src/vs/editor/common/viewLayout/lineDecorations.ts @@ -28,8 +28,8 @@ export class LineDecoration { } public static equalsArr(a: LineDecoration[], b: LineDecoration[]): boolean { - let aLen = a.length; - let bLen = b.length; + const aLen = a.length; + const bLen = b.length; if (aLen !== bLen) { return false; } @@ -49,8 +49,8 @@ export class LineDecoration { let result: LineDecoration[] = [], resultLen = 0; for (let i = 0, len = lineDecorations.length; i < len; i++) { - let d = lineDecorations[i]; - let range = d.range; + const d = lineDecorations[i]; + const range = d.range; if (range.endLineNumber < lineNumber || range.startLineNumber > lineNumber) { // Ignore decorations that sit outside this line @@ -62,8 +62,8 @@ export class LineDecoration { continue; } - let startColumn = (range.startLineNumber === lineNumber ? range.startColumn : minLineColumn); - let endColumn = (range.endLineNumber === lineNumber ? range.endColumn : maxLineColumn); + const startColumn = (range.startLineNumber === lineNumber ? range.startColumn : minLineColumn); + const endColumn = (range.endLineNumber === lineNumber ? range.endColumn : maxLineColumn); result[resultLen++] = new LineDecoration(startColumn, endColumn, d.inlineClassName, d.type); } @@ -170,14 +170,14 @@ export class LineDecorationsNormalizer { let result: DecorationSegment[] = []; - let stack = new Stack(); + const stack = new Stack(); let nextStartOffset = 0; for (let i = 0, len = lineDecorations.length; i < len; i++) { - let d = lineDecorations[i]; + const d = lineDecorations[i]; let startColumn = d.startColumn; let endColumn = d.endColumn; - let className = d.className; + const className = d.className; // If the position would end up in the middle of a high-low surrogate pair, we move it to before the pair if (startColumn > 1) { @@ -194,8 +194,8 @@ export class LineDecorationsNormalizer { } } - let currentStartOffset = startColumn - 1; - let currentEndOffset = endColumn - 2; + const currentStartOffset = startColumn - 1; + const currentEndOffset = endColumn - 2; nextStartOffset = stack.consumeLowerThan(currentStartOffset, nextStartOffset, result); From 0792b07962e93296c85d77f514e4b358f13774be Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Sun, 23 Feb 2020 17:41:08 +0100 Subject: [PATCH 083/200] Add LinePart.metadata (for #91178) --- .../common/viewLayout/lineDecorations.ts | 33 ++++++++-- .../common/viewLayout/viewLineRenderer.ts | 61 +++++++++++++------ .../common/viewLayout/lineDecorations.test.ts | 40 ++++++------ 3 files changed, 89 insertions(+), 45 deletions(-) diff --git a/src/vs/editor/common/viewLayout/lineDecorations.ts b/src/vs/editor/common/viewLayout/lineDecorations.ts index 074dbea4045..1c49b6b95ba 100644 --- a/src/vs/editor/common/viewLayout/lineDecorations.ts +++ b/src/vs/editor/common/viewLayout/lineDecorations.ts @@ -6,6 +6,7 @@ import * as strings from 'vs/base/common/strings'; import { Constants } from 'vs/base/common/uint'; import { InlineDecoration, InlineDecorationType } from 'vs/editor/common/viewModel/viewModel'; +import { LinePartMetadata } from 'vs/editor/common/viewLayout/viewLineRenderer'; export class LineDecoration { _lineDecorationBrand: void; @@ -92,11 +93,13 @@ export class DecorationSegment { startOffset: number; endOffset: number; className: string; + metadata: number; - constructor(startOffset: number, endOffset: number, className: string) { + constructor(startOffset: number, endOffset: number, className: string, metadata: number) { this.startOffset = startOffset; this.endOffset = endOffset; this.className = className; + this.metadata = metadata; } } @@ -104,13 +107,23 @@ class Stack { public count: number; private readonly stopOffsets: number[]; private readonly classNames: string[]; + private readonly metadata: number[]; constructor() { this.stopOffsets = []; this.classNames = []; + this.metadata = []; this.count = 0; } + private static _metadata(metadata: number[]): number { + let result = 0; + for (let i = 0, len = metadata.length; i < len; i++) { + result |= metadata[i]; + } + return result; + } + public consumeLowerThan(maxStopOffset: number, nextStartOffset: number, result: DecorationSegment[]): number { while (this.count > 0 && this.stopOffsets[0] < maxStopOffset) { @@ -122,34 +135,37 @@ class Stack { } // Basically we are consuming the first i + 1 elements of the stack - result.push(new DecorationSegment(nextStartOffset, this.stopOffsets[i], this.classNames.join(' '))); + result.push(new DecorationSegment(nextStartOffset, this.stopOffsets[i], this.classNames.join(' '), Stack._metadata(this.metadata))); nextStartOffset = this.stopOffsets[i] + 1; // Consume them this.stopOffsets.splice(0, i + 1); this.classNames.splice(0, i + 1); + this.metadata.splice(0, i + 1); this.count -= (i + 1); } if (this.count > 0 && nextStartOffset < maxStopOffset) { - result.push(new DecorationSegment(nextStartOffset, maxStopOffset - 1, this.classNames.join(' '))); + result.push(new DecorationSegment(nextStartOffset, maxStopOffset - 1, this.classNames.join(' '), Stack._metadata(this.metadata))); nextStartOffset = maxStopOffset; } return nextStartOffset; } - public insert(stopOffset: number, className: string): void { + public insert(stopOffset: number, className: string, metadata: number): void { if (this.count === 0 || this.stopOffsets[this.count - 1] <= stopOffset) { // Insert at the end this.stopOffsets.push(stopOffset); this.classNames.push(className); + this.metadata.push(metadata); } else { // Find the insertion position for `stopOffset` for (let i = 0; i < this.count; i++) { if (this.stopOffsets[i] >= stopOffset) { this.stopOffsets.splice(i, 0, stopOffset); this.classNames.splice(i, 0, className); + this.metadata.splice(i, 0, metadata); break; } } @@ -178,6 +194,13 @@ export class LineDecorationsNormalizer { let startColumn = d.startColumn; let endColumn = d.endColumn; const className = d.className; + const metadata = ( + d.type === InlineDecorationType.Before + ? LinePartMetadata.PSEUDO_BEFORE + : d.type === InlineDecorationType.After + ? LinePartMetadata.PSEUDO_AFTER + : 0 + ); // If the position would end up in the middle of a high-low surrogate pair, we move it to before the pair if (startColumn > 1) { @@ -202,7 +225,7 @@ export class LineDecorationsNormalizer { if (stack.count === 0) { nextStartOffset = currentStartOffset; } - stack.insert(currentEndOffset, className); + stack.insert(currentEndOffset, className, metadata); } stack.consumeLowerThan(Constants.MAX_SAFE_SMALL_INTEGER, nextStartOffset, result); diff --git a/src/vs/editor/common/viewLayout/viewLineRenderer.ts b/src/vs/editor/common/viewLayout/viewLineRenderer.ts index 05e628ab095..2ac95ca4276 100644 --- a/src/vs/editor/common/viewLayout/viewLineRenderer.ts +++ b/src/vs/editor/common/viewLayout/viewLineRenderer.ts @@ -17,6 +17,16 @@ export const enum RenderWhitespace { All = 3 } +export const enum LinePartMetadata { + IS_WHITESPACE = 1, + PSEUDO_BEFORE = 2, + PSEUDO_AFTER = 4, + + IS_WHITESPACE_MASK = 0b001, + PSEUDO_BEFORE_MASK = 0b010, + PSEUDO_AFTER_MASK = 0b100, +} + class LinePart { _linePartBrand: void; @@ -25,10 +35,16 @@ class LinePart { */ public readonly endIndex: number; public readonly type: string; + public readonly metadata: number; - constructor(endIndex: number, type: string) { + constructor(endIndex: number, type: string, metadata: number) { this.endIndex = endIndex; this.type = type; + this.metadata = metadata; + } + + public isWhitespace(): boolean { + return (this.metadata & LinePartMetadata.IS_WHITESPACE_MASK ? true : false); } } @@ -470,7 +486,7 @@ function transformAndRemoveOverflowing(tokens: IViewLineTokens, fauxIndentLength // The faux indent part of the line should have no token type if (fauxIndentLength > 0) { - result[resultLen++] = new LinePart(fauxIndentLength, ''); + result[resultLen++] = new LinePart(fauxIndentLength, '', 0); } for (let tokenIndex = 0, tokensLen = tokens.getCount(); tokenIndex < tokensLen; tokenIndex++) { @@ -481,10 +497,10 @@ function transformAndRemoveOverflowing(tokens: IViewLineTokens, fauxIndentLength } const type = tokens.getClassName(tokenIndex); if (endIndex >= len) { - result[resultLen++] = new LinePart(len, type); + result[resultLen++] = new LinePart(len, type, 0); break; } - result[resultLen++] = new LinePart(endIndex, type); + result[resultLen++] = new LinePart(endIndex, type, 0); } return result; @@ -513,6 +529,7 @@ function splitLargeTokens(lineContent: string, tokens: LinePart[], onlyAtSpaces: const tokenEndIndex = token.endIndex; if (lastTokenEndIndex + Constants.LongToken < tokenEndIndex) { const tokenType = token.type; + const tokenMetadata = token.metadata; let lastSpaceOffset = -1; let currTokenStart = lastTokenEndIndex; @@ -522,13 +539,13 @@ function splitLargeTokens(lineContent: string, tokens: LinePart[], onlyAtSpaces: } if (lastSpaceOffset !== -1 && j - currTokenStart >= Constants.LongToken) { // Split at `lastSpaceOffset` + 1 - result[resultLen++] = new LinePart(lastSpaceOffset + 1, tokenType); + result[resultLen++] = new LinePart(lastSpaceOffset + 1, tokenType, tokenMetadata); currTokenStart = lastSpaceOffset + 1; lastSpaceOffset = -1; } } if (currTokenStart !== tokenEndIndex) { - result[resultLen++] = new LinePart(tokenEndIndex, tokenType); + result[resultLen++] = new LinePart(tokenEndIndex, tokenType, tokenMetadata); } } else { result[resultLen++] = token; @@ -544,12 +561,13 @@ function splitLargeTokens(lineContent: string, tokens: LinePart[], onlyAtSpaces: let diff = (tokenEndIndex - lastTokenEndIndex); if (diff > Constants.LongToken) { const tokenType = token.type; + const tokenMetadata = token.metadata; const piecesCount = Math.ceil(diff / Constants.LongToken); for (let j = 1; j < piecesCount; j++) { let pieceEndIndex = lastTokenEndIndex + (j * Constants.LongToken); - result[resultLen++] = new LinePart(pieceEndIndex, tokenType); + result[resultLen++] = new LinePart(pieceEndIndex, tokenType, tokenMetadata); } - result[resultLen++] = new LinePart(tokenEndIndex, tokenType); + result[resultLen++] = new LinePart(tokenEndIndex, tokenType, tokenMetadata); } else { result[resultLen++] = token; } @@ -640,17 +658,17 @@ function _applyRenderWhitespace(input: RenderLineInput, lineContent: string, len if (generateLinePartForEachWhitespace) { const lastEndIndex = (resultLen > 0 ? result[resultLen - 1].endIndex : fauxIndentLength); for (let i = lastEndIndex + 1; i <= charIndex; i++) { - result[resultLen++] = new LinePart(i, 'mtkw'); + result[resultLen++] = new LinePart(i, 'mtkw', LinePartMetadata.IS_WHITESPACE); } } else { - result[resultLen++] = new LinePart(charIndex, 'mtkw'); + result[resultLen++] = new LinePart(charIndex, 'mtkw', LinePartMetadata.IS_WHITESPACE); } tmpIndent = tmpIndent % tabSize; } } else { // was in regular token if (charIndex === tokenEndIndex || (isInWhitespace && charIndex > fauxIndentLength)) { - result[resultLen++] = new LinePart(charIndex, tokenType); + result[resultLen++] = new LinePart(charIndex, tokenType, 0); tmpIndent = tmpIndent % tabSize; } } @@ -693,13 +711,13 @@ function _applyRenderWhitespace(input: RenderLineInput, lineContent: string, len if (generateLinePartForEachWhitespace) { const lastEndIndex = (resultLen > 0 ? result[resultLen - 1].endIndex : fauxIndentLength); for (let i = lastEndIndex + 1; i <= len; i++) { - result[resultLen++] = new LinePart(i, 'mtkw'); + result[resultLen++] = new LinePart(i, 'mtkw', LinePartMetadata.IS_WHITESPACE); } } else { - result[resultLen++] = new LinePart(len, 'mtkw'); + result[resultLen++] = new LinePart(len, 'mtkw', LinePartMetadata.IS_WHITESPACE); } } else { - result[resultLen++] = new LinePart(len, tokenType); + result[resultLen++] = new LinePart(len, tokenType, 0); } return result; @@ -720,42 +738,45 @@ function _applyInlineDecorations(lineContent: string, len: number, tokens: LineP const token = tokens[tokenIndex]; const tokenEndIndex = token.endIndex; const tokenType = token.type; + const tokenMetadata = token.metadata; while (lineDecorationIndex < lineDecorationsLen && lineDecorations[lineDecorationIndex].startOffset < tokenEndIndex) { const lineDecoration = lineDecorations[lineDecorationIndex]; if (lineDecoration.startOffset > lastResultEndIndex) { lastResultEndIndex = lineDecoration.startOffset; - result[resultLen++] = new LinePart(lastResultEndIndex, tokenType); + result[resultLen++] = new LinePart(lastResultEndIndex, tokenType, tokenMetadata); } if (lineDecoration.endOffset + 1 <= tokenEndIndex) { // This line decoration ends before this token ends lastResultEndIndex = lineDecoration.endOffset + 1; - result[resultLen++] = new LinePart(lastResultEndIndex, tokenType + ' ' + lineDecoration.className); + result[resultLen++] = new LinePart(lastResultEndIndex, tokenType + ' ' + lineDecoration.className, tokenMetadata | lineDecoration.metadata); lineDecorationIndex++; } else { // This line decoration continues on to the next token lastResultEndIndex = tokenEndIndex; - result[resultLen++] = new LinePart(lastResultEndIndex, tokenType + ' ' + lineDecoration.className); + result[resultLen++] = new LinePart(lastResultEndIndex, tokenType + ' ' + lineDecoration.className, tokenMetadata | lineDecoration.metadata); break; } } if (tokenEndIndex > lastResultEndIndex) { lastResultEndIndex = tokenEndIndex; - result[resultLen++] = new LinePart(lastResultEndIndex, tokenType); + result[resultLen++] = new LinePart(lastResultEndIndex, tokenType, tokenMetadata); } } const lastTokenEndIndex = tokens[tokens.length - 1].endIndex; if (lineDecorationIndex < lineDecorationsLen && lineDecorations[lineDecorationIndex].startOffset === lastTokenEndIndex) { let classNames: string[] = []; + let metadata = 0; while (lineDecorationIndex < lineDecorationsLen && lineDecorations[lineDecorationIndex].startOffset === lastTokenEndIndex) { classNames.push(lineDecorations[lineDecorationIndex].className); + metadata |= lineDecorations[lineDecorationIndex].metadata; lineDecorationIndex++; } - result[resultLen++] = new LinePart(lastResultEndIndex, classNames.join(' ')); + result[resultLen++] = new LinePart(lastResultEndIndex, classNames.join(' '), metadata); } return result; @@ -799,7 +820,7 @@ function _renderLine(input: ResolvedRenderLineInput, sb: IStringBuilder): Render const part = parts[partIndex]; const partEndIndex = part.endIndex; const partType = part.type; - const partRendersWhitespace = (renderWhitespace !== RenderWhitespace.None && (partType.indexOf('mtkw') >= 0)); + const partRendersWhitespace = (renderWhitespace !== RenderWhitespace.None && part.isWhitespace()); const partRendersWhitespaceWithWidth = partRendersWhitespace && !fontIsMonospace && (partType === 'mtkw'/*only whitespace*/ || !containsForeignElements); charOffsetInPart = 0; diff --git a/src/vs/editor/test/common/viewLayout/lineDecorations.test.ts b/src/vs/editor/test/common/viewLayout/lineDecorations.test.ts index 053a3ef301a..8be7627bc72 100644 --- a/src/vs/editor/test/common/viewLayout/lineDecorations.test.ts +++ b/src/vs/editor/test/common/viewLayout/lineDecorations.test.ts @@ -18,9 +18,9 @@ suite('Editor ViewLayout - ViewLineParts', () => { ]); assert.deepEqual(result, [ - new DecorationSegment(0, 1, 'c1'), - new DecorationSegment(2, 2, 'c2 c1'), - new DecorationSegment(3, 9, 'c1'), + new DecorationSegment(0, 1, 'c1', 0), + new DecorationSegment(2, 2, 'c2 c1', 0), + new DecorationSegment(3, 9, 'c1', 0), ]); }); @@ -32,8 +32,8 @@ suite('Editor ViewLayout - ViewLineParts', () => { ]); assert.deepEqual(result, [ - new DecorationSegment(14, 18, 'mtkw'), - new DecorationSegment(19, 19, 'mtkw inline-folded') + new DecorationSegment(14, 18, 'mtkw', 0), + new DecorationSegment(19, 19, 'mtkw inline-folded', 0) ]); }); @@ -66,24 +66,24 @@ suite('Editor ViewLayout - ViewLineParts', () => { new LineDecoration(1, 2, 'c1', InlineDecorationType.Regular), new LineDecoration(3, 4, 'c2', InlineDecorationType.Regular) ]), [ - new DecorationSegment(0, 0, 'c1'), - new DecorationSegment(2, 2, 'c2') + new DecorationSegment(0, 0, 'c1', 0), + new DecorationSegment(2, 2, 'c2', 0) ]); assert.deepEqual(LineDecorationsNormalizer.normalize('abcabcabcabcabcabcabcabcabcabc', [ new LineDecoration(1, 3, 'c1', InlineDecorationType.Regular), new LineDecoration(3, 4, 'c2', InlineDecorationType.Regular) ]), [ - new DecorationSegment(0, 1, 'c1'), - new DecorationSegment(2, 2, 'c2') + new DecorationSegment(0, 1, 'c1', 0), + new DecorationSegment(2, 2, 'c2', 0) ]); assert.deepEqual(LineDecorationsNormalizer.normalize('abcabcabcabcabcabcabcabcabcabc', [ new LineDecoration(1, 4, 'c1', InlineDecorationType.Regular), new LineDecoration(3, 4, 'c2', InlineDecorationType.Regular) ]), [ - new DecorationSegment(0, 1, 'c1'), - new DecorationSegment(2, 2, 'c1 c2') + new DecorationSegment(0, 1, 'c1', 0), + new DecorationSegment(2, 2, 'c1 c2', 0) ]); assert.deepEqual(LineDecorationsNormalizer.normalize('abcabcabcabcabcabcabcabcabcabc', [ @@ -91,8 +91,8 @@ suite('Editor ViewLayout - ViewLineParts', () => { new LineDecoration(1, 4, 'c1*', InlineDecorationType.Regular), new LineDecoration(3, 4, 'c2', InlineDecorationType.Regular) ]), [ - new DecorationSegment(0, 1, 'c1 c1*'), - new DecorationSegment(2, 2, 'c1 c1* c2') + new DecorationSegment(0, 1, 'c1 c1*', 0), + new DecorationSegment(2, 2, 'c1 c1* c2', 0) ]); assert.deepEqual(LineDecorationsNormalizer.normalize('abcabcabcabcabcabcabcabcabcabc', [ @@ -101,8 +101,8 @@ suite('Editor ViewLayout - ViewLineParts', () => { new LineDecoration(1, 4, 'c1**', InlineDecorationType.Regular), new LineDecoration(3, 4, 'c2', InlineDecorationType.Regular) ]), [ - new DecorationSegment(0, 1, 'c1 c1* c1**'), - new DecorationSegment(2, 2, 'c1 c1* c1** c2') + new DecorationSegment(0, 1, 'c1 c1* c1**', 0), + new DecorationSegment(2, 2, 'c1 c1* c1** c2', 0) ]); assert.deepEqual(LineDecorationsNormalizer.normalize('abcabcabcabcabcabcabcabcabcabc', [ @@ -112,8 +112,8 @@ suite('Editor ViewLayout - ViewLineParts', () => { new LineDecoration(3, 4, 'c2', InlineDecorationType.Regular), new LineDecoration(3, 4, 'c2*', InlineDecorationType.Regular) ]), [ - new DecorationSegment(0, 1, 'c1 c1* c1**'), - new DecorationSegment(2, 2, 'c1 c1* c1** c2 c2*') + new DecorationSegment(0, 1, 'c1 c1* c1**', 0), + new DecorationSegment(2, 2, 'c1 c1* c1** c2 c2*', 0) ]); assert.deepEqual(LineDecorationsNormalizer.normalize('abcabcabcabcabcabcabcabcabcabc', [ @@ -123,9 +123,9 @@ suite('Editor ViewLayout - ViewLineParts', () => { new LineDecoration(3, 4, 'c2', InlineDecorationType.Regular), new LineDecoration(3, 5, 'c2*', InlineDecorationType.Regular) ]), [ - new DecorationSegment(0, 1, 'c1 c1* c1**'), - new DecorationSegment(2, 2, 'c1 c1* c1** c2 c2*'), - new DecorationSegment(3, 3, 'c2*') + new DecorationSegment(0, 1, 'c1 c1* c1**', 0), + new DecorationSegment(2, 2, 'c1 c1* c1** c2 c2*', 0), + new DecorationSegment(3, 3, 'c2*', 0) ]); }); }); From ee764991ac4f0f5f83a4bc79ce1dd96183a5519d Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Sun, 23 Feb 2020 21:47:25 +0100 Subject: [PATCH 084/200] Fixes #91178: Use previous part when dealing with offsets with an after inline decoration --- .../browser/viewParts/lines/rangeUtil.ts | 7 ++ .../common/viewLayout/lineDecorations.ts | 21 +++-- .../common/viewLayout/viewLineRenderer.ts | 14 ++- .../viewLayout/viewLineRenderer.test.ts | 87 +++++++++++++++++++ 4 files changed, 121 insertions(+), 8 deletions(-) diff --git a/src/vs/editor/browser/viewParts/lines/rangeUtil.ts b/src/vs/editor/browser/viewParts/lines/rangeUtil.ts index f84a79df8d4..7718ae4003a 100644 --- a/src/vs/editor/browser/viewParts/lines/rangeUtil.ts +++ b/src/vs/editor/browser/viewParts/lines/rangeUtil.ts @@ -121,6 +121,13 @@ export class RangeUtil { startChildIndex = Math.min(max, Math.max(min, startChildIndex)); endChildIndex = Math.min(max, Math.max(min, endChildIndex)); + if (startChildIndex === endChildIndex && startOffset === endOffset && startOffset === 0) { + // We must find the position at the beginning of a + // To cover cases of empty s, aboid using a range and use the 's bounding box + const clientRects = domNode.children[startChildIndex].getClientRects(); + return this._createHorizontalRangesFromClientRects(clientRects, clientRectDeltaLeft); + } + // If crossing over to a span only to select offset 0, then use the previous span's maximum offset // Chrome is buggy and doesn't handle 0 offsets well sometimes. if (startChildIndex !== endChildIndex) { diff --git a/src/vs/editor/common/viewLayout/lineDecorations.ts b/src/vs/editor/common/viewLayout/lineDecorations.ts index 1c49b6b95ba..74428ae28d7 100644 --- a/src/vs/editor/common/viewLayout/lineDecorations.ts +++ b/src/vs/editor/common/viewLayout/lineDecorations.ts @@ -72,16 +72,25 @@ export class LineDecoration { return result; } + private static _typeCompare(a: InlineDecorationType, b: InlineDecorationType): number { + const ORDER = [2, 0, 1, 3]; + return ORDER[a] - ORDER[b]; + } + public static compare(a: LineDecoration, b: LineDecoration): number { if (a.startColumn === b.startColumn) { if (a.endColumn === b.endColumn) { - if (a.className < b.className) { - return -1; + const typeCmp = LineDecoration._typeCompare(a.type, b.type); + if (typeCmp === 0) { + if (a.className < b.className) { + return -1; + } + if (a.className > b.className) { + return 1; + } + return 0; } - if (a.className > b.className) { - return 1; - } - return 0; + return typeCmp; } return a.endColumn - b.endColumn; } diff --git a/src/vs/editor/common/viewLayout/viewLineRenderer.ts b/src/vs/editor/common/viewLayout/viewLineRenderer.ts index 2ac95ca4276..c032724b8ba 100644 --- a/src/vs/editor/common/viewLayout/viewLineRenderer.ts +++ b/src/vs/editor/common/viewLayout/viewLineRenderer.ts @@ -809,6 +809,7 @@ function _renderLine(input: ResolvedRenderLineInput, sb: IStringBuilder): Render let visibleColumn = startVisibleColumn; let charOffsetInPart = 0; + let partDisplacement = 0; let prevPartContentCnt = 0; let partAbsoluteOffset = 0; @@ -822,6 +823,7 @@ function _renderLine(input: ResolvedRenderLineInput, sb: IStringBuilder): Render const partType = part.type; const partRendersWhitespace = (renderWhitespace !== RenderWhitespace.None && part.isWhitespace()); const partRendersWhitespaceWithWidth = partRendersWhitespace && !fontIsMonospace && (partType === 'mtkw'/*only whitespace*/ || !containsForeignElements); + const partIsEmptyAndHasPseudoAfter = (charIndex === partEndIndex && part.metadata === LinePartMetadata.PSEUDO_AFTER); charOffsetInPart = 0; sb.appendASCIIString('//just\u00a0a\u00a0com', + '', + '', + 'ment', + ].join(''); + + const expectedCharacterMapping = new CharacterMapping(17, 4); + expectedCharacterMapping.setPartData(0, 0, 0, 0); + expectedCharacterMapping.setPartData(1, 0, 1, 0); + expectedCharacterMapping.setPartData(2, 0, 2, 0); + expectedCharacterMapping.setPartData(3, 0, 3, 0); + expectedCharacterMapping.setPartData(4, 0, 4, 0); + expectedCharacterMapping.setPartData(5, 0, 5, 0); + expectedCharacterMapping.setPartData(6, 0, 6, 0); + expectedCharacterMapping.setPartData(7, 0, 7, 0); + expectedCharacterMapping.setPartData(8, 0, 8, 0); + expectedCharacterMapping.setPartData(9, 0, 9, 0); + expectedCharacterMapping.setPartData(10, 0, 10, 0); + expectedCharacterMapping.setPartData(11, 0, 11, 0); + expectedCharacterMapping.setPartData(12, 2, 0, 12); + expectedCharacterMapping.setPartData(13, 3, 1, 12); + expectedCharacterMapping.setPartData(14, 3, 2, 12); + expectedCharacterMapping.setPartData(15, 3, 3, 12); + expectedCharacterMapping.setPartData(16, 3, 4, 12); + + const actual = renderViewLine(new RenderLineInput( + true, + false, + lineText, + false, + true, + false, + 0, + lineParts, + [ + new LineDecoration(13, 13, 'dec1', InlineDecorationType.After), + new LineDecoration(13, 13, 'dec2', InlineDecorationType.Before), + ], + 4, + 0, + 10, + 10, + 10, + -1, + 'none', + false, + false, + null + )); + + assert.equal(actual.html, '' + expectedOutput + ''); + assertCharacterMapping2(actual.characterMapping, expectedCharacterMapping); + }); + test('issue Microsoft/monaco-editor#280: Improved source code rendering for RTL languages', () => { let lineText = 'var קודמות = \"מיותר קודמות צ\'ט של, אם לשון העברית שינויים ויש, אם\";'; @@ -693,6 +753,33 @@ suite('viewLineRenderer.renderLine', () => { assert.equal(_actual.html, '' + expectedOutput + ''); }); + interface ICharMappingData { + charOffset: number; + partIndex: number; + charIndex: number; + } + + function decodeCharacterMapping(source: CharacterMapping) { + const mapping: ICharMappingData[] = []; + for (let charOffset = 0; charOffset < source.length; charOffset++) { + const partData = source.charOffsetToPartData(charOffset); + const partIndex = CharacterMapping.getPartIndex(partData); + const charIndex = CharacterMapping.getCharIndex(partData); + mapping.push({ charOffset, partIndex, charIndex }); + } + const absoluteOffsets: number[] = []; + for (const absoluteOffset of source.getAbsoluteOffsets()) { + absoluteOffsets.push(absoluteOffset); + } + return { mapping, absoluteOffsets }; + } + + function assertCharacterMapping2(actual: CharacterMapping, expected: CharacterMapping): void { + const _actual = decodeCharacterMapping(actual); + const _expected = decodeCharacterMapping(expected); + assert.deepEqual(_actual, _expected); + } + function assertCharacterMapping(actual: CharacterMapping, expectedCharPartOffsets: number[][], expectedPartLengths: number[]): void { assertCharPartOffsets(actual, expectedCharPartOffsets); From bf4a9e26d836a76625571552f813cdc9a5a2ed17 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Sun, 23 Feb 2020 21:56:16 +0100 Subject: [PATCH 085/200] Fixes #90819: High contrast is more important than render whitespace in selection --- src/vs/editor/browser/viewParts/lines/viewLine.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/browser/viewParts/lines/viewLine.ts b/src/vs/editor/browser/viewParts/lines/viewLine.ts index 0c1b66decdd..9c93f326a6f 100644 --- a/src/vs/editor/browser/viewParts/lines/viewLine.ts +++ b/src/vs/editor/browser/viewParts/lines/viewLine.ts @@ -195,7 +195,7 @@ export class ViewLine implements IVisibleLine { const endColumn = (selection.endLineNumber === lineNumber ? selection.endColumn : lineData.maxColumn); if (startColumn < endColumn) { - if (this._options.renderWhitespace !== 'selection') { + if (options.themeType === HIGH_CONTRAST || this._options.renderWhitespace !== 'selection') { actualInlineDecorations.push(new LineDecoration(startColumn, endColumn, 'inline-selected-text', InlineDecorationType.Regular)); } else { if (!selectionsOnLine) { From d5513f2fe25fbb8d39c27cc11b2200c5bc21d86b Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Sun, 23 Feb 2020 22:42:40 +0100 Subject: [PATCH 086/200] Fixes microsoft/monaco-editor#1810 --- .../editor/browser/viewParts/contentWidgets/contentWidgets.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts b/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts index 7e1a22ab3ca..a3a16436846 100644 --- a/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts +++ b/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts @@ -350,7 +350,7 @@ class Widget { const [belowLeft, absoluteBelowLeft] = this._layoutHorizontalSegmentInPage(windowSize, domNodePosition, bottomLeft.left - ctx.scrollLeft + this._contentLeft, width); // Leave some clearance to the top/bottom - const TOP_PADDING = 22 + (this._range?.startLineNumber === 2 ? this._lineHeight : 0); + const TOP_PADDING = 22; const BOTTOM_PADDING = 22; const fitsAbove = (absoluteAboveTop >= TOP_PADDING); @@ -369,7 +369,7 @@ class Widget { return { fitsAbove, - aboveTop: Math.max(aboveTop, TOP_PADDING), + aboveTop: aboveTop, aboveLeft, fitsBelow, belowTop, From e5d4033931baa62a79f2cf618608d512973b4a69 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Sun, 23 Feb 2020 23:04:32 +0100 Subject: [PATCH 087/200] Fixes #91203 --- src/vs/editor/common/controller/cursor.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/common/controller/cursor.ts b/src/vs/editor/common/controller/cursor.ts index cd488b900ac..883abf19c6d 100644 --- a/src/vs/editor/common/controller/cursor.ts +++ b/src/vs/editor/common/controller/cursor.ts @@ -297,7 +297,7 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors { return this._cursors.getAll(); } - public setStates(source: string, reason: CursorChangeReason, states: PartialCursorState[] | null): void { + public setStates(source: string, reason: CursorChangeReason, states: PartialCursorState[] | null): boolean { if (states !== null && states.length > Cursor.MAX_CURSOR_COUNT) { states = states.slice(0, Cursor.MAX_CURSOR_COUNT); this._onDidReachMaxCursorCount.fire(undefined); @@ -311,7 +311,7 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors { this._validateAutoClosedActions(); - this._emitStateChangedIfNecessary(source, reason, oldState); + return this._emitStateChangedIfNecessary(source, reason, oldState); } public setColumnSelectData(columnSelectData: IColumnSelectData): void { @@ -411,7 +411,9 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors { } else { if (this._hasFocus && e.resultingSelection && e.resultingSelection.length > 0) { const cursorState = CursorState.fromModelSelections(e.resultingSelection); - this.setStates('modelChange', e.isUndoing ? CursorChangeReason.Undo : e.isRedoing ? CursorChangeReason.Redo : CursorChangeReason.RecoverFromMarkers, cursorState); + if (this.setStates('modelChange', e.isUndoing ? CursorChangeReason.Undo : e.isRedoing ? CursorChangeReason.Redo : CursorChangeReason.RecoverFromMarkers, cursorState)) { + this._revealRange('modelChange', RevealTarget.Primary, viewEvents.VerticalRevealType.Simple, true, editorCommon.ScrollType.Smooth); + } } else { const selectionsFromMarkers = this._cursors.readSelectionFromMarkers(); this.setStates('modelChange', CursorChangeReason.RecoverFromMarkers, CursorState.fromModelSelections(selectionsFromMarkers)); From 07c33977da255861edf71e65b18249d2f67d0762 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Sun, 23 Feb 2020 23:06:58 +0100 Subject: [PATCH 088/200] Fixes #91177 --- src/vs/editor/common/config/commonEditorConfig.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/common/config/commonEditorConfig.ts b/src/vs/editor/common/config/commonEditorConfig.ts index af8e96020e6..ee224f1f6fd 100644 --- a/src/vs/editor/common/config/commonEditorConfig.ts +++ b/src/vs/editor/common/config/commonEditorConfig.ts @@ -523,7 +523,7 @@ const editorConfiguration: IConfigurationNode = { 'diffEditor.ignoreTrimWhitespace': { type: 'boolean', default: true, - description: nls.localize('ignoreTrimWhitespace', "Controls whether the diff editor shows changes in leading or trailing whitespace as diffs.") + description: nls.localize('ignoreTrimWhitespace', "When enabled, the diff editor ignores changes in leading or trailing whitespace.") }, 'diffEditor.renderIndicators': { type: 'boolean', From 7adff8721e17a4a5d87b9b3f3b35e5b2dc843d13 Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Mon, 24 Feb 2020 12:06:15 +0100 Subject: [PATCH 089/200] node-debug@1.43.1 --- build/builtInExtensions.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/builtInExtensions.json b/build/builtInExtensions.json index 9bd52b930f6..a9a0386d1d1 100644 --- a/build/builtInExtensions.json +++ b/build/builtInExtensions.json @@ -1,7 +1,7 @@ [ { "name": "ms-vscode.node-debug", - "version": "1.43.0", + "version": "1.43.1", "repo": "https://github.com/Microsoft/vscode-node-debug", "metadata": { "id": "b6ded8fb-a0a0-4c1c-acbd-ab2a3bc995a6", From 049b51ba9f693cdcb4abb3720050bb133c805a1e Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Mon, 24 Feb 2020 12:29:58 +0100 Subject: [PATCH 090/200] update DAP --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 8a295591a6e..2883e2c688a 100644 --- a/package.json +++ b/package.json @@ -156,7 +156,7 @@ "vinyl": "^2.0.0", "vinyl-fs": "^3.0.0", "vsce": "1.48.0", - "vscode-debugprotocol": "1.37.0", + "vscode-debugprotocol": "1.39.0-pre.0", "vscode-nls-dev": "^3.3.1", "webpack": "^4.16.5", "webpack-cli": "^3.3.8", diff --git a/yarn.lock b/yarn.lock index 4ae02c9d3ed..9050b677d92 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9638,10 +9638,10 @@ vsce@1.48.0: yauzl "^2.3.1" yazl "^2.2.2" -vscode-debugprotocol@1.37.0: - version "1.37.0" - resolved "https://registry.yarnpkg.com/vscode-debugprotocol/-/vscode-debugprotocol-1.37.0.tgz#e8c4694a078d18ea1a639553a7a241b35c1e6f6d" - integrity sha512-ppZLEBbFRVNsK0YpfgFi/x2CDyihx0F+UpdKmgeJcvi05UgSXYdO0n9sDVYwoGvvYQPvlpDQeWuY0nloOC4mPA== +vscode-debugprotocol@1.39.0-pre.0: + version "1.39.0-pre.0" + resolved "https://registry.yarnpkg.com/vscode-debugprotocol/-/vscode-debugprotocol-1.39.0-pre.0.tgz#67843631a3c53f2d5282f75ab5996e1408c3958c" + integrity sha512-VpoD8m0gOo2Ag5dEpNT9sAI6BBxIyCxEk2dhGIBegxnlOuiB1SVxMgo1tmsvNYcRCpf9eng27kZ6d6FGoPpIAg== vscode-minimist@^1.2.2: version "1.2.2" From f6bd0bac9b1ed4d207772ae99ba72a1755beb4c6 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 24 Feb 2020 07:31:17 -0800 Subject: [PATCH 091/200] US spelling, grammar --- src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index c0edf981803..3af86359029 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -490,7 +490,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo if (!this.storageService.getBoolean('sync.donotAskPreviewConfirmation', StorageScope.GLOBAL, false)) { const result = await this.dialogService.show( Severity.Info, - localize('sync preview message', "Synchronising your preferences is a preview feature and please read the documentation before turnin on."), + localize('sync preview message', "Synchronizing your preferences is a preview feature, please read the documentation before turning it on."), [ localize('open doc', "Open Documentation"), localize('confirm', "Continue"), From cae183ef31b68c115875f4db169209783f1535a3 Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Mon, 24 Feb 2020 09:43:26 -0800 Subject: [PATCH 092/200] Fix #88863 --- .../contrib/extensions/browser/media/extensionsViewlet.css | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/extensions/browser/media/extensionsViewlet.css b/src/vs/workbench/contrib/extensions/browser/media/extensionsViewlet.css index 8e62f3a97be..f61faa096cb 100644 --- a/src/vs/workbench/contrib/extensions/browser/media/extensionsViewlet.css +++ b/src/vs/workbench/contrib/extensions/browser/media/extensionsViewlet.css @@ -154,6 +154,7 @@ .extensions-viewlet > .extensions .monaco-list-row > .extension > .details > .header-container > .header .extension-remote-badge > .codicon { font-size: 12px; + color: currentColor; } .extensions-viewlet.narrow > .extensions .extension > .icon-container, From 278004b67b0800e785a626f885f9e3ee6b3eb9fe Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Mon, 24 Feb 2020 10:16:54 -0800 Subject: [PATCH 093/200] Fix #91299. --- .../contrib/search/browser/searchView.ts | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 67ce6b13534..c782c6ef98f 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -181,25 +181,26 @@ export class SearchView extends ViewPane { this.container = dom.$('.search-view'); + // globals + this.viewletVisible = Constants.SearchViewVisibleKey.bindTo(this.contextKeyService); + this.firstMatchFocused = Constants.FirstMatchFocusKey.bindTo(this.contextKeyService); + this.fileMatchOrMatchFocused = Constants.FileMatchOrMatchFocusKey.bindTo(this.contextKeyService); + this.fileMatchOrFolderMatchFocus = Constants.FileMatchOrFolderMatchFocusKey.bindTo(this.contextKeyService); + this.fileMatchOrFolderMatchWithResourceFocus = Constants.FileMatchOrFolderMatchWithResourceFocusKey.bindTo(this.contextKeyService); + this.fileMatchFocused = Constants.FileFocusKey.bindTo(this.contextKeyService); + this.folderMatchFocused = Constants.FolderFocusKey.bindTo(this.contextKeyService); + this.hasSearchResultsKey = Constants.HasSearchResults.bindTo(this.contextKeyService); + this.matchFocused = Constants.MatchFocusKey.bindTo(this.contextKeyService); + + // scoped this.contextKeyService = this._register(this.contextKeyService.createScoped(this.container)); - const viewletFocused = Constants.SearchViewFocusedKey.bindTo(this.contextKeyService); - viewletFocused.set(true); - - this.instantiationService = this.instantiationService.createChild( - new ServiceCollection([IContextKeyService, this.contextKeyService])); - - this.viewletVisible = Constants.SearchViewVisibleKey.bindTo(contextKeyService); + Constants.SearchViewFocusedKey.bindTo(this.contextKeyService).set(true); this.inputBoxFocused = Constants.InputBoxFocusedKey.bindTo(this.contextKeyService); this.inputPatternIncludesFocused = Constants.PatternIncludesFocusedKey.bindTo(this.contextKeyService); this.inputPatternExclusionsFocused = Constants.PatternExcludesFocusedKey.bindTo(this.contextKeyService); - this.firstMatchFocused = Constants.FirstMatchFocusKey.bindTo(contextKeyService); - this.fileMatchOrMatchFocused = Constants.FileMatchOrMatchFocusKey.bindTo(contextKeyService); - this.fileMatchOrFolderMatchFocus = Constants.FileMatchOrFolderMatchFocusKey.bindTo(contextKeyService); - this.fileMatchOrFolderMatchWithResourceFocus = Constants.FileMatchOrFolderMatchWithResourceFocusKey.bindTo(contextKeyService); - this.fileMatchFocused = Constants.FileFocusKey.bindTo(contextKeyService); - this.folderMatchFocused = Constants.FolderFocusKey.bindTo(contextKeyService); - this.matchFocused = Constants.MatchFocusKey.bindTo(this.contextKeyService); - this.hasSearchResultsKey = Constants.HasSearchResults.bindTo(this.contextKeyService); + + this.instantiationService = this.instantiationService.createChild( + new ServiceCollection([IContextKeyService, this.contextKeyService])); this.configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration('search.sortOrder')) { From 3d10b3156e4aab05fb591ecf9a6ce0ece38397a8 Mon Sep 17 00:00:00 2001 From: deepak1556 Date: Mon, 24 Feb 2020 10:26:20 -0800 Subject: [PATCH 094/200] update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2883e2c688a..55e4247dbb9 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.43.0", - "distro": "ce5d74deb336afe4dcce871e6c5b221aed44fb69", + "distro": "3034776cbf7d76660423c2709c847d87a0d5c6c1", "author": { "name": "Microsoft Corporation" }, From 94b4d22c336c7a32e89c9c45f7f11f55ffe2482e Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Mon, 24 Feb 2020 10:45:24 -0800 Subject: [PATCH 095/200] fixes #86309 --- src/vs/base/browser/ui/dialog/dialog.css | 1 - src/vs/base/browser/ui/dialog/dialog.ts | 5 +++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/vs/base/browser/ui/dialog/dialog.css b/src/vs/base/browser/ui/dialog/dialog.css index 1249a329acf..876f2168fe3 100644 --- a/src/vs/base/browser/ui/dialog/dialog.css +++ b/src/vs/base/browser/ui/dialog/dialog.css @@ -93,7 +93,6 @@ .monaco-dialog-box .dialog-message-row .dialog-message-container .dialog-message-detail { line-height: 22px; flex: 1; /* let the message always grow */ - opacity: .9; } .monaco-dialog-box .dialog-message-row .dialog-message-container .dialog-message a:focus { diff --git a/src/vs/base/browser/ui/dialog/dialog.ts b/src/vs/base/browser/ui/dialog/dialog.ts index 46190302d98..987bebaf377 100644 --- a/src/vs/base/browser/ui/dialog/dialog.ts +++ b/src/vs/base/browser/ui/dialog/dialog.ts @@ -268,6 +268,11 @@ export class Dialog extends Disposable { this.checkbox.style(style); } } + + if (this.messageDetailElement && fgColor && bgColor) { + const messageDetailColor = Color.fromHex(fgColor).transparent(.9); + this.messageDetailElement.style.color = messageDetailColor.makeOpaque(Color.fromHex(bgColor)).toString(); + } } } From bf0a32e729986fc6841d97718b6f39e6ea4dce2d Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Mon, 24 Feb 2020 11:14:00 -0800 Subject: [PATCH 096/200] Add support for other vscode environments for github auth --- build/azure-pipelines/product-compile.yml | 10 +++++ .../build/postinstall.js | 2 +- .../src/common/clientRegistrar.ts | 44 ++++++++++++++++--- .../github-authentication/src/githubServer.ts | 2 +- 4 files changed, 51 insertions(+), 7 deletions(-) diff --git a/build/azure-pipelines/product-compile.yml b/build/azure-pipelines/product-compile.yml index 58611074190..48916189326 100644 --- a/build/azure-pipelines/product-compile.yml +++ b/build/azure-pipelines/product-compile.yml @@ -82,6 +82,16 @@ steps: OSS_GITHUB_SECRET: $(oss-github-client-secret) INSIDERS_GITHUB_ID: "31f02627809389d9f111" INSIDERS_GITHUB_SECRET: $(insiders-github-client-secret) + STABLE_GITHUB_ID: "baa8a44b5e861d918709" + STABLE_GITHUB_SECRET: $(stable-github-client-secret) + EXPLORATION_GITHUB_ID: "94e8376d3a90429aeaea" + EXPLORATION_GITHUB_SECRET: $(exploration-github-client-secret) + VSO_GITHUB_ID: "3d4be8f37a0325b5817d" + VSO_GITHUB_SECRET: $(vso-github-client-secret) + VSO_PPE_GITHUB_ID: "eabf35024dc2e891a492" + VSO_PPE_GITHUB_SECRET: $(vso-ppe-github-client-secret) + VSO_DEV_GITHUB_ID: "84383ebd8a7c5f5efc5c" + VSO_DEV_GITHUB_SECRET: $(vso-dev-github-client-secret) # Mixin must run before optimize, because the CSS loader will # inline small SVGs diff --git a/extensions/github-authentication/build/postinstall.js b/extensions/github-authentication/build/postinstall.js index 23228f45e8a..e12fd05f191 100644 --- a/extensions/github-authentication/build/postinstall.js +++ b/extensions/github-authentication/build/postinstall.js @@ -6,7 +6,7 @@ const fs = require('fs'); const path = require('path'); -const schemes = ['OSS', 'INSIDERS']; +const schemes = ['OSS', 'INSIDERS', 'STABLE', 'EXPLORATION', 'VSO', 'VSO_PPE', 'VSO_DEV']; function main() { let content = {}; diff --git a/extensions/github-authentication/src/common/clientRegistrar.ts b/extensions/github-authentication/src/common/clientRegistrar.ts index b4de0b8138a..c16c7221ff2 100644 --- a/extensions/github-authentication/src/common/clientRegistrar.ts +++ b/extensions/github-authentication/src/common/clientRegistrar.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { Uri } from 'vscode'; + export interface ClientDetails { id?: string; secret?: string; @@ -11,6 +13,12 @@ export interface ClientDetails { export interface ClientConfig { OSS: ClientDetails; INSIDERS: ClientDetails; + STABLE: ClientDetails; + EXPLORATION: ClientDetails; + + VSO: ClientDetails; + VSO_PPE: ClientDetails; + VSO_DEV: ClientDetails; } export class Registrar { @@ -22,13 +30,18 @@ export class Registrar { } catch (e) { this._config = { OSS: {}, - INSIDERS: {} + INSIDERS: {}, + STABLE: {}, + EXPLORATION: {}, + VSO: {}, + VSO_PPE: {}, + VSO_DEV: {} }; } } - getClientDetails(product: string): ClientDetails { + getClientDetails(callbackUri: Uri): ClientDetails { let details: ClientDetails | undefined; - switch (product) { + switch (callbackUri.scheme) { case 'code-oss': details = this._config.OSS; break; @@ -37,12 +50,33 @@ export class Registrar { details = this._config.INSIDERS; break; + case 'vscode': + details = this._config.STABLE; + break; + + case 'vscode-exploration': + details = this._config.EXPLORATION; + break; + + case 'https': + switch (callbackUri.authority) { + case 'online.visualstudio.com': + details = this._config.VSO; + break; + case 'online-ppe.core.vsengsaas.visualstudio.com': + details = this._config.VSO_PPE; + break; + case 'online.dev.core.vsengsaas.visualstudio.com': + details = this._config.VSO_DEV; + break; + } + default: - throw new Error(`Unrecognized product ${product}`); + throw new Error(`Unrecognized callback ${callbackUri}`); } if (!details.id || !details.secret) { - throw new Error(`No client configuration available for ${product}`); + throw new Error(`No client configuration available for ${callbackUri}`); } return details; diff --git a/extensions/github-authentication/src/githubServer.ts b/extensions/github-authentication/src/githubServer.ts index 6c84738261d..336a27b2392 100644 --- a/extensions/github-authentication/src/githubServer.ts +++ b/extensions/github-authentication/src/githubServer.ts @@ -71,7 +71,7 @@ export class GitHubServer { Logger.info('Logging in...'); const state = uuid(); const callbackUri = await vscode.env.asExternalUri(vscode.Uri.parse(`${vscode.env.uriScheme}://vscode.github-authentication/did-authenticate`)); - const clientDetails = ClientRegistrar.getClientDetails(callbackUri.scheme); + const clientDetails = ClientRegistrar.getClientDetails(callbackUri); const uri = vscode.Uri.parse(`https://github.com/login/oauth/authorize?redirect_uri=${encodeURIComponent(callbackUri.toString())}&scope=${scopes}&state=${state}&client_id=${clientDetails.id}`); vscode.env.openExternal(uri); From e8237e723186b06f8d14596afc8ee3372f250830 Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Mon, 24 Feb 2020 11:19:07 -0800 Subject: [PATCH 097/200] Update auth page branding --- extensions/vscode-account/media/auth.css | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/extensions/vscode-account/media/auth.css b/extensions/vscode-account/media/auth.css index e87a6372763..45c42c75ad5 100644 --- a/extensions/vscode-account/media/auth.css +++ b/extensions/vscode-account/media/auth.css @@ -16,14 +16,14 @@ body { flex-direction: column; color: white; font-family: "Segoe UI","Helvetica Neue","Helvetica",Arial,sans-serif; - background-color: #373277; + background-color: #2C2C32; } .branding { - background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PGRlZnM+PHN0eWxlPi5pY29uLWNhbnZhcy10cmFuc3BhcmVudHtmaWxsOiNmNmY2ZjY7b3BhY2l0eTowO30uaWNvbi13aGl0ZXtmaWxsOiNmZmY7fTwvc3R5bGU+PC9kZWZzPjx0aXRsZT5CcmFuZFZpc3VhbFN0dWRpb0NvZGUyMDE3UlRXXzI0eF93aGl0ZV8yNHg8L3RpdGxlPjxwYXRoIGNsYXNzPSJpY29uLWNhbnZhcy10cmFuc3BhcmVudCIgZD0iTTI0LDBWMjRIMFYwWiIvPjxwYXRoIGNsYXNzPSJpY29uLXdoaXRlIiBkPSJNMjQsMi41VjIxLjVMMTgsMjQsMCwxOC41di0uNTYxbDE4LDEuNTQ1VjBaTTEsMTMuMTExLDQuMzg1LDEwLDEsNi44ODlsMS40MTgtLjgyN0w1Ljg1Myw4LjY1LDEyLDNsMywxLjQ1NlYxNS41NDRMMTIsMTcsNS44NTMsMTEuMzUsMi40MTksMTMuOTM5Wk03LjY0NCwxMCwxMiwxMy4yODNWNi43MTdaIi8+PC9zdmc+"); + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJYAAACWCAYAAAA8AXHiAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAhGVYSWZNTQAqAAAACAAFARIAAwAAAAEAAQAAARoABQAAAAEAAABKARsABQAAAAEAAABSASgAAwAAAAEAAgAAh2kABAAAAAEAAABaAAAAAAAAAEgAAAABAAAASAAAAAEAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAlqADAAQAAAABAAAAlgAAAADkcSUjAAAACXBIWXMAAAsTAAALEwEAmpwYAAABWWlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS40LjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyI+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YXRpb24+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgpMwidZAAAxaElEQVR4Ae19CbgdRbXu6j2cecoECTIkICCGzAg+7qeQ9544QFQgiXpVEJTEe59ALsbMwE5AMZCQELgKeSoqDlyiQogCSUAC6FNCQhIwQMALCbNMGc68p37/v7prnz47++yzzzl76OCu851d3dXV1VXVf69atWqtVZaUg397IPJwSCKT46zgx16wh+19XSYnO6PjE7H4MDuZ7BSxXhQJ/On5KTWbTSMm3bYlvHXmJNxj2SatFLFVioeWn5lDDwAgMvOU2NG/3DMo2h691rLtr4bqBtVX1NVJRUOVBIIiyYSI3RkXO9q5NZmU5S98tu7XpmQF2AwAzCoNwMrAMm/CT3HkrgqJTI/KwodOD4ZDa5MVdUPtfe+KxONKiSob6+SIE46XqsYGK5FIBoLVtQEJiSRaWp+xktaNuzpqfirTLcBOZNJtdnjrDCk6wMrA8hOgWJcZoFSrT4nJogfPESu4Dv8AVLTTEqsC9MkSDHB2NIYfkZGnjJOqpiZQrkTCAmWyKqtDgXBAEs0tLyZtWRl7953/u/uiUR0sVgH2xuKERCJJnhc6lIFV6B7OuXyAJrIpqDzVlQ99TUKVt0scAEomQaXskBAONlEFrgpvLdkZk6r6Wjn6IxMx2qVeY0IsSVrhqnCgMiiJA62v4a5Vkmz/4a7PDWtmVXSIfGMSAGYVFGCpGvGh5VCiHohEAiJXi77sKx+eLZU1N0hHCyuTAJCCDqCAKIUCYxv4AcY6YnL0qeOkZuhQSeoo6b5OG+AC4ZJQRThQFQbAWt7Bvf8ZSNg3P3deA8ZUF2AzADCrMAArA4u9XMoQsQMp6nHlH5dKdcMcad+fJGVCCKRAxfMU1XKB1dYpI8acKI0jj5FkDNSti3LpzSgjCQQmrFA4HKiuIMAO2EnrtkBb+8pdXxr2OjOd+bAd2vQ2cro8mXPjwH/LwBp4H/a/hLvuCsr06cpky6KHfiK1jRdJ235M80ClABMtmMMfKJQDNB4j1VCstg4ZcTKANWpkZmCxAJZiABYIhgO1VRwi21HGj63OxI27pje9xGz5BlgZWOzVUgSPjEqufOheqWmcIq37YwBCWKsDDGlQULnAUh6L4CLFAh/fCmCNOSE7sNxi3IilxiUQCAdrayR+oCWBcn4q0eSy56c2PMc80+6yg2t4MEAKVgYWO7HYwcz8Ig9XSTz5kNQ0nC7tB6KoBmZ+bnAZdaVUhIMHVDxWQtQCYI3thWKZ8rrHLICiCwCslmIKFGn/GoPm9S+cW7ddswJgZw4Ta9NkC/n6HsrA6nufDewOV/Ap8x8cggHvMUztTpLO5igkCV2g4hMIJI3dY56mqBdwgXOlWP0DlhbNkhVgtoQDELza7Z1iJ2J3J2P2DX8/r+Evmsm2A2dukkBfAYbZSDkUrQco+IQ0XeY9eKwEraekovYkzP4OBhUrlGLEXYB1q6QnzXPYLUtuJ5SLcei1k62tMYDKDtTWnRusrf5/J9zbcv9xa5snc9aooLJti3xYbsVy1lEOxekBI01f8NBECVnbJVx1hHS2YSrnGf4y1SQTwAyYTJzpvr6lAWA2AWYlW1vidrQjGait/VS4pvaPx9/bvOn43+4/GwCzDcAoC0P+rKNd1ot9q1s5d489YIa/BQ9+Ap/yAxKsgJSJ8gGXUc94I1BD4Og/j91zDodYGFTmvf88VsYnpiWSt7ICNXWcoQoo2uNJ217298/X/8bky7ZcVKZYppcKE0OaDg0FDn9XPfwFCYc3YEYGUEUpTQeFUNTgyZniDBUyfJdecu9hVJjAYS+YbGuJ8z9QXXNaqL5uzQlrm3d88O79X+Ujt860MIu17ExDZJliFealkMJYMn1NQNZATnXlQ9+ScPXNEm3H02zKrZQK9PpoxQ5+PBJ3pVwqbkBJpFhjPiSNx47sWY7V60Nyy4BaQDQhdqCyJgRWX5LNLbuA8xUfaKz7sTtEkkhhMuBoU5QpVm792rdclKbzkyWoFj64WCrrAKo2SNMxhukSjb4CvoZe/pGBebyfP88ZTNx14KQX6BdV4McQSna2JZItLXGrsurEUEPdra8daPnv43934CIy+Qoqth2hDKx8v4hpkKbrAi++3AUbf4AlmqukvTmuiytgWfr3OBdg/bs533cpwMDgJ6BFEQtUVB4dGlz/kxPuab5TH8S2A1z9bGi+6/o+KY/8FKkUw6KH/ktqm/4Ngk8y6UHltvVCH39IrQx1SsXmoI9l5Tc72xS2Y50AWGtnaEjdF46/p3mtPuJqqmOUQ356YMZtYai8xITrfzsGb4Dg8392W6IZ8FOygImXslwe8KOzF0Dti2D8vZbO0KC6z37wdwfm/d2yvu8dvbPfXr7acw8YcULkrw0Sa34EoBovHZCmq3Jez7dlvwKkGMCoqIHn+E8tQtse5n1UwZn37HXVq0nOeO14sj0Ws44tD4U59FjWLEaaPue+IyXWskMqa11QUfCZQgaK6OtxhqeaIkpInjLUyiRBjJKMhRrrqkOh5IXlodB0S3/ilDR9/cngyx+RcOVg6WjtXZqe67OUUqVnJroQsgu+nTzF/oXUlsIUTIk/WaZY/e18A6r5D34MXbkV0vTBEoVKZ1Zpen8flv2+FCHLnq0YV4N2Zwcwb48pU6z+dDfVXiKnRKGh8HksJt+tIp44penZlmj68yBzD6DjRQ8pmQbwWR4u2aSau0oQg2JRT9EaUqZYfep9vEaKFGhFs2DDN6Si8m594Yk4RQyF+0gVMRlgwyTzb9qRIZu51C02+Uzc7WLaSS55eIvJh1WHwnVGWt0O+VM1eFgMg4dIXBY8OF+qar8HlRd0JciHRRutEoXUy/Q836R5kjIemnwmzpjJTcwlD7O6+crAytaZ5ppK013B5/wNy6W6/goIPkmlMBD1V5puCu8h5gs66GV6E7zHPZRRsmSrgOS7ZI3K84O9Bg/zN/wcuulfVYMHZ+3Mw+Hk+bmmuHT8pPgrZki/aG4qfVymWNneAfmp6Y5TDpm34T6s+306v9L0bA/ntSzAMZdK45qht4qXKVaPPeTM/GISWVcjnZV/xPB3mho8WL1ofPZY4AAuGHmWAZMpKv3cpPsgLlOsTC/BLNHMXn+YRAOPglE/ERoK3a1oMt1XrDQdDsne4YEEl/kv1vOzPcetUxlY6Z3kLNFEZdGG42Gk/hh00w9Xg4fedNPTy8n7OdBjgKRl+5RcudUqy7G8ADDS9DkPnApQbYNjjsOlsz1/SzTeZ/V2bKiQiZk/HUvp572VWcTrfaNYVLddvTUkgyYl5Rk2c1NARtRbUkIHX3nrKyNNn/fgp6BCcL8E0DUxuHShNL3ULzBFqVCR1KwwlZi3LshnQbkDizMkS61i2dkmqDa2zMQpr7tuDc3FQyOmNH0TtD4hTZ97/1ckFLxDEniBCbpvUZMofzQjHdzp5/6oZaoWuQHLMLOrXmgQO34hvugzUMJgfMnvIH5I9rXfgRfTJpqv9P4vU63r7cAYPESmx2X+A7NgQLoCtn68q7BLNL3VK3U9DT1pp6lsPjzoBVj8mqFWSzOfW549B7ZwP4W67RB1fgm7ACh24T84DZTsKlm2/WKZOX499Z1l512OdYoPG5yqkho8qBAoIXMf+K5UNiyAch4axbdXwiWaVAU9BwZQqdHPJCAPD82/55aSHbp17BlY/JrXQLVmOoa/VTtnSqjqVuFI2LI3itkJmX6nCLrICVceIRVVD8iNTy2RK6yrtVGGypWshVke7Bg8kCqJzF2/Gk45LoGMynEf5HGPl6WEIl/yAIlPTjstcmWyP86tW+ZZoWvCo65sbvrblVLVcCuGCFtiUb6MCjQshH/oOquAtQJMbgKzp6TUD7lKVjy9Ua57bJAaaUZ2dnd0kb1KxblKXtAYPMxd/zss0VwibTB4sEGlaF7sl8AXZP61TjhxX1qqirYO2alTPx0c3JHetbGbdq6SmqZLpXUfAEUnAo7NWA8NYLNjkFBXQJj4NpxkTpMrJj6ixgXPTLNTXut6uLkoyTR4WD0zpg5kB7+3EbrpZ6g0veQyqh5abyTuRBSP1dsM8iYwnMBTn91ph4afdKw0jDrWtqForq+oh6KKmWyDoegOLO/MbuXOO6V20BekdS9ngRwyu+ftsaZ2DPIfmI8je7Rtvlwx9vuatdRDo/FJNe/3gzDZg8FD3RjH4KE/SzTsinTy0WOH9OFCWrl8hD4GPwZYSSj/wmzBqm2y7Ff23H7cZ856Lzxk6LfjLc30nFw69R1PKwmsrqGQL57iAg6DK3c+CCadoOIyhnoh8dzXy6EVhvvohMQ7bQyN18mNf1sHUURdSYdGCj6pnDd349H4RmDwUD0AULH5hQBVT+WaZwF0YEYkGA5KKGRZrfsWyS++fHGgtvofYEpArArjpLaXl93jZYd5N9SE4oTkM5vAd0yQln1k0vvLI/HLsZXa1TbCX7n1nNyw43z5zujHBZ7iVLhaYHfQqRYbafrcB8ZhDHkEk5BG6YD7oP63LVV04Q8AKhIxGuZjsAOVBZtx4C1JJKYmb/78Y/r8aKLKrsYR1A0NBAtfryxPcIkuPPbajjeUVTuOhBcUfM11E6Aa4iy4sqb9/4fPJVCvNrhADFd+QCqr/irLd8zSCYFjht3zjDRLvft0SakwdniYd/+ZuG+LhMKNKk03fj77VFiJMiuDjnGwuoG860a4ED1RAKojbttSwxrB7zYGHuc1MS55AF4IGUjLIUO46RksuNp/lYpqmC/Bw1x+mVln1mhB07Ju6AoMjf8iL738FTy3UzhrjIzm8/IfyKjTfdCc+6bC1eYa/Z7jMcfggS33BsPamDjTtfQ0c55eFtO95XiPzT0m5jUGU0Z6XhvyQ3g6ViXVtr2LZeWUiOZH2044YVL0dT3x5w8oFhys2rIeqiEwX2rPN6hMqx0n+G37YuDdpsqoY56V67eNV1DphCHSxeuZO/obU/7GMjn7m/PANyFNX6MC3XiCzvgpJjk4mDQTe3P0lMb0TNd4rzfde+wt1+TzXk8d66gWxUYC8P4f2yvtLWfJjQAV9e7JA7NtPg8haTzsPzD7GyUt72GbMqksYH35PWJoxDBbUTUK85ptsvypb8q3x96mz/TOSPtbCTV4wGvlJGTu+kWwSr7GFwYPfWoP51RoA4e+1n2PSqdMlZs/97aKSCKngOJGUvDrXiyTDQnsfqX4Z7bOCqc6DsHwNRcnYGjsgJQbHVE3+FZZ/vQdcC7u7CGDnar6XQUtI+L4aJpz/01gdK8BTwL5Gx9Egwd2vJ//teUY+kIBsCRBgGqpLPvMGXLzZxxQcVbrNCCti5wFBCfRL+2juMGW0QoseAzRd8D30O2fzUlP6+nc5DWxN583jUMSCm2Dw/y6QV+R007aKUu3nqRrkpxMcDjrS6BQ10jT59z/K6luvAyg4syPQ6wLqr4UWIq8Nih5NYe+Vuk48FmAap72Az8YB1Q9VwrdfNA3w9wm3RybOD2d5yakX2N6+nWTZtLNPeYc17lXC10yp93NcxM8uU1Sj7HJa2JvRm+aHjueelsh1qiqOREzx7/Jsh0X6mSC7gY5NOYSyKRz2xC24zv3rccQ8iXsRYOv2/ECnEsRpc2jQ19ch77Ols0ST5woy6as06GPn5f5YLJV0tu1Jp83zRybuKc83uvm2Bt7jzOVwTTmwT9dGu6ERS+TvDSV58UKFViH5EaNAfB6PwXftVofTD6J4oJsQaXpYGRnr6+V79y/GUs0ZykPR9/lbgN9G7NdNjeoDGLoqw2h3jfJDWefJiumvJbaszBX0VTf6Hu2Hs3bNQwT1m+kgiIRiB1KFSxYZFNLghoGdYMvkRuf3iHXbzlOxQUEV6ahcZorTZ/1wAjoiO3AyzkFwx+c8XORHKjiv5+DLRz6sA8h9nZuOzBdbvjMLK1uLkOfn9vl1g3A2r8CM8I9mEFV4qU4MiXvOzHH3th7zIK85+nH5rynfCYdW2ugnJBK/Ctrx0qw6hm5fvsXFFwcGimxN4HS9DUQfH573YfwPTwFg4fjpJM7PKjmhcnlV3Bx1ucOfe07xI6dJDees0Y4pNPFRy5DX1cLfXsUkCtOxxZjyU/Br9M+fEFUiekClwFFeszmpKeZc+8102xeM9dNbPJ5z538FbrkEghWQOZ1pyx7apUmczcqUi/VTQeoZq37H3g9T8J90FDpgMGDoVR8kLdMf1EuZ8ivqgthdWO1LPv0eFn22ZecoY+yKX+syjivYWC/kLxjFjYba3k37RwHbYQ/gU85qgDS977WMgyhZhL1SGJovFSWPf1ReIs7T2aOe1ULunTt2Vgp+L0EQcSiEF1YWDqiSoluDwKGg++HE0vDexhwmfO+1iYv+S0ubVHUksTQ9zVZfvYdWqzK71SU0L+nOFIv517vB9W/0gZ+l9vHkOSCtyIluHz0yxK1x0GV5GmdoeCVDfwpAyqBogJ82Zg1Vtd/RIKBF8B7nSUX/OJf4drz90qVYjHOBimecKgUY90ShOfuMaJU8B6nEgt+gFePpZlqLCBH25/DBzNaQUVAkXfMhwEK21Wath3ceW5d+PIkxSTPH7tXmk+eiOn6I1Dwo2ZDqcHF2nHxNY79jaskWLleRh33S2irQqVQfVIFHSDh3XHWrpQJLdPGeWMee/9ZbFECJkSY7VbVYcWh+eey7AmAaspz+iETUO4uDgOuSUkpcebad8mKuGCrZFlnh2fKsr/9FtP/86TNVfSDWkbmIoqQylljPEaib0lTExaGwra8/lpQosB9GE1QIOEqPxPW0tSUMa9pwEnqnIkmk3s5/1EUCo8V0E0TiBJmyvJzVusj2Mfs6/d5cCiWaSS/IkqxGWaffD70qWBoMIgyIcq4Uq9Irxfzx3myA5t43JKamoCMHClSXw+aSlkoMpihzwyFhKFJ1/vdPE5ZXdfy3Q5oTqFI6E5BRTvW8SK2lx+voNKPNgLWA338TxC6A4sNphRbjSkw/s8+eaa07fsurFhI2dhhfF2lDWTQoaggQVTpA0eJHHY4XiPOwetrDVNgwrnqiBtApRDl5DOtYP78BQ5vAlCFsSxzl7zcClHClB2iyoYAVARrmf8koWso9DZYFfHwdX0Y9oHTRy/CrOxt8AkrIZJALjpcpm51Xl+I9+m9H3MU05UQxEOHiVRXibwO7SRSrwoOjahbgJkQWE0e8oVztmji9Fkj0wcWMPRVVKiKTtuBy+XGsx0xiVKqyYXlVdlG/vshuN2YGVisoH5dnLWoOOImyJPeAc/wC3QcGGa4xnUMLErfFFaltk5k5CiRN94QaW4GuCBrVPGDCyRvLfkC2Hj+6zEO9Nh9MzzOOWhm3hhXteGOlldBTc/HssxmxzrpGbsYQ182omua21OT3FZrFzBPT+e85u0ak8+km3OVxOGkZ2DxDn7iEXQaxREzx/4SDD1M6m3sEAqO2ewQakrU/O6Pvije7p5785hrvGSu89jk8V7PlMdcN/eS0pih8UgMje++LfI2qoklOLXUZkt1wOcDkFfv1x8cI874Vkw+VgCB2RlMHXmsRejegwGIZ8JYjlon1rtfkhUXtOrQNx1C3CIFVstbtfTHZrtm8qbnST9nvkxp6enMw/+DeSzmTA86Y4Qa8eyT16NHT8UMrRVGCWTqM89uTA3MU7zlmWtMM9fT09Lzm3zmHhOn0vGWafJPkAw9TOQoAIygUb8eyJxi6HE9xXchnflT/ywUQctkunPaleY552HStZgJhi3M+uZBNgVVF4CKSzOR4oGKVTG457FfQm7AYm2pm04d9dknPwFqMB4znjexvkjtg6J9mVk7jUBiIPWqw2zxmFGC2SNqh+oRPBnB5QJIQcRjPXDKMYBzzry/zARVH7Q9EXsL1uEfx1rfUkdZ8dBQG/Y2plDHuQOLNTDgumL03/Gmxkln6y61fPYLuFhHMzSGgfkjj3aYezL1pGgMBjA8V+rlUjoDKr3uZE3l7zoFakHLqDbc0bIRVk0nyopzHtO1Pi4eF8ukras+vj3qG7DYDIKLPNd3xr0lweYJ4C0ehx2iX6T0TkcTXAZIFEccBYBxFqgyL2QheMyQmKJkSNc0FpGJenHoCwWx3hfAWl8EVOosWXnuPh36etPwZJH/ZKEX5r2H3lCeC7PFKyzunv1Ruf6pP0BK/xnIvEAaqFvFt1jqgCpw0OLQWN8gMhLKjG+8JtIK/1ecNbKGJGLpNeU9BCbTnWOagsZgMQOFRGiAWPHpECVsVIuZnR+Gh8PpmflM3F6UwDawnvz3S0Bd+k6xTOW5eG10pOaMPRuLxXCuTyl90hkuTL6Sx0AIlxXDIKpHjRQZMsShXPTa56VcqSHQTdcXBbJnQ2BWSWPR1kelDUPfcoBKVXci9vtFd6oQr6j/wGJtqCNFjUeGOWMuxBLQcixekwriO/KRLwHv0Hj4CPBeRzqgov0qAaT8Fqps+C4m2kkYY0BmEa4KghIvlZXnnCG3nveWZ+hT6LHp5XBwD/RvKPSWo0wrZkMMc6zZGBbfgT+t66DRyY6nkagDPM1Qwh+Ci4FDYwMWsisprcfQ2IGhkYw+KRazONloLIq1vvZWiFa+BJP2dchgybQ1MBYt8dCnjfD/z8AolmkfZ0NXA0JcvpgDt0Wdzd/Al45XFODSD4dG/wQCjOCiAcnRI0WaBrtMPb8D12KmAgvIHa2bpS3+IQUVhz6G94nasLalwD8Dp1imgo5ukSulH/NjuQGUywrdA78JQch7yGLmB8TmeQONCS6CbPgRWDSGu5Z/vAF+CkNfTS1mfXtXya1TL9dHqCXQIaLmwm/DBFJe7znT09PMuYl7upfXGdLLc1K7/7p58/+yOWNUccTYtVhS+QgsaPaBcPE5BJd/gndoHDTYlpHHBeB36oC89drZCirObA8VixkltmldmwkE6Wnm3MSmiEzn6Wkmb3rMfPjPP7D4oK3u0/78Z5FX9jhMsVq551o79/5iRDo0gomvAs818liR8ad1PXVa12H5qA89AKqVf2BxrYwCw5lrPieHD31C2jub5OUXk2CCAw7L5UNwcUxMQKHLCjTAKvsPct2Om5Bkq24aqW859LkH8ggsDB1k3uli59/v/jqMYO9Rvj2AmWFHR0BefglMMhza0LKGMzD/hQDUgRyj2fpBl8l12zdL5K9HpuwBVHTvv0r7tUb5AZb6bVoMYEFL8lv3zINu0o/AsBM9CTCMWAbBHCEOATXB1Y7pvU4WcZk5/PXfZTRLy6Cq6ufke09+VsHFehq1bb++TR/Va+CzQjK4EXe/5MvWLoerSeyXDPdBAVCwpOvFlxQqRHBhJrZnt7M4TOU8MzPzUYe4VaG4gS6FatGetfK97ddjaJyLa47RrB+NIQh8PwTOClGXgVEsgsrIdi5f+zOs+l8Bwahj7UuzJz5E//mDgDVcPX9lt8iB/Y7euj+HRdbWMZqNtiakfvAc8F2PSmTHYV1Do7aE+UobONf2C6jYE6gL33b/gUV+KgWqdX+AT6oLoOnARWhnhwcDKM669JhPRSCPxf/XXhHZS01PHPuqZ7SW5of9E3CNZj8mVbJLrtl+loIr4oojTM4Sxn7CFbuB9enfUKhakpNj8h93VUuyGvsl130UVilw323R94MTCCg9YYygp7hI1RQaOpB6vQEDCA6H1PrkOp03v97kgx/HnpL6V1CbqWgCv7hevrttsSy0Ilq7EgtQ2Wump33QW6kq9B1YpiMvvW8YhJ+PqdO0Ds9+yYoj/Ojam56kHqYHmgRwMaZFzVtvOuAaNtzBlR/B5bSAGqPYGAFbi9QPuVq+t+NfJJqcJpEJ+4QuLrlDWjmkeqBvQ6HZ4eGydcdLAO6DKqpPhI4SVZOp6OeARYHhAsekKZjwo8OiJ+Z16kbRAOLNV3nm5NElO5RhyvJLTHea3MiJ/iRq6v+3VFjPy7VPflxBxRmj2mNqK/7pf3IHltnh4fJ7P4IZ3zZoUg6Hdxp+pQ6oTFfqEOieKKBwzDh17AKLwyH/mR/meLJ/L7QN9jhDIvdB9CtTr0qMNneIiKEPhmH7lEfku9vnOoa+WIw3C9amP4oR+/AbzA1Y7Cxanlz++08CCI9DRbcWPgnIqGeWSnvBpR0L8JiYh/qPH+bTf6RREa8FNoFcAqKtoJ/B5bQFewZhO7141IaD3u9DJLFOZu+o1VUHP26np3Uu3k8vwDLSdCzRzFr7ZciiHgAQsPwBuyobfEW2LyVFokxjCCIee8BkQMV02v8RXNSPenk3zOYxwhpBKm/LFkw9TB6eDySY+03MsjIeu3I6qmTXYM+gwfYuWbLlNLULoHZtUYdGVtD8pzc+vfLefOnXvPem5zN5M6V3v9YzsChNp2Ibpemz7p0l4dpfqOeUJFSPnQ0wvTXo4ViR5FxLgYqnBlxpMTuGSnc02SK4OqBST3AZIwfTnvTYeUL3fk3P05dzlsf8DOa+no45a6QzXe4ZFKr6APjOv8o12y4v6p5BWlHvj6m0idMrb/Jma6S5Zu5Nvyc9vft5ZmDpEk3E0em+7N5rIX1eIdFWzGyVq6bgKfdAqmSCF1wpapUJXHgEh8NXXoILoBa/C1JN6+CyqBOUPI7t9AatxKzxLrn0vkp1bPdPODQeDCxdook4QqVZa2/D7GchZDjcL5mwQH7zFfQh5p0KKjc2YPOCy0vF+Koo5+KzXtkNfwz7HHBpAp/r2wALJdSNGyPUNE6DEuGzsuTJcY49JgTKzpYsvq18PivWHVjdpOlrfwvd9RmuM/787Jes4HIB6QUaW6Tgw4+Czb1IqTyZ+NdeFtn3btKR0pMZ85GhxsFvg5V39wyqHgWB6na5ZutMZSnoaIV9XKrQn2+yP/egfV2NNNJ0xjXDN0ql2S8Z0vR8BgLHK0pgxV0cOY/xnDAflU8tGIu+9aYzWRgCt0UJOF9znMH1bVjOZzt6L8vZM4h9zD2Drt3+LxIf97WUz9d8LWSbBYtcAZBrPm/7+noP8jsUS5XzoEc16+4mqR6+RSrqzkjtl8xCc/lnRUw+7zHTTEgde8Bz0LCIzLys/5oPFjPwkxCQDnn+2bPgYO1fwSTjOrh6G4Ya5pn+jDE0grpS5lXb9FUJP7VTIls+5Kw1gnL5wrDXvJz8xpj1oYFUzrt83dGSCMAZf81Y8FQHO+Pv7bkp0CCj95j3mZdujhl7yZSCCyDSYZCXcBzACwnAKLYGxqLxts2QPxwvd39zo8w/+ddQGDxHqR73Rwa3rMX59kc3RjBD44cw690JvusCHRqppVrKobGAfeaIEy5feyImfNg2pOooSNO7lmgK+GAt2lArnihxYkyAYeijAUYl9pjpaF4lt00/TX70lVfVWJSqwgvH/AE5T8eQ2AEzMw7n7jpdOqJxJWPIlC/XtPQCM92XMQ/ccbt7BtU1/QwiidWai+Kc96H6c0C+xh1W7fWQvzTBNVHxQGX6vhu4lGphjxnsLCrJKDZvmp4yw1KLGVBW9RsBd0oLxv4FIJyAOr+NZRXkx7ZsGviie/tnxvQ8uablcl9PecBv2VDj0D2DBl0i127bLpEnjtU26VIQBNLvkxCQ+v1XQJfqGHxNUEjHGthBHZ7eSQU4d7qTbChcLtLbMPaYSdgnyQ/Py7zHjHGntGDscxhaMHS3/re6asy0XYvBi/eFsQnpwdsscy0930DymHsdvha7fWEhu7J2HDZAfxYAm+bsSYih0fjDMHXoT2yeZervPc+WxmeZ6+bYe6+pS7Y0Nw+1PKfiq+dp1wzRFFC8OA5+Co72MfS1H1gtt5w7Xn54/otZ95gx7pTmjH5T4vvGw2nHFvVbZcDFupsO8B6bjjPXTOxtqzfNHDP2BpPONHOcLY+5tysvvdeg3dgzqKr+Llmy/SbNYvYMMvlzibvK7A4M3ptep2xpJq8pL/3ZmdLT09xzzgpH61INfSyYTMWMsYCDWV4Iin5JqDVfAFDN1PbopAJrlNmCDos2BI+TW2Th2FMByg3ujhrZ78tWZlGvweWTs2cQttNrugx81+MQS3xAh0blu3IbGv04ftIqJTOqC93BxtF+pTrafw6mYqNl1bl36CyJ03AytbkE406JM6xF4z8Jge6vAK6wTgDcvWNzKaaEefhxO0NjVd2pON4li7dOUXDx3ZC37CUwmwnmdZrYpDP2ppljE5t86efp96XnY34TzDFjSh//Bv/kvAYd4aIFGFzgj3vMdGCPmSHbR8stU59z/E4BUI4fiNwr43WntHDcl8G/rJJqgIu+rfwtpfe2EbNG1zKouv5eiWxbqv1Au4JeZo0GDObFegvNdC1bPnNvT/eZdJOPsTfNlM09oddgys7ruVEI5hxIsDj0VYKfAwFvb54Jby4XYg3NWeoYiMtFvgCqqZDaLRp/OcB1JZakKOci3YLDD1Ta7/9JWgbBRq6TlkGD5sjiJx+V2X/2n2VQDu8/IC17V2L42A1xA/z64KUXKujQR5eL2F4tFn0RBHI8+KnVOvSpNkWOQ1+2+tGdEgP5s0Xjr8WOW/8GgS9oozqOKCZFzlbL7NccXjfoWAY1fEzqq3fJVds+4YhZImARXF9k3lL89MGwXqhPQH56UQcsts6CuOE93ahR5UF5rimXXshhco+Z9pY1MrTzJLn5vB3gHypSi7PejhrIsUqzAVLKha4afytEF1NVpyug6hLFocoDqT/vdWR7mDU2O5ZBVdUb5KqtEYey4+Pxqj87n9JAn5i/+wkdBGdJZ9WUFyCQxA6rHS/hCyfDxSWd/AwdOuurgHYEFNw7sMfMLZ+frmrOpCrc17lQgcMqeZNF434LmdhkDDHc8zAEgB8iM0b2P3eOhWVQtD0J58FXy5VPbpBZ25oo83r++TfyqxyQ5/fgLOlwEfrm818Vq3U8KNc2LEI74BrYwwBNDH0sK9b5GmZ9p4FKrdJZTr6Gvt7qZ6T0V47ZBHP5SRCr7JMwpfQ+B5f71euXzaGRE522/di0oP4TUpvcJXM3f/z1mUdAhxvB5jDvv+BUiovQpCA3f+WADN1xCuRJD6oEnIx2/4KjdVCFPWY6WtZJdcuJcvO5m3XoUyZbFQn7V3Jf7zJS+oVjnsIEcTx2Z31FKqAtQUGqUmXi32f/rJjWCY1lHeFgCb/caTYGqnsY+OFHAnO3LGJXBMIWWBkeaU498MOPUyVTE4LLyI++dc+vAa4vYvji0EGpfPe85p70mGAM0CoCIR6dJ/953lI99patCUX+4bBICjbvsUFwB/kIpPRj8KL44fhzSCGgGBh3Axn41aQdsGqHWPZbr99+zLgT3gsNGvrtROuBBF5QrzIvLbPQP6jzwWDxOvr41t2rAK5LIWvijIp5s5FdKi9j6KvlUsVbYECnQpTwmA59o6fZUHArPZtJptfwXv8IboT68Bk6xPgRXClg4cALLvZiEjIVKDraiXBo2PB6qR9xmG1z59kMr7PQGMpYPup7MFCcocqRB91y7mUA1VVYLOVyDypuPCBrS1EmYwam47OiFL2zZSPoG4Y+BVWFOg7xA6hYTYKKlJOU66oJZ0rr/rtTS0CHgpReuxs/jmVQCPywI0JxztlC34SDKZapGgWN02H+RaB963ffxHreDzGzwteC3bNtBSTvZZOgkIehjzxkLBqRH5y7WIswWqmmPD/FNIfnFsUMS7bDYKRxBgSqFEVwKOm5T5i/WKELROxl/W7R06RW+g+KJXZnTIYdNVzqh9ONBprjVUEqVj0zPQd1660TIZB7GIaXkAv9+2+mADy3Y2gcgoVTp6E0dCCgOg5g1pe8WH4wbQNkLQHhHjMEpJ8DBY0RvjK8osg27HvduACCYjNcH0zJi90W1Ayd7DyVkbGtzAiswwAsfBeHELCchhnqM+OuRqkIXoD2noEL8LxvvY3GPCTh2B2yYnq7sx3ITH75bo84t/v210uVl2ybBf5wBfhDNAvrpqXeUUN70O1Gwt0w8OnAOhIUa8ShCiwio7dZXW/X/YsuUOVNDlVevA1uBCp+4Qz5HPd1NlyamucCrA53KDykgcXu5Rc+c3VI9g5KymhspE3m//UR2FptBumw+3mV5j0M+KlGHBHZ+ikM7/dj32sMP7EYKBcFqsUP2YCFncu681igWLQcTx8KWUZPzE62awNtLcru6bEDLfrQvJ+m8CpQ3X4q3tzD0PqogXYt5XjFB1cmYPXIvPtvKCw9k+onCBopfWT8ZkjksLzV+aYadqSk9KgsX3gx/k2/pD/LpPs8LgMr/QUZXfprJ2Bh3sK+123P6/JWIVWK0utgzg2ozPkhNL6UgWVemjemAJU8VwT7XjcMHQ8h8eNqqFEIlaKeyB9ngb0FD/ByyN1baXm9fgh9A3ltd26FRWysnVqcHWJW/OR9kNJ/GoLU4vBcBilGzGBil8/iAhkFpEMpIPXhrLBMsbJBzBhqKLAmcjN17HsNXXpHjdu8+mwl9P9apk9eKRR+0p9szvU6HlnK2G1xGVi9vXqvoUZk4oUA140w8KW2B2hGARfWDThYP++x1tckmNjNo9dK/ONWKdN3UeKa+fTxRtecC+pXbZkPcH0PqsPU5GdX5v8DZakMZgg0cZrkXYfC4Yf7bkkn/x3idMf775eAigBEXGFYcgo3U79EQtXQ7cSCKXX6CxIMunoo3KUOPVwtaXIZWH3qfi5Yu4YakQk/go3A51XafUi4U+pTQweWGSpIZWD1pwtVrwtS+iUT1ko8/nFVJaKtZKENNQyFMnF/6l7Ye2wrCIc62GukDKz+drSR0l8z6TG4s5yIPXbew9BIXXqKI7oYbgOCvsbeepl7vWneY/JfDMqH4bhEMRj2hAXjZxCsp8vAcl5J/34NuK4av1OsTpjPte+GQa5jqNG/ErvuUjDhVbmY6bqQ6cgFU6ZLRUyzEdRnbFIeKANroB1vloAiH31VAlEsAbVsd6T0/bZw8tQoJ1Qhvy8m99ysPZxs2d8mVujnZWB5XmO/D7kEREONyEcPyMkTTsEuFX+ERir2boRdZSGDUrVCPqAPZYMFCNbX84Yluy+qe7MMrD70XdasxlCDAtVrJv4vWP+sgadkDIsElzI9uN0gIdfYPNGlXOnFmMsljdFeLNAHmxorE/v33bv7oqalaK4/rWhL2k8DeThFEcaf1ZKJ0+HY4wegXOS5YAuI31zx5OJI8cib9L5UImpojgdS2QHfS0DFrFA4GKxrrEjs3/9fuy8e9DktdbFaQgz4AeUCvD2g5nOuO6VrJv0fDItL4E4JogjVsOUScm7BYMfEB93FC+b/oIuFTEiAcMYx+wsGahvDyVjnq4nmfReDUn1RH6pGKlayPBQW4hUYO0qamV0z8WpYMV0KhUH0tY4Q+ZfSG/AZnBUmTuDTiFsVNcFgbWPIjna8YLfun1H38uvHgVLdnvJN5rbdF9OJQrxbn5RJf1Yw1IDqzZVPfhEbiP5aN1BLUkG9F0MNBQt+UiBxj71rhdCaHno07QoLt1YIgGA7M9BbGC1zM5Bk+4GnwUIt3XNxw69cKixnPmyHNk121Yvcji9TrMIiEEtA6HAqDV4z8U5I6T+hzmyD3GW9Hx5vCDIGAzZzrIn5/eFwhxITVlVdKFBVF7Q7W59IdLSev/viprF7vt74S4JqEtsFlKWDijUpU6z8vo+eS0sZamyBlN7aBEONejXUsGCoYQDjvVvT8GNAZGaEXoqVUvTLH8VS/snCnmvVDQFa/tjR1sdsO3AdwHS/Wz0LgAptnTmJZkGZaq7ZysDyvsxCHxtwLdoxCiPMn2AgewQc2mb2eJMJWAQV0xHzlXZpkA4YWCiNQ5kdCtQ0WuCf4NmscwOmsd/f8/VBD2u3gDKduUmCmahTpm4rAytTrxQyLWW/uGUoKNejMNQ4CTr13cFF8GggenDgpVY89wLr6BHgsQ7DOjidTff5dbI0DnlhAEqSHdjNNh5fiw3Llu75+pC/IB0q2aqoAHcE3XkovZblp881yVJW+VKuPWDcKV36QqU0qJT+dPiN6AIXX7cGHPA4G7COGiF1AFaffDfQsw55KMsCoBok2bqfT7sTz7l+zzcGbdNHc+uVZzZh8jGZwOtzKAOrz12Wpxu8LgkWbr0XUvopEKimGWp4gEUJmAGZl2L1DVgsAYAKhAO19ZJs3o9j+Zkkg8v3XFL/rLaM9frwmbZulj6AppaBNYDOG/CtpApcAmJYuPUnMNS4SNr2gmO2HP/0TDdgMjEBRmDxkjLvOVEs3pWA92hQqDpJtuxrR7k/hl/AFS9f3PQirolAZCBvI9XURxP7/1MGVv/7Lj93upJqLWzh1qWQ0s/pcqdECT6umKGQxwAV05R5j9L8C8A6vMehUAFlBcNhq7oGgNp/wLKTt8bDVatevbDmNT6TIoOtg15MpvyFMTEPoQysPHTigIugTzG5Gowy9OoXPPEdMPTXwykwAUVqxi2KFUwKMgUWKRYYpWhchn/wGGCxAU5BkLWLeQegrATW8cJWVTUB9Q6YqlsqrI4f/P3iEW+zvpNus8Nb3wAVM6sEA25E9wLKwOreHyU8gycf405pwdavwZ3S7ZIAP5+EOyUbUvoUuFxQxeLY76FSjjj+WC+giK6kFa4IWxVVYMr3vQ5ErgzG965+ceZxyqErhXpjUsEAZTqwDCzTE36JzYxx/tZzYFS2DkpzdMHZiRcF7866L5DjFhJE7IhRx8CNfi135COg7EBFdQig4izvJSwdLU80t/7k1SuOamfTCk2h0ruvDKz0HvHDuRGkLtx2OpistXZlw1C7+T2w3xBWwV9yuKpKhh0xApuIYJ+gZDJgVdUGaMQAQD0HfN2w59Wmnxu5kwOoxaBQEfJbRQtlYBWtq/v4IJdyjbx9b1Pzu3uvxXB4QbCmqT4MnqmiBgYL4Mq4aZ7diQleLPokhKM37P5G453mKTrkzcCyi6OuY5KLFpeBVbSu7seDPLKuCffZw5rfjU6OJ2LjIQyFm2S7E2B7MRBI/OmlbwzdbEpXQPWyjmfyFjL+/4JPu45FLkyEAAAAAElFTkSuQmCC'); background-size: 24px; background-repeat: no-repeat; - background-position: left 50%; + background-position: left center; padding-left: 36px; font-size: 20px; letter-spacing: -0.04rem; @@ -42,7 +42,7 @@ body { .message { font-weight: 300; - font-size: 1.3rem; + font-size: 1.4rem; } body.error .message { From 0a1c99f7c6e5ee4034da3a49d28edcfac2e3eea9 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Mon, 24 Feb 2020 11:26:38 -0800 Subject: [PATCH 098/200] Add go to nex/prev result for search editor --- src/vs/workbench/common/actions.ts | 5 +- .../search/browser/search.contribution.ts | 4 +- .../contrib/search/browser/searchActions.ts | 22 ++++++-- .../searchEditor/browser/searchEditor.ts | 53 ++++++++++++++++++- 4 files changed, 76 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/common/actions.ts b/src/vs/workbench/common/actions.ts index 46112bcce7c..7f5a5cbdd14 100644 --- a/src/vs/workbench/common/actions.ts +++ b/src/vs/workbench/common/actions.ts @@ -44,7 +44,10 @@ Registry.add(Extensions.WorkbenchActions, new class implements IWorkbenchActionR KeybindingsRegistry.registerKeybindingRule({ id: descriptor.id, weight: weight, - when: (descriptor.keybindingContext || when ? ContextKeyExpr.and(descriptor.keybindingContext, when) : null), + when: + descriptor.keybindingContext && when + ? ContextKeyExpr.and(descriptor.keybindingContext, when) + : descriptor.keybindingContext || when || null, primary: keybindings ? keybindings.primary : 0, secondary: keybindings?.secondary, win: keybindings?.win, diff --git a/src/vs/workbench/contrib/search/browser/search.contribution.ts b/src/vs/workbench/contrib/search/browser/search.contribution.ts index 8d5a04998d9..2bae8bb20ce 100644 --- a/src/vs/workbench/contrib/search/browser/search.contribution.ts +++ b/src/vs/workbench/contrib/search/browser/search.contribution.ts @@ -556,8 +556,8 @@ MenuRegistry.appendMenuItem(MenuId.MenubarEditMenu, { order: 1 }); -registry.registerWorkbenchAction(SyncActionDescriptor.create(FocusNextSearchResultAction, FocusNextSearchResultAction.ID, FocusNextSearchResultAction.LABEL, { primary: KeyCode.F4 }, ContextKeyExpr.and(Constants.HasSearchResults)), 'Focus Next Search Result', category); -registry.registerWorkbenchAction(SyncActionDescriptor.create(FocusPreviousSearchResultAction, FocusPreviousSearchResultAction.ID, FocusPreviousSearchResultAction.LABEL, { primary: KeyMod.Shift | KeyCode.F4 }, ContextKeyExpr.and(Constants.HasSearchResults)), 'Focus Previous Search Result', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(FocusNextSearchResultAction, FocusNextSearchResultAction.ID, FocusNextSearchResultAction.LABEL, { primary: KeyCode.F4 }, ContextKeyExpr.or(Constants.HasSearchResults, SearchEditorConstants.InSearchEditor)), 'Focus Next Search Result', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(FocusPreviousSearchResultAction, FocusPreviousSearchResultAction.ID, FocusPreviousSearchResultAction.LABEL, { primary: KeyMod.Shift | KeyCode.F4 }, ContextKeyExpr.or(Constants.HasSearchResults, SearchEditorConstants.InSearchEditor)), 'Focus Previous Search Result', category); registry.registerWorkbenchAction(SyncActionDescriptor.create(ReplaceInFilesAction, ReplaceInFilesAction.ID, ReplaceInFilesAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_H }), 'Replace in Files', category); MenuRegistry.appendMenuItem(MenuId.MenubarEditMenu, { diff --git a/src/vs/workbench/contrib/search/browser/searchActions.ts b/src/vs/workbench/contrib/search/browser/searchActions.ts index 0b16e7a65ad..574189c1a09 100644 --- a/src/vs/workbench/contrib/search/browser/searchActions.ts +++ b/src/vs/workbench/contrib/search/browser/searchActions.ts @@ -488,12 +488,19 @@ export class FocusNextSearchResultAction extends Action { static readonly LABEL = nls.localize('FocusNextSearchResult.label', "Focus Next Search Result"); constructor(id: string, label: string, - @IViewsService private readonly viewsService: IViewsService + @IViewsService private readonly viewsService: IViewsService, + @IEditorService private readonly editorService: IEditorService, ) { super(id, label); } - run(): Promise { + async run(): Promise { + const input = this.editorService.activeEditor; + if (input instanceof SearchEditorInput) { + // cast as we cannot import SearchEditor as a value b/c cyclic dependency. + return (this.editorService.activeControl as SearchEditor).focusNextResult(); + } + return openSearchView(this.viewsService).then(searchView => { if (searchView) { searchView.selectNextMatch(); @@ -507,12 +514,19 @@ export class FocusPreviousSearchResultAction extends Action { static readonly LABEL = nls.localize('FocusPreviousSearchResult.label', "Focus Previous Search Result"); constructor(id: string, label: string, - @IViewsService private readonly viewsService: IViewsService + @IViewsService private readonly viewsService: IViewsService, + @IEditorService private readonly editorService: IEditorService, ) { super(id, label); } - run(): Promise { + async run(): Promise { + const input = this.editorService.activeEditor; + if (input instanceof SearchEditorInput) { + // cast as we cannot import SearchEditor as a value b/c cyclic dependency. + return (this.editorService.activeControl as SearchEditor).focusPreviousResult(); + } + return openSearchView(this.viewsService).then(searchView => { if (searchView) { searchView.selectPreviousMatch(); diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts index edabee77d45..825889c6311 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts @@ -47,6 +47,7 @@ import { assertIsDefined } from 'vs/base/common/types'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfigurationService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { Position } from 'vs/editor/common/core/position'; const RESULT_LINE_REGEX = /^(\s+)(\d+)(:| )(\s+)(.*)$/; const FILE_LINE_REGEX = /^(\S.*):$/; @@ -299,6 +300,35 @@ export class SearchEditor extends BaseTextEditor { return this.configurationService.getValue('search'); } + private iterateThroughMatches(reverse: boolean) { + const model = this.searchResultEditor.getModel(); + if (!model) { return; } + + const lastLine = model.getLineCount() ?? 1; + const lastColumn = model.getLineLength(lastLine); + + const fallbackStart = reverse ? new Position(lastLine, lastColumn) : new Position(1, 1); + + const currentPosition = this.searchResultEditor.getSelection()?.getStartPosition() ?? fallbackStart; + + const matchRanges = this.getInput()?.getMatchRanges(); + if (!matchRanges) { return; } + + const matchRange = (reverse ? findPrevRange : findNextRange)(matchRanges, currentPosition); + + this.searchResultEditor.setSelection(matchRange); + this.searchResultEditor.revealLineInCenterIfOutsideViewport(matchRange.startLineNumber); + this.searchResultEditor.focus(); + } + + focusNextResult() { + this.iterateThroughMatches(false); + } + + focusPreviousResult() { + this.iterateThroughMatches(true); + } + async triggerSearch(_options?: { resetCursor?: boolean; delay?: number; }) { const options = { resetCursor: true, delay: 0, ..._options }; @@ -307,7 +337,7 @@ export class SearchEditor extends BaseTextEditor { await this.doRunSearch(); this.toggleRunAgainMessage(false); if (options.resetCursor) { - this.searchResultEditor.setSelection(new Range(1, 1, 1, 1)); + this.searchResultEditor.setPosition(new Position(1, 1)); this.searchResultEditor.setScrollPosition({ scrollTop: 0, scrollLeft: 0 }); } }, options.delay); @@ -523,3 +553,24 @@ registerThemingParticipant((theme, collector) => { }); export const searchEditorTextInputBorder = registerColor('searchEditor.textInputBorder', { dark: inputBorder, light: inputBorder, hc: inputBorder }, localize('textInputBoxBorder', "Search editor text input box border.")); + +function findNextRange(matchRanges: Range[], currentPosition: Position) { + for (const matchRange of matchRanges) { + if (Position.isBefore(currentPosition, matchRange.getStartPosition())) { + return matchRange; + } + } + return matchRanges[0]; +} + +function findPrevRange(matchRanges: Range[], currentPosition: Position) { + for (let i = matchRanges.length - 1; i >= 0; i--) { + const matchRange = matchRanges[i]; + if (Position.isBefore(matchRange.getStartPosition(), currentPosition)) { + { + return matchRange; + } + } + } + return matchRanges[matchRanges.length - 1]; +} From 64517a93849bcb9c4e270fb3e46a17473f03cf13 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Mon, 24 Feb 2020 11:47:19 -0800 Subject: [PATCH 099/200] Add selectAllSearchEditorMatches command --- .../contrib/searchEditor/browser/constants.ts | 1 + .../searchEditor/browser/searchEditor.contribution.ts | 10 +++++++++- .../contrib/searchEditor/browser/searchEditor.ts | 8 ++++++++ .../searchEditor/browser/searchEditorActions.ts | 8 ++++++++ 4 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/searchEditor/browser/constants.ts b/src/vs/workbench/contrib/searchEditor/browser/constants.ts index 890ea78e730..d2d0e3c3537 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/constants.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/constants.ts @@ -13,6 +13,7 @@ export const ToggleSearchEditorWholeWordCommandId = 'toggleSearchEditorWholeWord export const ToggleSearchEditorRegexCommandId = 'toggleSearchEditorRegex'; export const ToggleSearchEditorContextLinesCommandId = 'toggleSearchEditorContextLines'; export const RerunSearchEditorSearchCommandId = 'rerunSearchEditorSearch'; +export const SelectAllSearchEditorMatchesCommandId = 'selectAllSearchEditorMatches'; export const InSearchEditor = new RawContextKey('inSearchEditor', false); diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts index 9f3e023d83c..ec7fcb3c879 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts @@ -25,7 +25,7 @@ import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileE import * as SearchConstants from 'vs/workbench/contrib/search/common/constants'; import * as SearchEditorConstants from 'vs/workbench/contrib/searchEditor/browser/constants'; import { SearchEditor } from 'vs/workbench/contrib/searchEditor/browser/searchEditor'; -import { OpenResultsInEditorAction, OpenSearchEditorAction, toggleSearchEditorCaseSensitiveCommand, toggleSearchEditorContextLinesCommand, toggleSearchEditorRegexCommand, toggleSearchEditorWholeWordCommand } from 'vs/workbench/contrib/searchEditor/browser/searchEditorActions'; +import { OpenResultsInEditorAction, OpenSearchEditorAction, toggleSearchEditorCaseSensitiveCommand, toggleSearchEditorContextLinesCommand, toggleSearchEditorRegexCommand, toggleSearchEditorWholeWordCommand, selectAllSearchEditorMatchesCommand } from 'vs/workbench/contrib/searchEditor/browser/searchEditorActions'; import { getOrMakeSearchEditorInput, SearchEditorInput } from 'vs/workbench/contrib/searchEditor/browser/searchEditorInput'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; @@ -146,6 +146,14 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_L } }); +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: SearchEditorConstants.SelectAllSearchEditorMatchesCommandId, + weight: KeybindingWeight.WorkbenchContrib, + when: ContextKeyExpr.and(SearchEditorConstants.InSearchEditor), + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_L, + handler: selectAllSearchEditorMatchesCommand +}); + CommandsRegistry.registerCommand( SearchEditorConstants.RerunSearchEditorSearchCommandId, (accessor: ServicesAccessor) => { diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts index 825889c6311..d7557b789ba 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts @@ -48,6 +48,7 @@ import { ITextResourceConfigurationService } from 'vs/editor/common/services/tex import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { Position } from 'vs/editor/common/core/position'; +import { Selection } from 'vs/editor/common/core/selection'; const RESULT_LINE_REGEX = /^(\s+)(\d+)(:| )(\s+)(.*)$/; const FILE_LINE_REGEX = /^(\S.*):$/; @@ -329,6 +330,13 @@ export class SearchEditor extends BaseTextEditor { this.iterateThroughMatches(true); } + focusAllResults() { + this.searchResultEditor + .setSelections((this.getInput()?.getMatchRanges() ?? []).map( + range => new Selection(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn))); + this.searchResultEditor.focus(); + } + async triggerSearch(_options?: { resetCursor?: boolean; delay?: number; }) { const options = { resetCursor: true, delay: 0, ..._options }; diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions.ts index 30dcf6a88a3..094acf25fd3 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions.ts @@ -54,6 +54,14 @@ export const toggleSearchEditorContextLinesCommand = (accessor: ServicesAccessor } }; +export const selectAllSearchEditorMatchesCommand = (accessor: ServicesAccessor) => { + const editorService = accessor.get(IEditorService); + const input = editorService.activeEditor; + if (input instanceof SearchEditorInput) { + (editorService.activeControl as SearchEditor).focusAllResults(); + } +}; + export class OpenSearchEditorAction extends Action { From fff2324963cf40560081967707655672d5a769a2 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Mon, 24 Feb 2020 11:00:09 -0800 Subject: [PATCH 100/200] refs #86309 --- src/vs/base/browser/ui/dialog/dialog.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/vs/base/browser/ui/dialog/dialog.ts b/src/vs/base/browser/ui/dialog/dialog.ts index 987bebaf377..97bcd36f292 100644 --- a/src/vs/base/browser/ui/dialog/dialog.ts +++ b/src/vs/base/browser/ui/dialog/dialog.ts @@ -267,12 +267,13 @@ export class Dialog extends Disposable { if (this.checkbox) { this.checkbox.style(style); } + + if (this.messageDetailElement) { + const messageDetailColor = Color.fromHex(fgColor).transparent(.9); + this.messageDetailElement.style.color = messageDetailColor.makeOpaque(Color.fromHex(bgColor)).toString(); + } } - if (this.messageDetailElement && fgColor && bgColor) { - const messageDetailColor = Color.fromHex(fgColor).transparent(.9); - this.messageDetailElement.style.color = messageDetailColor.makeOpaque(Color.fromHex(bgColor)).toString(); - } } } From d226035b7315232024f3f9cfc15a5cd520e3fb52 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Mon, 24 Feb 2020 12:02:14 -0800 Subject: [PATCH 101/200] additional drag feedback --- src/vs/workbench/browser/parts/compositeBarActions.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/compositeBarActions.ts b/src/vs/workbench/browser/parts/compositeBarActions.ts index f09ef9d3c3a..b025bf17474 100644 --- a/src/vs/workbench/browser/parts/compositeBarActions.ts +++ b/src/vs/workbench/browser/parts/compositeBarActions.ts @@ -547,6 +547,13 @@ export class CompositeActionViewItem extends ActivityActionViewItem { this.updateFromDragging(container, true); } } + + if (this.compositeTransfer.hasData(DraggedViewIdentifier.prototype)) { + const data = this.compositeTransfer.getData(DraggedViewIdentifier.prototype); + if (Array.isArray(data) && data[0].id !== this.activity.id) { + this.updateFromDragging(container, true); + } + } }, onDragOver: e => { @@ -575,7 +582,8 @@ export class CompositeActionViewItem extends ActivityActionViewItem { }, onDragLeave: e => { - if (this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype)) { + if (this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype) || + this.compositeTransfer.hasData(DraggedViewIdentifier.prototype)) { this.updateFromDragging(container, false); } }, From d46c8a8c3c2d86ede577dc11eec9983cc62177da Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Mon, 24 Feb 2020 15:48:26 -0500 Subject: [PATCH 102/200] Lots of timeline related changes, below UI: Adds Refresh icon to view title Adds "Load more" entry at the end of the list for paging API: Restructures api around cursors Renames TimelineCursor to generic TimelineOptions for more flexibility Adds paging object to Timeline for clearer paging usage Changes cursors to be strings, and explicit before and after cursors Allows limit to take a cursor, so we can reload current data set Clarifies id and fallback to timestamp Adds reset flag to TimelineChangeEvent for providers to reset caching Git provider: Orders and returns commit date as the timestamp Supports limit of a cursor (using rev-list --count) Stops returning working/index changes when paging Forcably resets cached data when changes are detected (naive for now) --- extensions/git/src/git.ts | 31 +- extensions/git/src/timelineProvider.ts | 207 ++++++----- src/vs/base/common/iterator.ts | 13 + src/vs/vscode.proposed.d.ts | 71 ++-- .../api/browser/mainThreadTimeline.ts | 6 +- .../workbench/api/common/extHost.protocol.ts | 4 +- .../workbench/api/common/extHostTimeline.ts | 17 +- .../timeline/browser/timeline.contribution.ts | 89 +++++ .../contrib/timeline/browser/timelinePane.ts | 339 +++++++++++++++--- .../contrib/timeline/common/timeline.ts | 28 +- .../timeline/common/timelineService.ts | 18 +- 11 files changed, 636 insertions(+), 187 deletions(-) diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index 89206132470..ac3120c115a 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -50,8 +50,13 @@ interface MutableRemote extends Remote { * Log file options. */ export interface LogFileOptions { - /** Max number of log entries to retrieve. If not specified, the default is 32. */ - readonly maxEntries?: number; + /** Optional. The maximum number of log entries to retrieve. */ + readonly maxEntries?: number | string; + /** Optional. The Git sha (hash) to start retrieving log entries from. */ + readonly hash?: string; + /** Optional. Specifies whether to start retrieving log entries in reverse order. */ + readonly reverse?: boolean; + readonly sortByAuthorDate?: boolean; } function parseVersion(raw: string): string { @@ -817,8 +822,26 @@ export class Repository { } async logFile(uri: Uri, options?: LogFileOptions): Promise { - const maxEntries = options?.maxEntries ?? 32; - const args = ['log', `-n${maxEntries}`, `--format=${COMMIT_FORMAT}`, '-z', '--', uri.fsPath]; + const args = ['log', `--format=${COMMIT_FORMAT}`, '-z']; + + if (options?.maxEntries && !options?.reverse) { + args.push(`-n${options.maxEntries}`); + } + + if (options?.hash) { + // If we are reversing, we must add a range (with HEAD) because we are using --ancestry-path for better reverse walking + if (options?.reverse) { + args.push('--reverse', '--ancestry-path', `${options.hash}..HEAD`); + } else { + args.push(options.hash); + } + } + + if (options?.sortByAuthorDate) { + args.push('--author-date-order'); + } + + args.push('--', uri.fsPath); const result = await this.run(args); if (result.exitCode) { diff --git a/extensions/git/src/timelineProvider.ts b/extensions/git/src/timelineProvider.ts index 9db3495aec7..f0a95d61a34 100644 --- a/extensions/git/src/timelineProvider.ts +++ b/extensions/git/src/timelineProvider.ts @@ -5,7 +5,7 @@ import * as dayjs from 'dayjs'; import * as advancedFormat from 'dayjs/plugin/advancedFormat'; -import { CancellationToken, Disposable, Event, EventEmitter, ThemeIcon, Timeline, TimelineChangeEvent, TimelineCursor, TimelineItem, TimelineProvider, Uri, workspace } from 'vscode'; +import { CancellationToken, Disposable, Event, EventEmitter, ThemeIcon, Timeline, TimelineChangeEvent, TimelineItem, TimelineOptions, TimelineProvider, Uri, workspace } from 'vscode'; import { Model } from './model'; import { Repository } from './repository'; import { debounce } from './decorators'; @@ -87,7 +87,7 @@ export class GitTimelineProvider implements TimelineProvider { this._disposable.dispose(); } - async provideTimeline(uri: Uri, _cursor: TimelineCursor, _token: CancellationToken): Promise { + async provideTimeline(uri: Uri, options: TimelineOptions, _token: CancellationToken): Promise { // console.log(`GitTimelineProvider.provideTimeline: uri=${uri} state=${this._model.state}`); const repo = this._model.getRepository(uri); @@ -112,109 +112,152 @@ export class GitTimelineProvider implements TimelineProvider { // TODO[ECA]: Ensure that the uri is a file -- if not we could get the history of the repo? - const commits = await repo.logFile(uri); + let limit: number | undefined; + if (typeof options.limit === 'string') { + try { + const result = await this._model.git.exec(repo.root, ['rev-list', '--count', `${options.limit}..`, '--', uri.fsPath]); + if (!result.exitCode) { + // Ask for 1 more than so we can determine if there are more commits + limit = Number(result.stdout) + 1; + } + } + catch { + limit = undefined; + } + } else { + // If we are not getting everything, ask for 1 more than so we can determine if there are more commits + limit = options.limit === undefined ? undefined : options.limit + 1; + } + + + const commits = await repo.logFile(uri, { + maxEntries: limit, + hash: options.cursor, + reverse: options.before, + // sortByAuthorDate: true + }); + + const more = limit === undefined || options.before ? false : commits.length >= limit; + const paging = commits.length ? { + more: more, + cursors: { + before: commits[0]?.hash, + after: commits[commits.length - (more ? 1 : 2)]?.hash + } + } : undefined; + + // If we asked for an extra commit, strip it off + if (limit !== undefined && commits.length >= limit) { + commits.splice(commits.length - 1, 1); + } let dateFormatter: dayjs.Dayjs; const items = commits.map(c => { - dateFormatter = dayjs(c.authorDate); + const date = c.commitDate; // c.authorDate - const item = new GitTimelineItem(c.hash, `${c.hash}^`, c.message, c.authorDate?.getTime() ?? 0, c.hash, 'git:file:commit'); + dateFormatter = dayjs(date); + + const item = new GitTimelineItem(c.hash, `${c.hash}^`, c.message, date?.getTime() ?? 0, c.hash, 'git:file:commit'); item.iconPath = new (ThemeIcon as any)('git-commit'); item.description = c.authorName; item.detail = `${c.authorName} (${c.authorEmail}) \u2014 ${c.hash.substr(0, 8)}\n${dateFormatter.format('MMMM Do, YYYY h:mma')}\n\n${c.message}`; item.command = { title: 'Open Comparison', command: 'git.timeline.openDiff', - arguments: [uri, this.id, item] + arguments: [item, uri, this.id] }; return item; }); - const index = repo.indexGroup.resourceStates.find(r => r.resourceUri.fsPath === uri.fsPath); - if (index) { - const date = this._repoStatusDate ?? new Date(); - dateFormatter = dayjs(date); + if (options.cursor === undefined || options.before) { + const index = repo.indexGroup.resourceStates.find(r => r.resourceUri.fsPath === uri.fsPath); + if (index) { + const date = this._repoStatusDate ?? new Date(); + dateFormatter = dayjs(date); - let status; - switch (index.type) { - case Status.INDEX_MODIFIED: - status = 'Modified'; - break; - case Status.INDEX_ADDED: - status = 'Added'; - break; - case Status.INDEX_DELETED: - status = 'Deleted'; - break; - case Status.INDEX_RENAMED: - status = 'Renamed'; - break; - case Status.INDEX_COPIED: - status = 'Copied'; - break; - default: - status = ''; - break; + let status; + switch (index.type) { + case Status.INDEX_MODIFIED: + status = 'Modified'; + break; + case Status.INDEX_ADDED: + status = 'Added'; + break; + case Status.INDEX_DELETED: + status = 'Deleted'; + break; + case Status.INDEX_RENAMED: + status = 'Renamed'; + break; + case Status.INDEX_COPIED: + status = 'Copied'; + break; + default: + status = ''; + break; + } + + const item = new GitTimelineItem('~', 'HEAD', 'Staged Changes', date.getTime(), 'index', 'git:file:index'); + // TODO[ECA]: Replace with a better icon -- reflecting its status maybe? + item.iconPath = new (ThemeIcon as any)('git-commit'); + item.description = 'You'; + item.detail = `You \u2014 Index\n${dateFormatter.format('MMMM Do, YYYY h:mma')}\n${status}`; + item.command = { + title: 'Open Comparison', + command: 'git.timeline.openDiff', + arguments: [item, uri, this.id] + }; + + items.splice(0, 0, item); } - const item = new GitTimelineItem('~', 'HEAD', 'Staged Changes', date.getTime(), 'index', 'git:file:index'); - // TODO[ECA]: Replace with a better icon -- reflecting its status maybe? - item.iconPath = new (ThemeIcon as any)('git-commit'); - item.description = 'You'; - item.detail = `You \u2014 Index\n${dateFormatter.format('MMMM Do, YYYY h:mma')}\n${status}`; - item.command = { - title: 'Open Comparison', - command: 'git.timeline.openDiff', - arguments: [uri, this.id, item] - }; + const working = repo.workingTreeGroup.resourceStates.find(r => r.resourceUri.fsPath === uri.fsPath); + if (working) { + const date = new Date(); + dateFormatter = dayjs(date); - items.push(item); - } + let status; + switch (working.type) { + case Status.INDEX_MODIFIED: + status = 'Modified'; + break; + case Status.INDEX_ADDED: + status = 'Added'; + break; + case Status.INDEX_DELETED: + status = 'Deleted'; + break; + case Status.INDEX_RENAMED: + status = 'Renamed'; + break; + case Status.INDEX_COPIED: + status = 'Copied'; + break; + default: + status = ''; + break; + } + const item = new GitTimelineItem('', index ? '~' : 'HEAD', 'Uncommited Changes', date.getTime(), 'working', 'git:file:working'); + // TODO[ECA]: Replace with a better icon -- reflecting its status maybe? + item.iconPath = new (ThemeIcon as any)('git-commit'); + item.description = 'You'; + item.detail = `You \u2014 Working Tree\n${dateFormatter.format('MMMM Do, YYYY h:mma')}\n${status}`; + item.command = { + title: 'Open Comparison', + command: 'git.timeline.openDiff', + arguments: [item, uri, this.id] + }; - const working = repo.workingTreeGroup.resourceStates.find(r => r.resourceUri.fsPath === uri.fsPath); - if (working) { - const date = new Date(); - dateFormatter = dayjs(date); - - let status; - switch (working.type) { - case Status.INDEX_MODIFIED: - status = 'Modified'; - break; - case Status.INDEX_ADDED: - status = 'Added'; - break; - case Status.INDEX_DELETED: - status = 'Deleted'; - break; - case Status.INDEX_RENAMED: - status = 'Renamed'; - break; - case Status.INDEX_COPIED: - status = 'Copied'; - break; - default: - status = ''; - break; + items.splice(0, 0, item); } - - const item = new GitTimelineItem('', index ? '~' : 'HEAD', 'Uncommited Changes', date.getTime(), 'working', 'git:file:working'); - // TODO[ECA]: Replace with a better icon -- reflecting its status maybe? - item.iconPath = new (ThemeIcon as any)('git-commit'); - item.description = 'You'; - item.detail = `You \u2014 Working Tree\n${dateFormatter.format('MMMM Do, YYYY h:mma')}\n${status}`; - item.command = { - title: 'Open Comparison', - command: 'git.timeline.openDiff', - arguments: [uri, this.id, item] - }; - - items.push(item); } - return { items: items }; + return { + items: items, + paging: paging + }; } private onRepositoriesChanged(_repo: Repository) { @@ -241,6 +284,6 @@ export class GitTimelineProvider implements TimelineProvider { @debounce(500) private fireChanged() { - this._onDidChange.fire({}); + this._onDidChange.fire({ reset: true }); } } diff --git a/src/vs/base/common/iterator.ts b/src/vs/base/common/iterator.ts index 9287a6eaebf..6c87c85cf48 100644 --- a/src/vs/base/common/iterator.ts +++ b/src/vs/base/common/iterator.ts @@ -125,6 +125,19 @@ export module Iterator { }; } + export function some(iterator: Iterator | NativeIterator, fn: (t: T) => boolean): boolean { + while (true) { + const element = iterator.next(); + if (element.done) { + return false; + } + + if (fn(element.value)) { + return true; + } + } + } + export function forEach(iterator: Iterator, fn: (t: T) => void): void { for (let next = iterator.next(); !next.done; next = iterator.next()) { fn(next.value); diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index d04c46c8151..157c8732986 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1558,12 +1558,9 @@ declare module 'vscode' { label: string; /** - * Optional id for the timeline item. - */ - /** - * Optional id for the timeline item that has to be unique across your timeline source. + * Optional id for the timeline item. It must be unique across all the timeline items provided by this source. * - * If not provided, an id is generated using the timeline item's label. + * If not provided, an id is generated using the timeline item's timestamp. */ id?: string; @@ -1620,40 +1617,50 @@ declare module 'vscode' { * If the [uri](#Uri) is `undefined` that signals that the timeline source for all resources changed. */ uri?: Uri; - } - - export interface TimelineCursor { - /** - * A provider-defined cursor specifing the range of timeline items to be returned. Must be serializable. - */ - cursor?: any; /** - * A flag to specify whether the timeline items requested are before or after (default) the provided cursor. + * A flag which indicates whether the entire timeline should be reset. */ - before?: boolean; - - /** - * The maximum number of timeline items that should be returned. - */ - limit?: number; + reset?: boolean; } export interface Timeline { - /** - * A provider-defined cursor specifing the range of timeline items returned. Must be serializable. - */ - cursor?: any; + readonly paging?: { + /** + * A set of provider-defined cursors specifing the range of timeline items returned. + */ + readonly cursors: { + readonly before: string; + readonly after?: string + }; - /** - * A flag which indicates whether there are any more items that weren't returned. - */ - more?: boolean; + /** + * A flag which indicates whether there are more items that weren't returned. + */ + readonly more?: boolean; + } /** * An array of [timeline items](#TimelineItem). */ - items: TimelineItem[]; + readonly items: readonly TimelineItem[]; + } + + export interface TimelineOptions { + /** + * A provider-defined cursor specifing the range of timeline items that should be returned. + */ + cursor?: string; + + /** + * A flag to specify whether the timeline items being requested should be before or after (default) the provided cursor. + */ + before?: boolean; + + /** + * The maximum number or the ending cursor of timeline items that should be returned. + */ + limit?: number | string; } export interface TimelineProvider { @@ -1666,23 +1673,23 @@ declare module 'vscode' { /** * An identifier of the source of the timeline items. This can be used to filter sources. */ - id: string; + readonly id: string; /** * A human-readable string describing the source of the timeline items. This can be used as the display label when filtering sources. */ - label: string; + readonly label: string; /** * Provide [timeline items](#TimelineItem) for a [Uri](#Uri). * * @param uri The [uri](#Uri) of the file to provide the timeline for. + * @param options A set of options to determine how results should be returned. * @param token A cancellation token. - * @param cursor TBD * @return The [timeline result](#TimelineResult) or a thenable that resolves to such. The lack of a result * can be signaled by returning `undefined`, `null`, or an empty array. */ - provideTimeline(uri: Uri, cursor: TimelineCursor, token: CancellationToken): ProviderResult; + provideTimeline(uri: Uri, options: TimelineOptions, token: CancellationToken): ProviderResult; } export namespace workspace { diff --git a/src/vs/workbench/api/browser/mainThreadTimeline.ts b/src/vs/workbench/api/browser/mainThreadTimeline.ts index 428bf0ed2d9..cfeb1c6c38c 100644 --- a/src/vs/workbench/api/browser/mainThreadTimeline.ts +++ b/src/vs/workbench/api/browser/mainThreadTimeline.ts @@ -9,7 +9,7 @@ import { URI } from 'vs/base/common/uri'; import { ILogService } from 'vs/platform/log/common/log'; import { MainContext, MainThreadTimelineShape, IExtHostContext, ExtHostTimelineShape, ExtHostContext } from 'vs/workbench/api/common/extHost.protocol'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; -import { TimelineChangeEvent, TimelineCursor, TimelineProviderDescriptor, ITimelineService } from 'vs/workbench/contrib/timeline/common/timeline'; +import { TimelineChangeEvent, TimelineOptions, TimelineProviderDescriptor, ITimelineService } from 'vs/workbench/contrib/timeline/common/timeline'; @extHostNamedCustomer(MainContext.MainThreadTimeline) export class MainThreadTimeline implements MainThreadTimelineShape { @@ -39,8 +39,8 @@ export class MainThreadTimeline implements MainThreadTimelineShape { this._timelineService.registerTimelineProvider({ ...provider, onDidChange: onDidChange.event, - provideTimeline(uri: URI, cursor: TimelineCursor, token: CancellationToken, options?: { cacheResults?: boolean }) { - return proxy.$getTimeline(provider.id, uri, cursor, token, options); + provideTimeline(uri: URI, options: TimelineOptions, token: CancellationToken, internalOptions?: { cacheResults?: boolean }) { + return proxy.$getTimeline(provider.id, uri, options, token, internalOptions); }, dispose() { emitters.delete(provider.id); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 9a28df76850..a54b3d0766a 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -49,7 +49,7 @@ import { SaveReason } from 'vs/workbench/common/editor'; import { ExtensionActivationReason } from 'vs/workbench/api/common/extHostExtensionActivator'; import { TunnelDto } from 'vs/workbench/api/common/extHostTunnelService'; import { TunnelOptions } from 'vs/platform/remote/common/tunnel'; -import { Timeline, TimelineChangeEvent, TimelineCursor, TimelineProviderDescriptor } from 'vs/workbench/contrib/timeline/common/timeline'; +import { Timeline, TimelineChangeEvent, TimelineOptions, TimelineProviderDescriptor } from 'vs/workbench/contrib/timeline/common/timeline'; import { revive } from 'vs/base/common/marshalling'; import { CallHierarchyItem } from 'vs/workbench/contrib/callHierarchy/common/callHierarchy'; import { Dto } from 'vs/base/common/types'; @@ -1468,7 +1468,7 @@ export interface ExtHostTunnelServiceShape { } export interface ExtHostTimelineShape { - $getTimeline(source: string, uri: UriComponents, cursor: TimelineCursor, token: CancellationToken, options?: { cacheResults?: boolean }): Promise; + $getTimeline(source: string, uri: UriComponents, options: TimelineOptions, token: CancellationToken, internalOptions?: { cacheResults?: boolean }): Promise; } // --- proxy identifiers diff --git a/src/vs/workbench/api/common/extHostTimeline.ts b/src/vs/workbench/api/common/extHostTimeline.ts index f5f63a2ce4d..87fefc82cfc 100644 --- a/src/vs/workbench/api/common/extHostTimeline.ts +++ b/src/vs/workbench/api/common/extHostTimeline.ts @@ -7,7 +7,7 @@ import * as vscode from 'vscode'; import { UriComponents, URI } from 'vs/base/common/uri'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { ExtHostTimelineShape, MainThreadTimelineShape, IMainContext, MainContext } from 'vs/workbench/api/common/extHost.protocol'; -import { Timeline, TimelineCursor, TimelineItem, TimelineProvider } from 'vs/workbench/contrib/timeline/common/timeline'; +import { Timeline, TimelineItem, TimelineOptions, TimelineProvider } from 'vs/workbench/contrib/timeline/common/timeline'; import { IDisposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { CancellationToken } from 'vs/base/common/cancellation'; import { CommandsConverter, ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; @@ -16,7 +16,7 @@ import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; export interface IExtHostTimeline extends ExtHostTimelineShape { readonly _serviceBrand: undefined; - $getTimeline(id: string, uri: UriComponents, cursor: vscode.TimelineCursor, token: vscode.CancellationToken, options?: { cacheResults?: boolean }): Promise; + $getTimeline(id: string, uri: UriComponents, options: vscode.TimelineOptions, token: vscode.CancellationToken, internalOptions?: { cacheResults?: boolean }): Promise; } export const IExtHostTimeline = createDecorator('IExtHostTimeline'); @@ -50,9 +50,9 @@ export class ExtHostTimeline implements IExtHostTimeline { }); } - async $getTimeline(id: string, uri: UriComponents, cursor: vscode.TimelineCursor, token: vscode.CancellationToken, options?: { cacheResults?: boolean }): Promise { + async $getTimeline(id: string, uri: UriComponents, options: vscode.TimelineOptions, token: vscode.CancellationToken, internalOptions?: { cacheResults?: boolean }): Promise { const provider = this._providers.get(id); - return provider?.provideTimeline(URI.revive(uri), cursor, token, options); + return provider?.provideTimeline(URI.revive(uri), options, token, internalOptions); } registerTimelineProvider(scheme: string | string[], provider: vscode.TimelineProvider, _extensionId: ExtensionIdentifier, commandConverter: CommandsConverter): IDisposable { @@ -70,15 +70,15 @@ export class ExtHostTimeline implements IExtHostTimeline { ...provider, scheme: scheme, onDidChange: undefined, - async provideTimeline(uri: URI, cursor: TimelineCursor, token: CancellationToken, options?: { cacheResults?: boolean }) { + async provideTimeline(uri: URI, options: TimelineOptions, token: CancellationToken, internalOptions?: { cacheResults?: boolean }) { timelineDisposables.clear(); // For now, only allow the caching of a single Uri - if (options?.cacheResults && !itemsBySourceByUriMap.has(getUriKey(uri))) { + if (internalOptions?.cacheResults && !itemsBySourceByUriMap.has(getUriKey(uri))) { itemsBySourceByUriMap.clear(); } - const result = await provider.provideTimeline(uri, cursor, token); + const result = await provider.provideTimeline(uri, options, token); // Intentional == we don't know how a provider will respond // eslint-disable-next-line eqeqeq if (result == null) { @@ -86,7 +86,7 @@ export class ExtHostTimeline implements IExtHostTimeline { } // TODO: Determine if we should cache dependent on who calls us (internal vs external) - const convertItem = convertTimelineItem(uri, options?.cacheResults ?? false); + const convertItem = convertTimelineItem(uri, internalOptions?.cacheResults ?? false); return { ...result, source: provider.id, @@ -143,6 +143,7 @@ export class ExtHostTimeline implements IExtHostTimeline { return { ...props, + id: props.id ?? undefined, handle: handle, source: source, command: item.command ? commandConverter.toInternal(item.command, disposables) : undefined, diff --git a/src/vs/workbench/contrib/timeline/browser/timeline.contribution.ts b/src/vs/workbench/contrib/timeline/browser/timeline.contribution.ts index 142fb5cb596..11cae1ddb07 100644 --- a/src/vs/workbench/contrib/timeline/browser/timeline.contribution.ts +++ b/src/vs/workbench/contrib/timeline/browser/timeline.contribution.ts @@ -14,6 +14,8 @@ import { TimelineService } from 'vs/workbench/contrib/timeline/common/timelineSe import { TimelinePane } from './timelinePane'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; +import { ICommandHandler, CommandsRegistry } from 'vs/platform/commands/common/commands'; import product from 'vs/platform/product/common/product'; export class TimelinePaneDescriptor implements IViewDescriptor { @@ -51,4 +53,91 @@ if (product.quality !== 'stable') { Registry.as(ViewExtensions.ViewsRegistry).registerViews([new TimelinePaneDescriptor()], VIEW_CONTAINER); } + +namespace TimelineViewRefreshAction { + + export const ID = 'timeline.refresh'; + export const LABEL = localize('timeline.refreshView', "Refresh"); + + export function handler(): ICommandHandler { + return (accessor, arg) => { + const service = accessor.get(ITimelineService); + return service.reset(); + }; + } +} + +CommandsRegistry.registerCommand(TimelineViewRefreshAction.ID, TimelineViewRefreshAction.handler()); + +// namespace TimelineViewRefreshHardAction { + +// export const ID = 'timeline.refreshHard'; +// export const LABEL = localize('timeline.refreshHard', "Refresh (Hard)"); + +// export function handler(fetch?: 'all' | 'more'): ICommandHandler { +// return (accessor, arg) => { +// const service = accessor.get(ITimelineService); +// return service.refresh(fetch); +// }; +// } +// } + +// CommandsRegistry.registerCommand(TimelineViewRefreshAction.ID, TimelineViewRefreshAction.handler()); + +// namespace TimelineViewLoadMoreAction { + +// export const ID = 'timeline.loadMore'; +// export const LABEL = localize('timeline.loadMoreInView', "Load More"); + +// export function handler(): ICommandHandler { +// return (accessor, arg) => { +// const service = accessor.get(ITimelineService); +// return service.refresh('more'); +// }; +// } +// } + +// CommandsRegistry.registerCommand(TimelineViewLoadMoreAction.ID, TimelineViewLoadMoreAction.handler()); + +// namespace TimelineViewLoadAllAction { + +// export const ID = 'timeline.loadAll'; +// export const LABEL = localize('timeline.loadAllInView', "Load All"); + +// export function handler(): ICommandHandler { +// return (accessor, arg) => { +// const service = accessor.get(ITimelineService); +// return service.refresh('all'); +// }; +// } +// } + +// CommandsRegistry.registerCommand(TimelineViewLoadAllAction.ID, TimelineViewLoadAllAction.handler()); + +MenuRegistry.appendMenuItem(MenuId.TimelineTitle, ({ + group: 'navigation', + order: 1, + command: { + id: TimelineViewRefreshAction.ID, + title: TimelineViewRefreshAction.LABEL, + icon: { id: 'codicon/refresh' } + } +})); + +// MenuRegistry.appendMenuItem(MenuId.TimelineTitle, ({ +// group: 'navigation', +// order: 2, +// command: { +// id: TimelineViewLoadMoreAction.ID, +// title: TimelineViewLoadMoreAction.LABEL, +// icon: { id: 'codicon/unfold' } +// }, +// alt: { +// id: TimelineViewLoadAllAction.ID, +// title: TimelineViewLoadAllAction.LABEL, +// icon: { id: 'codicon/unfold' } + +// } +// })); + registerSingleton(ITimelineService, TimelineService, true); diff --git a/src/vs/workbench/contrib/timeline/browser/timelinePane.ts b/src/vs/workbench/contrib/timeline/browser/timelinePane.ts index d15f9ab2a42..9e322c2fcd9 100644 --- a/src/vs/workbench/contrib/timeline/browser/timelinePane.ts +++ b/src/vs/workbench/contrib/timeline/browser/timelinePane.ts @@ -8,6 +8,7 @@ import { localize } from 'vs/nls'; import * as DOM from 'vs/base/browser/dom'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { FuzzyScore, createMatches } from 'vs/base/common/filters'; +import { Iterator } from 'vs/base/common/iterator'; import { DisposableStore, IDisposable, Disposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; @@ -20,7 +21,7 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { ITimelineService, TimelineChangeEvent, TimelineProvidersChangeEvent, TimelineRequest, TimelineItem } from 'vs/workbench/contrib/timeline/common/timeline'; +import { ITimelineService, TimelineChangeEvent, TimelineItem, TimelineOptions, TimelineProvidersChangeEvent, TimelineRequest, Timeline } from 'vs/workbench/contrib/timeline/common/timeline'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { SideBySideEditor, toResource } from 'vs/workbench/common/editor'; import { ICommandService } from 'vs/platform/commands/common/commands'; @@ -28,7 +29,6 @@ import { IThemeService, LIGHT, ThemeIcon } from 'vs/platform/theme/common/themeS import { IViewDescriptorService } from 'vs/workbench/common/views'; import { basename } from 'vs/base/common/path'; import { IProgressService } from 'vs/platform/progress/common/progress'; -import { VIEWLET_ID } from 'vs/workbench/contrib/files/common/files'; import { debounce } from 'vs/base/common/decorators'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IActionViewItemProvider, ActionBar, ActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; @@ -40,13 +40,53 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; // TODO[ECA]: Localize all the strings -type TreeElement = TimelineItem; +const InitialPageSize = 20; +const SubsequentPageSize = 40; + +interface CommandItem { + handle: 'vscode-command:loadMore'; + timestamp: number; + label: string; + themeIcon?: { id: string }; + description?: string; + detail?: string; + contextValue?: string; + + // Make things easier for duck typing + id: undefined; + icon: undefined; + iconDark: undefined; + source: undefined; +} + +type TreeElement = TimelineItem | CommandItem; + +// function isCommandItem(item: TreeElement | undefined): item is CommandItem { +// return item?.handle.startsWith('vscode-command:') ?? false; +// } + +function isLoadMoreCommandItem(item: TreeElement | undefined): item is CommandItem & { + handle: 'vscode-command:loadMore'; +} { + return item?.handle === 'vscode-command:loadMore'; +} + +function isTimelineItem(item: TreeElement | undefined): item is TimelineItem { + return !item?.handle.startsWith('vscode-command:') ?? false; +} + interface TimelineActionContext { uri: URI | undefined; item: TreeElement; } +interface TimelineCursors { + startCursors?: { before: any; after?: any }; + endCursors?: { before: any; after?: any }; + more: boolean; +} + export class TimelinePane extends ViewPane { static readonly ID = 'timeline'; static readonly TITLE = localize('timeline', 'Timeline'); @@ -60,7 +100,8 @@ export class TimelinePane extends ViewPane { private _visibilityDisposables: DisposableStore | undefined; // private _excludedSources: Set | undefined; - private _items: TimelineItem[] = []; + private _cursorsByProvider: Map = new Map(); + private _items: { element: TreeElement }[] = []; private _loadingMessageTimer: any | undefined; private _pendingRequests = new Map(); private _uri: URI | undefined; @@ -105,7 +146,7 @@ export class TimelinePane extends ViewPane { this._uri = uri; this._treeRenderer?.setUri(uri); - this.loadTimeline(); + this.loadTimeline(true); } private onProvidersChanged(e: TimelineProvidersChangeEvent) { @@ -116,16 +157,20 @@ export class TimelinePane extends ViewPane { } if (e.added) { - this.loadTimeline(e.added); + this.loadTimeline(true, e.added); } } private onTimelineChanged(e: TimelineChangeEvent) { - if (e.uri === undefined || e.uri.toString(true) !== this._uri?.toString(true)) { - this.loadTimeline([e.id]); + if (e?.uri === undefined || e.uri.toString(true) !== this._uri?.toString(true)) { + this.loadTimeline(e.reset ?? false, e?.id === undefined ? undefined : [e.id], { before: !e.reset }); } } + private onReset() { + this.loadTimeline(true); + } + private _message: string | undefined; get message(): string | undefined { return this._message; @@ -160,22 +205,27 @@ export class TimelinePane extends ViewPane { DOM.clearNode(this._messageElement); } - private async loadTimeline(sources?: string[]) { + private async loadTimeline(reset: boolean, sources?: string[], options: TimelineOptions = {}) { + const defaultPageSize = reset ? InitialPageSize : SubsequentPageSize; + // If we have no source, we are reseting all sources, so cancel everything in flight and reset caches if (sources === undefined) { - this._items.length = 0; + if (reset) { + this._items.length = 0; + this._cursorsByProvider.clear(); - if (this._loadingMessageTimer) { - clearTimeout(this._loadingMessageTimer); - this._loadingMessageTimer = undefined; + if (this._loadingMessageTimer) { + clearTimeout(this._loadingMessageTimer); + this._loadingMessageTimer = undefined; + } + + for (const { tokenSource } of this._pendingRequests.values()) { + tokenSource.dispose(true); + } + + this._pendingRequests.clear(); } - for (const { tokenSource } of this._pendingRequests.values()) { - tokenSource.dispose(true); - } - - this._pendingRequests.clear(); - // TODO[ECA]: Are these the right the list of schemes to exclude? Is there a better way? if (this._uri && (this._uri.scheme === 'vscode-settings' || this._uri.scheme === 'webview-panel' || this._uri.scheme === 'walkThrough')) { this.message = 'The active editor cannot provide timeline information.'; @@ -184,7 +234,7 @@ export class TimelinePane extends ViewPane { return; } - if (this._uri !== undefined) { + if (reset && this._uri !== undefined) { this._loadingMessageTimer = setTimeout((uri: URI) => { if (uri !== this._uri) { return; @@ -200,50 +250,155 @@ export class TimelinePane extends ViewPane { return; } + let lastIndex = this._items.length - 1; + let lastItem = this._items[lastIndex]?.element; + if (isLoadMoreCommandItem(lastItem)) { + lastItem.themeIcon = { id: 'sync~spin' }; + // this._items.splice(lastIndex, 1); + lastIndex--; + + if (!reset && !options.before) { + lastItem = this._items[lastIndex]?.element; + const selection = [lastItem]; + this._tree.setSelection(selection); + this._tree.setFocus(selection); + } + } + for (const source of sources ?? this.timelineService.getSources()) { let request = this._pendingRequests.get(source); - request?.tokenSource.dispose(true); - request = this.timelineService.getTimeline(source, this._uri, {}, new CancellationTokenSource(), { cacheResults: true })!; + const cursors = this._cursorsByProvider.get(source); + if (!reset) { + // TODO: Handle pending request - this._pendingRequests.set(source, request); - request.tokenSource.token.onCancellationRequested(() => this._pendingRequests.delete(source)); + if (cursors?.more === false) { + continue; + } + + const reusingToken = request?.tokenSource !== undefined; + request = this.timelineService.getTimeline( + source, this._uri, + { + cursor: options.before ? cursors?.startCursors?.before : (cursors?.endCursors ?? cursors?.startCursors)?.after, + ...options, + limit: options.limit === 0 ? undefined : options.limit ?? defaultPageSize + }, + request?.tokenSource ?? new CancellationTokenSource(), { cacheResults: true } + )!; + + this._pendingRequests.set(source, request); + if (!reusingToken) { + request.tokenSource.token.onCancellationRequested(() => this._pendingRequests.delete(source)); + } + } else { + request?.tokenSource.dispose(true); + + request = this.timelineService.getTimeline( + source, this._uri, + { + ...options, + limit: options.limit === 0 ? undefined : (reset ? cursors?.endCursors?.after : undefined) ?? options.limit ?? defaultPageSize + }, + new CancellationTokenSource(), { cacheResults: true } + )!; + + this._pendingRequests.set(source, request); + request.tokenSource.token.onCancellationRequested(() => this._pendingRequests.delete(source)); + } this.handleRequest(request); } } private async handleRequest(request: TimelineRequest) { - let items; + let timeline: Timeline | undefined; try { - items = await this.progressService.withProgress({ location: VIEWLET_ID }, () => request.result.then(r => r?.items ?? [])); + timeline = await this.progressService.withProgress({ location: this.getProgressLocation() }, () => request.result); + } + finally { + this._pendingRequests.delete(request.source); } - catch { } - this._pendingRequests.delete(request.source); - if (request.tokenSource.token.isCancellationRequested || request.uri !== this._uri) { + if ( + timeline === undefined || + request.tokenSource.token.isCancellationRequested || + request.uri !== this._uri + ) { return; } - this.replaceItems(request.source, items); - } + let items: TreeElement[]; - private replaceItems(source: string, items?: TimelineItem[]) { - const hasItems = this._items.length !== 0; + const source = request.source; - if (items?.length) { - this._items.splice(0, this._items.length, ...this._items.filter(i => i.source !== source), ...items); - this._items.sort((a, b) => (b.timestamp - a.timestamp) || b.source.localeCompare(a.source, undefined, { numeric: true, sensitivity: 'base' })); + if (timeline !== undefined) { + if (timeline.paging !== undefined) { + let cursors = this._cursorsByProvider.get(timeline.source ?? source); + if (cursors === undefined) { + cursors = { startCursors: timeline.paging.cursors, more: timeline.paging.more ?? false }; + this._cursorsByProvider.set(timeline.source, cursors); + } else { + if (request.options.before) { + if (cursors.endCursors === undefined) { + cursors.endCursors = cursors.startCursors; + } + cursors.startCursors = timeline.paging.cursors; + } + else { + if (cursors.startCursors === undefined) { + cursors.startCursors = timeline.paging.cursors; + } + cursors.endCursors = timeline.paging.cursors; + } + cursors.more = timeline.paging.more ?? true; + } + } + } else { + this._cursorsByProvider.delete(source); } - else if (this._items.length && this._items.some(i => i.source === source)) { - this._items = this._items.filter(i => i.source !== source); + items = (timeline.items as TreeElement[]) ?? []; + + const alreadyHadItems = this._items.length !== 0; + + let changed; + if (request.options.cursor) { + changed = this.mergeItems(request.source, items, request.options); + } else { + changed = this.replaceItems(request.source, items); } - else { + + if (!changed) { return; } + if (this._pendingRequests.size === 0 && this._items.length !== 0) { + const lastIndex = this._items.length - 1; + const lastItem = this._items[lastIndex]?.element; + + if (timeline.paging?.more || Iterator.some(this._cursorsByProvider.values(), cursors => cursors.more)) { + if (isLoadMoreCommandItem(lastItem)) { + lastItem.themeIcon = undefined; + } + else { + this._items.push({ + element: { + handle: 'vscode-command:loadMore', + label: 'Load more', + timestamp: 0 + } as CommandItem + }); + } + } + else { + if (isLoadMoreCommandItem(lastItem)) { + this._items.splice(lastIndex, 1); + } + } + } + // If we have items already and there are other pending requests, debounce for a bit to wait for other requests - if (hasItems && this._pendingRequests.size !== 0) { + if (alreadyHadItems && this._pendingRequests.size !== 0) { this.refreshDebounced(); } else { @@ -251,6 +406,79 @@ export class TimelinePane extends ViewPane { } } + private mergeItems(source: string, items: TreeElement[] | undefined, options: TimelineOptions): boolean { + if (items?.length === undefined || items.length === 0) { + return false; + } + + if (options.before) { + const ids = new Set(); + const timestamps = new Set(); + + for (const item of items) { + if (item.id === undefined) { + timestamps.add(item.timestamp); + } + else { + ids.add(item.id); + } + } + + // Remove any duplicate items + // I don't think we need to check all the items, just the most recent page + let i = Math.min(SubsequentPageSize, this._items.length); + let item; + while (i--) { + item = this._items[i].element; + if ( + (item.id === undefined && ids.has(item.id)) || + (item.timestamp === undefined && timestamps.has(item.timestamp)) + ) { + this._items.splice(i, 1); + } + } + + this._items.splice(0, 0, ...items.map(item => ({ element: item }))); + } else { + this._items.push(...items.map(item => ({ element: item }))); + } + + this.sortItems(); + return true; + } + + private replaceItems(source: string, items?: TreeElement[]): boolean { + if (items?.length) { + this._items.splice( + 0, this._items.length, + ...this._items.filter(item => item.element.source !== source), + ...items.map(item => ({ element: item })) + ); + this.sortItems(); + + return true; + } + + if (this._items.length && this._items.some(item => item.element.source === source)) { + this._items = this._items.filter(item => item.element.source !== source); + + return true; + } + + return false; + } + + private sortItems() { + this._items.sort( + (a, b) => + (b.element.timestamp - a.element.timestamp) || + (a.element.source === undefined + ? b.element.source === undefined ? 0 : 1 + : b.element.source === undefined ? -1 : b.element.source.localeCompare(a.element.source, undefined, { numeric: true, sensitivity: 'base' })) + ); + + } + private refresh() { if (this._loadingMessageTimer) { clearTimeout(this._loadingMessageTimer); @@ -263,7 +491,7 @@ export class TimelinePane extends ViewPane { this.message = undefined; } - this._tree.setChildren(null, this._items.map(item => ({ element: item }))); + this._tree.setChildren(null, this._items); } @debounce(500) @@ -282,6 +510,7 @@ export class TimelinePane extends ViewPane { this.timelineService.onDidChangeProviders(this.onProvidersChanged, this, this._visibilityDisposables); this.timelineService.onDidChangeTimeline(this.onTimelineChanged, this, this._visibilityDisposables); + this.timelineService.onDidReset(this.onReset, this, this._visibilityDisposables); this.editorService.onDidActiveEditorChange(this.onActiveEditorChanged, this, this._visibilityDisposables); this.onActiveEditorChanged(); @@ -329,9 +558,24 @@ export class TimelinePane extends ViewPane { } const selection = this._tree.getSelection(); - const command = selection.length === 1 ? selection[0]?.command : undefined; - if (command) { - this.commandService.executeCommand(command.id, ...(command.arguments || [])); + const item = selection.length === 1 ? selection[0] : undefined; + // eslint-disable-next-line eqeqeq + if (item == null) { + return; + } + + if (isTimelineItem(item)) { + if (item.command) { + this.commandService.executeCommand(item.command.id, ...(item.command.arguments || [])); + } + } + else if (isLoadMoreCommandItem(item)) { + // TODO: Change this, but right now this is the pending signal + if (item.themeIcon !== undefined) { + return; + } + + this.loadTimeline(false); } }) ); @@ -417,6 +661,11 @@ export class TimelineIdentityProvider implements IIdentityProvider class TimelineActionRunner extends ActionRunner { runAction(action: IAction, { uri, item }: TimelineActionContext): Promise { + if (!isTimelineItem(item)) { + // TODO + return action.run(); + } + return action.run(...[ { $mid: 11, @@ -499,7 +748,7 @@ class TimelineTreeRenderer implements ITreeRenderer; - provideTimeline(uri: URI, cursor: TimelineCursor, token: CancellationToken, options?: { cacheResults?: boolean }): Promise; + provideTimeline(uri: URI, options: TimelineOptions, token: CancellationToken, internalOptions?: { cacheResults?: boolean }): Promise; } export interface TimelineProviderDescriptor { @@ -68,6 +75,7 @@ export interface TimelineProvidersChangeEvent { export interface TimelineRequest { readonly result: Promise; + readonly options: TimelineOptions; readonly source: string; readonly tokenSource: CancellationTokenSource; readonly uri: URI; @@ -78,13 +86,17 @@ export interface ITimelineService { onDidChangeProviders: Event; onDidChangeTimeline: Event; + onDidReset: Event; registerTimelineProvider(provider: TimelineProvider): IDisposable; unregisterTimelineProvider(id: string): void; getSources(): string[]; - getTimeline(id: string, uri: URI, cursor: TimelineCursor, tokenSource: CancellationTokenSource, options?: { cacheResults?: boolean }): TimelineRequest | undefined; + getTimeline(id: string, uri: URI, options: TimelineOptions, tokenSource: CancellationTokenSource, internalOptions?: { cacheResults?: boolean }): TimelineRequest | undefined; + + // refresh(fetch?: 'all' | 'more'): void; + reset(): void; } const TIMELINE_SERVICE_ID = 'timeline'; diff --git a/src/vs/workbench/contrib/timeline/common/timelineService.ts b/src/vs/workbench/contrib/timeline/common/timelineService.ts index 27038106272..d017b15245d 100644 --- a/src/vs/workbench/contrib/timeline/common/timelineService.ts +++ b/src/vs/workbench/contrib/timeline/common/timelineService.ts @@ -9,7 +9,7 @@ import { IDisposable } from 'vs/base/common/lifecycle'; // import { basename } from 'vs/base/common/path'; import { URI } from 'vs/base/common/uri'; import { ILogService } from 'vs/platform/log/common/log'; -import { ITimelineService, TimelineChangeEvent, TimelineCursor, TimelineProvidersChangeEvent, TimelineProvider } from './timeline'; +import { ITimelineService, TimelineChangeEvent, TimelineOptions, TimelineProvidersChangeEvent, TimelineProvider } from './timeline'; export class TimelineService implements ITimelineService { _serviceBrand: undefined; @@ -20,6 +20,9 @@ export class TimelineService implements ITimelineService { private readonly _onDidChangeTimeline = new Emitter(); readonly onDidChangeTimeline: Event = this._onDidChangeTimeline.event; + private readonly _onDidReset = new Emitter(); + readonly onDidReset: Event = this._onDidReset.event; + private readonly _providers = new Map(); private readonly _providerSubscriptions = new Map(); @@ -81,7 +84,7 @@ export class TimelineService implements ITimelineService { return [...this._providers.keys()]; } - getTimeline(id: string, uri: URI, cursor: TimelineCursor, tokenSource: CancellationTokenSource, options?: { cacheResults?: boolean }) { + getTimeline(id: string, uri: URI, options: TimelineOptions, tokenSource: CancellationTokenSource, internalOptions?: { cacheResults?: boolean }) { this.logService.trace(`TimelineService#getTimeline(${id}): uri=${uri.toString(true)}`); const provider = this._providers.get(id); @@ -98,7 +101,7 @@ export class TimelineService implements ITimelineService { } return { - result: provider.provideTimeline(uri, cursor, tokenSource.token, options) + result: provider.provideTimeline(uri, options, tokenSource.token, internalOptions) .then(result => { if (result === undefined) { return undefined; @@ -109,6 +112,7 @@ export class TimelineService implements ITimelineService { return result; }), + options: options, source: provider.id, tokenSource: tokenSource, uri: uri @@ -156,4 +160,12 @@ export class TimelineService implements ITimelineService { this._providerSubscriptions.delete(id); this._onDidChangeProviders.fire({ removed: [id] }); } + + // refresh(fetch?: 'all' | 'more') { + // this._onDidChangeTimeline.fire({ fetch: fetch }); + // } + + reset() { + this._onDidReset.fire(); + } } From 6ff3d9c899961a88a356a813bf48aa231d842070 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 24 Feb 2020 15:02:36 -0800 Subject: [PATCH 103/200] Auto restart TS Server on watch options change --- .../src/typescriptServiceClient.ts | 2 +- .../src/utils/configuration.ts | 17 ++++++-- .../src/utils/objects.ts | 43 +++++++++++++++++++ 3 files changed, 57 insertions(+), 5 deletions(-) create mode 100644 extensions/typescript-language-features/src/utils/objects.ts diff --git a/extensions/typescript-language-features/src/typescriptServiceClient.ts b/extensions/typescript-language-features/src/typescriptServiceClient.ts index 6be28453e0e..5671d49d301 100644 --- a/extensions/typescript-language-features/src/typescriptServiceClient.ts +++ b/extensions/typescript-language-features/src/typescriptServiceClient.ts @@ -477,7 +477,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType this.bufferSyncSupport.reset(); const watchOptions = this.apiVersion.gte(API.v380) - ? vscode.workspace.getConfiguration('typescript').get('tsserver.watchOptions') + ? this.configuration.watchOptions : undefined; const configureOptions: Proto.ConfigureRequestArguments = { diff --git a/extensions/typescript-language-features/src/utils/configuration.ts b/extensions/typescript-language-features/src/utils/configuration.ts index 25315feab3c..f42b0c780b2 100644 --- a/extensions/typescript-language-features/src/utils/configuration.ts +++ b/extensions/typescript-language-features/src/utils/configuration.ts @@ -2,10 +2,12 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as vscode from 'vscode'; -import * as arrays from './arrays'; + import * as os from 'os'; import * as path from 'path'; +import * as vscode from 'vscode'; +import * as objects from '../utils/objects'; +import * as arrays from './arrays'; export enum TsServerLogLevel { Off, @@ -50,13 +52,14 @@ export class TypeScriptServiceConfiguration { public readonly localTsdk: string | null; public readonly npmLocation: string | null; public readonly tsServerLogLevel: TsServerLogLevel = TsServerLogLevel.Off; - public readonly tsServerPluginPaths: string[]; + public readonly tsServerPluginPaths: readonly string[]; public readonly checkJs: boolean; public readonly experimentalDecorators: boolean; public readonly disableAutomaticTypeAcquisition: boolean; public readonly useSeparateSyntaxServer: boolean; public readonly enableProjectDiagnostics: boolean; public readonly maxTsServerMemory: number; + public readonly watchOptions: protocol.WatchOptions | undefined; public static loadFromWorkspace(): TypeScriptServiceConfiguration { return new TypeScriptServiceConfiguration(); @@ -77,6 +80,7 @@ export class TypeScriptServiceConfiguration { this.useSeparateSyntaxServer = TypeScriptServiceConfiguration.readUseSeparateSyntaxServer(configuration); this.enableProjectDiagnostics = TypeScriptServiceConfiguration.readEnableProjectDiagnostics(configuration); this.maxTsServerMemory = TypeScriptServiceConfiguration.readMaxTsServerMemory(configuration); + this.watchOptions = TypeScriptServiceConfiguration.readWatchOptions(configuration); } public isEqualTo(other: TypeScriptServiceConfiguration): boolean { @@ -91,7 +95,8 @@ export class TypeScriptServiceConfiguration { && arrays.equals(this.tsServerPluginPaths, other.tsServerPluginPaths) && this.useSeparateSyntaxServer === other.useSeparateSyntaxServer && this.enableProjectDiagnostics === other.enableProjectDiagnostics - && this.maxTsServerMemory === other.maxTsServerMemory; + && this.maxTsServerMemory === other.maxTsServerMemory + && objects.equals(this.watchOptions, other.watchOptions); } private static fixPathPrefixes(inspectValue: string): string { @@ -157,6 +162,10 @@ export class TypeScriptServiceConfiguration { return configuration.get('typescript.tsserver.experimental.enableProjectDiagnostics', false); } + private static readWatchOptions(configuration: vscode.WorkspaceConfiguration): protocol.WatchOptions | undefined { + return configuration.get('typescript.tsserver.watchOptions'); + } + private static readMaxTsServerMemory(configuration: vscode.WorkspaceConfiguration): number { const defaultMaxMemory = 3072; const minimumMaxMemory = 128; diff --git a/extensions/typescript-language-features/src/utils/objects.ts b/extensions/typescript-language-features/src/utils/objects.ts new file mode 100644 index 00000000000..a31467bd8d6 --- /dev/null +++ b/extensions/typescript-language-features/src/utils/objects.ts @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as array from './arrays'; + +export function equals(one: any, other: any): boolean { + if (one === other) { + return true; + } + if (one === null || one === undefined || other === null || other === undefined) { + return false; + } + if (typeof one !== typeof other) { + return false; + } + if (typeof one !== 'object') { + return false; + } + if (Array.isArray(one) !== Array.isArray(other)) { + return false; + } + + if (Array.isArray(one)) { + return array.equals(one, other, equals); + } else { + const oneKeys: string[] = []; + for (const key in one) { + oneKeys.push(key); + } + oneKeys.sort(); + const otherKeys: string[] = []; + for (const key in other) { + otherKeys.push(key); + } + otherKeys.sort(); + if (!array.equals(oneKeys, otherKeys)) { + return false; + } + return oneKeys.every(key => equals(one[key], other[key])); + } +} From 3c030d70146302f995d6c23dfa13fcb43a5448d8 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 24 Feb 2020 15:12:53 -0800 Subject: [PATCH 104/200] Add extra guard on restarting TS server on fatal crash --- .../src/typescriptServiceClient.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/extensions/typescript-language-features/src/typescriptServiceClient.ts b/extensions/typescript-language-features/src/typescriptServiceClient.ts index 5671d49d301..2d256281438 100644 --- a/extensions/typescript-language-features/src/typescriptServiceClient.ts +++ b/extensions/typescript-language-features/src/typescriptServiceClient.ts @@ -109,6 +109,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType private lastStart: number; private numberRestarts: number; private isRestarting: boolean = false; + private hasServerFatallyCrashedTooManyTimes = false; private readonly loadingIndicator = new ServerInitializingIndicator(); public readonly telemetryReporter: TelemetryReporter; @@ -306,7 +307,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType private token: number = 0; private startService(resendModels: boolean = false): ServerState.State { - if (this.isDisposed) { + if (this.isDisposed || this.hasServerFatallyCrashedTooManyTimes) { return ServerState.None; } @@ -542,6 +543,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType if (diff < 10 * 1000 /* 10 seconds */) { this.lastStart = Date.now(); startService = false; + this.hasServerFatallyCrashedTooManyTimes = true; prompt = vscode.window.showErrorMessage( localize('serverDiedAfterStart', 'The TypeScript language service died 5 times right after it got started. The service will not be restarted.'), { From 31f757f2547560bc85ba40c1831864e4c1d30d17 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Mon, 24 Feb 2020 15:26:55 -0800 Subject: [PATCH 105/200] debug: update js-debug-nightly to "2020.2.2117" @ 2020-02-22T01:07:51.383Z --- build/builtInExtensions.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/builtInExtensions.json b/build/builtInExtensions.json index a9a0386d1d1..e93b6dfbab6 100644 --- a/build/builtInExtensions.json +++ b/build/builtInExtensions.json @@ -46,7 +46,7 @@ }, { "name": "ms-vscode.js-debug-nightly", - "version": "2020.2.1417", + "version": "2020.2.2117", "forQualities": [ "insider" ], From a10e4cb91118858296b477b771a73923a4040034 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 24 Feb 2020 15:53:08 -0800 Subject: [PATCH 106/200] Make sure we set the correct replacement range for dot member completions Fixes #91105 --- .../src/features/completions.ts | 28 +++++++++++++------ .../src/test/completions.test.ts | 25 +++++++++++++++++ 2 files changed, 45 insertions(+), 8 deletions(-) diff --git a/extensions/typescript-language-features/src/features/completions.ts b/extensions/typescript-language-features/src/features/completions.ts index 52e82adf2a8..e2f8ba9fd37 100644 --- a/extensions/typescript-language-features/src/features/completions.ts +++ b/extensions/typescript-language-features/src/features/completions.ts @@ -83,7 +83,15 @@ class MyCompletionItem extends vscode.CompletionItem { if (completionContext.isMemberCompletion && completionContext.dotAccessorContext) { this.filterText = completionContext.dotAccessorContext.text + (this.insertText || this.label); if (!this.range) { - this.range = completionContext.dotAccessorContext.range; + const replacementRange = this.getReplaceRange(line); + if (replacementRange) { + this.range = { + inserting: completionContext.dotAccessorContext.range, + replacing: completionContext.dotAccessorContext.range.union(replacementRange), + }; + } else { + this.range = completionContext.dotAccessorContext.range; + } this.insertText = this.filterText; } } @@ -135,7 +143,6 @@ class MyCompletionItem extends vscode.CompletionItem { } else { return wordStart === '#' ? undefined : this.tsEntry.name.replace(/^#/, ''); } - return undefined; } // For `this.` completions, generally don't set the filter text since we don't want them to be overly prioritized. #74164 @@ -162,6 +169,16 @@ class MyCompletionItem extends vscode.CompletionItem { return; } + const replaceRange = this.getReplaceRange(line); + if (replaceRange) { + this.range = { + inserting: new vscode.Range(replaceRange.start, this.position), + replacing: replaceRange + }; + } + } + + private getReplaceRange(line: string) { const wordRange = this.document.getWordRangeAtPosition(this.position); let replaceRange = wordRange; @@ -177,12 +194,7 @@ class MyCompletionItem extends vscode.CompletionItem { } } - if (replaceRange) { - this.range = { - inserting: new vscode.Range(replaceRange.start, this.position), - replacing: replaceRange - }; - } + return replaceRange; } private static convertKind(kind: string): vscode.CompletionItemKind { diff --git a/extensions/typescript-language-features/src/test/completions.test.ts b/extensions/typescript-language-features/src/test/completions.test.ts index 20afd43678e..0c928fb4a39 100644 --- a/extensions/typescript-language-features/src/test/completions.test.ts +++ b/extensions/typescript-language-features/src/test/completions.test.ts @@ -622,4 +622,29 @@ suite('TypeScript Completions', () => { `Config: ${config}`); }); }); + + test('Replace should work after this. (#91105)', async () => { + await updateConfig(testDocumentUri, { [Config.insertMode]: 'replace' }); + + const editor = await createTestEditor(testDocumentUri, + `class A {`, + ` abc = 1`, + ` foo() {`, + ` this.$0abc`, + ` }`, + `}`, + ); + + await acceptFirstSuggestion(testDocumentUri, _disposables); + + assertEditorContents(editor, + joinLines( + `class A {`, + ` abc = 1`, + ` foo() {`, + ` this.abc`, + ` }`, + `}`, + )); + }); }); From 915089a8aaa25ba666fa731c161d7a262acd5b1b Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 24 Feb 2020 15:58:47 -0800 Subject: [PATCH 107/200] Fix silly error with toString for custom editors Fixes #91317 --- src/vs/workbench/api/common/extHostWebview.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/api/common/extHostWebview.ts b/src/vs/workbench/api/common/extHostWebview.ts index fb648338fe6..a898dbba4fc 100644 --- a/src/vs/workbench/api/common/extHostWebview.ts +++ b/src/vs/workbench/api/common/extHostWebview.ts @@ -389,7 +389,7 @@ class WebviewDocumentStore { } private key(viewType: string, resource: vscode.Uri): string { - return `${viewType}@@@${resource.toString}`; + return `${viewType}@@@${resource}`; } } From 838d34a996fea6e979231f52ec938c42e22a8f7b Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Mon, 24 Feb 2020 18:26:45 -0500 Subject: [PATCH 108/200] Fixes timeline message z-index --- src/vs/workbench/contrib/timeline/browser/media/timelinePane.css | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/timeline/browser/media/timelinePane.css b/src/vs/workbench/contrib/timeline/browser/media/timelinePane.css index ad6200c6561..3b036147cfc 100644 --- a/src/vs/workbench/contrib/timeline/browser/media/timelinePane.css +++ b/src/vs/workbench/contrib/timeline/browser/media/timelinePane.css @@ -12,6 +12,7 @@ opacity: 0.5; position: absolute; pointer-events: none; + z-index: 1; } .timeline-tree-view .monaco-list .monaco-list-row .custom-view-tree-node-item .monaco-icon-label { From 0a8ffc6bcbde9d156f130ecc4943f1309dc056f5 Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Mon, 24 Feb 2020 18:29:54 -0500 Subject: [PATCH 109/200] Enables timeline view for insiders & stable The timeline.showView setting is on by default for insiders --- .../contrib/timeline/browser/timeline.contribution.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/timeline/browser/timeline.contribution.ts b/src/vs/workbench/contrib/timeline/browser/timeline.contribution.ts index 11cae1ddb07..be0e55d8f2b 100644 --- a/src/vs/workbench/contrib/timeline/browser/timeline.contribution.ts +++ b/src/vs/workbench/contrib/timeline/browser/timeline.contribution.ts @@ -44,15 +44,12 @@ configurationRegistry.registerConfiguration({ 'timeline.showView': { type: 'boolean', description: localize('timeline.showView', "Experimental: When enabled, shows a Timeline view in the Explorer sidebar."), - default: false //product.quality !== 'stable' + default: product.quality !== 'stable' }, } }); -if (product.quality !== 'stable') { - Registry.as(ViewExtensions.ViewsRegistry).registerViews([new TimelinePaneDescriptor()], VIEW_CONTAINER); -} - +Registry.as(ViewExtensions.ViewsRegistry).registerViews([new TimelinePaneDescriptor()], VIEW_CONTAINER); namespace TimelineViewRefreshAction { From b78a012ae3bb77cb6fa5e42ab53a86ea5841f852 Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Mon, 24 Feb 2020 18:30:35 -0500 Subject: [PATCH 110/200] Adds timeline.excludeSources setting Allows the filtering of timeline sources from the Timeline view --- .../timeline/browser/timeline.contribution.ts | 5 ++++ .../contrib/timeline/browser/timelinePane.ts | 27 ++++++++++++++++--- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/timeline/browser/timeline.contribution.ts b/src/vs/workbench/contrib/timeline/browser/timeline.contribution.ts index be0e55d8f2b..b3958cb47c7 100644 --- a/src/vs/workbench/contrib/timeline/browser/timeline.contribution.ts +++ b/src/vs/workbench/contrib/timeline/browser/timeline.contribution.ts @@ -46,6 +46,11 @@ configurationRegistry.registerConfiguration({ description: localize('timeline.showView', "Experimental: When enabled, shows a Timeline view in the Explorer sidebar."), default: product.quality !== 'stable' }, + 'timeline.excludeSources': { + type: 'array', + description: localize('timeline.excludeSources', "Experimental: An array of Timeline sources that should be excluded from the Timeline view"), + default: null + }, } }); diff --git a/src/vs/workbench/contrib/timeline/browser/timelinePane.ts b/src/vs/workbench/contrib/timeline/browser/timelinePane.ts index 9e322c2fcd9..d178b1d98cd 100644 --- a/src/vs/workbench/contrib/timeline/browser/timelinePane.ts +++ b/src/vs/workbench/contrib/timeline/browser/timelinePane.ts @@ -19,7 +19,7 @@ import { TreeResourceNavigator, WorkbenchObjectTree } from 'vs/platform/list/bro import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ITimelineService, TimelineChangeEvent, TimelineItem, TimelineOptions, TimelineProvidersChangeEvent, TimelineRequest, Timeline } from 'vs/workbench/contrib/timeline/common/timeline'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -99,7 +99,7 @@ export class TimelinePane extends ViewPane { private _menus: TimelineMenus; private _visibilityDisposables: DisposableStore | undefined; - // private _excludedSources: Set | undefined; + private _excludedSources: Set; private _cursorsByProvider: Map = new Map(); private _items: { element: TreeElement }[] = []; private _loadingMessageTimer: any | undefined; @@ -128,6 +128,18 @@ export class TimelinePane extends ViewPane { const scopedContextKeyService = this._register(this.contextKeyService.createScoped()); scopedContextKeyService.createKey('view', TimelinePane.ID); + + this._excludedSources = new Set(configurationService.getValue('timeline.excludeSources')); + configurationService.onDidChangeConfiguration(this.onConfigurationChanged, this); + } + + private onConfigurationChanged(e: IConfigurationChangeEvent) { + if (!e.affectsConfiguration('timeline.excludeSources')) { + return; + } + + this._excludedSources = new Set(this.configurationService.getValue('timeline.excludeSources')); + this.loadTimeline(true); } private onActiveEditorChanged() { @@ -250,6 +262,15 @@ export class TimelinePane extends ViewPane { return; } + const filteredSources = (sources ?? this.timelineService.getSources()).filter(s => !this._excludedSources.has(s)); + if (filteredSources.length === 0) { + if (reset) { + this.refresh(); + } + + return; + } + let lastIndex = this._items.length - 1; let lastItem = this._items[lastIndex]?.element; if (isLoadMoreCommandItem(lastItem)) { @@ -265,7 +286,7 @@ export class TimelinePane extends ViewPane { } } - for (const source of sources ?? this.timelineService.getSources()) { + for (const source of filteredSources) { let request = this._pendingRequests.get(source); const cursors = this._cursorsByProvider.get(source); From 2c9ce783fb3c696e5f633b8201d89349b58df7d2 Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Mon, 24 Feb 2020 18:35:58 -0500 Subject: [PATCH 111/200] Fixes issue where no timeline message wasn't shown --- src/vs/workbench/contrib/timeline/browser/timelinePane.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/vs/workbench/contrib/timeline/browser/timelinePane.ts b/src/vs/workbench/contrib/timeline/browser/timelinePane.ts index d178b1d98cd..62e956ab2fb 100644 --- a/src/vs/workbench/contrib/timeline/browser/timelinePane.ts +++ b/src/vs/workbench/contrib/timeline/browser/timelinePane.ts @@ -390,6 +390,11 @@ export class TimelinePane extends ViewPane { } if (!changed) { + // If there are no items at all and no pending requests, make sure to refresh (to show the no timeline info message) + if (this._items.length === 0 && this._pendingRequests.size === 0) { + this.refresh(); + } + return; } From 8dfc759232a200188dc27da9915817220d4418b6 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 24 Feb 2020 18:00:02 -0800 Subject: [PATCH 112/200] Run OSS tool for v1.43 --- ThirdPartyNotices.txt | 10 +++++----- cglicenses.json | 8 ++++++++ package.json | 2 +- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/ThirdPartyNotices.txt b/ThirdPartyNotices.txt index 7bd723dcbff..7eaccc2a893 100644 --- a/ThirdPartyNotices.txt +++ b/ThirdPartyNotices.txt @@ -23,17 +23,17 @@ This project incorporates components from the projects listed below. The origina 16. fadeevab/make.tmbundle (https://github.com/fadeevab/make.tmbundle) 17. freebroccolo/atom-language-swift (https://github.com/freebroccolo/atom-language-swift) 18. HTML 5.1 W3C Working Draft version 08 October 2015 (http://www.w3.org/TR/2015/WD-html51-20151008/) -19. Ikuyadeu/vscode-R version 0.5.5 (https://github.com/Ikuyadeu/vscode-R) +19. Ikuyadeu/vscode-R version 1.1.8 (https://github.com/Ikuyadeu/vscode-R) 20. insane version 2.6.2 (https://github.com/bevacqua/insane) 21. Ionic documentation version 1.2.4 (https://github.com/ionic-team/ionic-site) 22. ionide/ionide-fsgrammar (https://github.com/ionide/ionide-fsgrammar) 23. jeff-hykin/cpp-textmate-grammar version 1.12.11 (https://github.com/jeff-hykin/cpp-textmate-grammar) -24. jeff-hykin/cpp-textmate-grammar version 1.14.15 (https://github.com/jeff-hykin/cpp-textmate-grammar) +24. jeff-hykin/cpp-textmate-grammar version 1.14.20 (https://github.com/jeff-hykin/cpp-textmate-grammar) 25. js-beautify version 1.6.8 (https://github.com/beautify-web/js-beautify) 26. Jxck/assert version 1.0.0 (https://github.com/Jxck/assert) 27. language-docker (https://github.com/moby/moby) 28. language-less version 0.34.2 (https://github.com/atom/language-less) -29. language-php version 0.44.3 (https://github.com/atom/language-php) +29. language-php version 0.44.4 (https://github.com/atom/language-php) 30. language-rust version 0.4.12 (https://github.com/zargony/atom-language-rust) 31. MagicStack/MagicPython version 1.1.1 (https://github.com/MagicStack/MagicPython) 32. marked version 0.6.2 (https://github.com/markedjs/marked) @@ -64,7 +64,7 @@ This project incorporates components from the projects listed below. The origina 57. TypeScript-TmLanguage version 1.0.0 (https://github.com/Microsoft/TypeScript-TmLanguage) 58. Unicode version 12.0.0 (http://www.unicode.org/) 59. vscode-codicons version 0.0.1 (https://github.com/microsoft/vscode-codicons) -60. vscode-logfile-highlighter version 2.5.0 (https://github.com/emilast/vscode-logfile-highlighter) +60. vscode-logfile-highlighter version 2.6.0 (https://github.com/emilast/vscode-logfile-highlighter) 61. vscode-swift version 0.0.1 (https://github.com/owensd/vscode-swift) 62. Web Background Synchronization (https://github.com/WICG/BackgroundSync) @@ -2807,4 +2807,4 @@ Apache License See the License for the specific language governing permissions and limitations under the License. ========================================= -END OF Web Background Synchronization NOTICES AND INFORMATION +END OF Web Background Synchronization NOTICES AND INFORMATION \ No newline at end of file diff --git a/cglicenses.json b/cglicenses.json index 1e9287cbaf2..76e6fe49f00 100644 --- a/cglicenses.json +++ b/cglicenses.json @@ -81,5 +81,13 @@ "prependLicenseText": [ "Copyright (c) Microsoft Corporation. All rights reserved." ] + }, + { + // Reason: The license at https://github.com/rbuckton/reflect-metadata/blob/master/LICENSE + // does not include a clear Copyright statement (it's in https://github.com/rbuckton/reflect-metadata/blob/master/CopyrightNotice.txt). + "name": "reflect-metadata", + "prependLicenseText": [ + "Copyright (c) Microsoft Corporation. All rights reserved." + ] } ] diff --git a/package.json b/package.json index 55e4247dbb9..7910be5d9ae 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.43.0", - "distro": "3034776cbf7d76660423c2709c847d87a0d5c6c1", + "distro": "d9ed2bff7e779c71264b0c08311d08192924f473", "author": { "name": "Microsoft Corporation" }, From 5e80bf449c995aa32a59254c0ff845d37da11b70 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Mon, 24 Feb 2020 18:03:54 -0800 Subject: [PATCH 113/200] add keyboard ability to move focused view --- .../browser/actions/layoutActions.ts | 93 ++++++++++++++++++- 1 file changed, 91 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/actions/layoutActions.ts b/src/vs/workbench/browser/actions/layoutActions.ts index 73f89d7a850..50906972e6c 100644 --- a/src/vs/workbench/browser/actions/layoutActions.ts +++ b/src/vs/workbench/browser/actions/layoutActions.ts @@ -21,10 +21,13 @@ import { isWindows, isLinux, isWeb } from 'vs/base/common/platform'; import { IsMacNativeContext } from 'vs/workbench/browser/contextkeys'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { InEditorZenModeContext, IsCenteredLayoutContext, EditorAreaVisibleContext } from 'vs/workbench/common/editor'; -import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { SideBarVisibleContext } from 'vs/workbench/common/viewlet'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { IViewDescriptorService, IViewContainersRegistry, Extensions as ViewContainerExtensions } from 'vs/workbench/common/views'; +import { IViewDescriptorService, IViewContainersRegistry, Extensions as ViewContainerExtensions, IViewsService, FocusedViewContext, ViewContainerLocation } from 'vs/workbench/common/views'; +import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; const registry = Registry.as(WorkbenchExtensions.WorkbenchActions); const viewCategory = nls.localize('view', "View"); @@ -518,6 +521,92 @@ export class ResetViewLocationsAction extends Action { registry.registerWorkbenchAction(SyncActionDescriptor.create(ResetViewLocationsAction, ResetViewLocationsAction.ID, ResetViewLocationsAction.LABEL), 'View: Reset View Locations', viewCategory); +// --- Move View with Command +export class MoveFocusedViewAction extends Action { + static readonly ID = 'workbench.action.moveFocusedView'; + static readonly LABEL = nls.localize('moveFocusedView', "Move Focused View"); + + constructor( + id: string, + label: string, + @IViewDescriptorService private viewDescriptorService: IViewDescriptorService, + @IViewsService private viewsService: IViewsService, + @IQuickInputService private quickInputService: IQuickInputService, + @IContextKeyService private contextKeyService: IContextKeyService, + @INotificationService private notificationService: INotificationService, + @IViewletService private viewletService: IViewletService + ) { + super(id, label); + } + + run(): Promise { + const viewContainerRegistry = Registry.as(ViewContainerExtensions.ViewContainersRegistry); + + const focusedView = FocusedViewContext.getValue(this.contextKeyService); + + if (focusedView === undefined || focusedView.trim() === '') { + this.notificationService.error(nls.localize('moveFocusedView.error.noFocusedView', "There is no view currently focused.")); + return Promise.resolve(); + } + + const viewDescriptor = this.viewDescriptorService.getViewDescriptor(focusedView); + if (!viewDescriptor || !viewDescriptor.canMoveView) { + this.notificationService.error(nls.localize('moveFocusedView.error.nonMovableView', "The currently focused view is not movable {0}.", focusedView)); + return Promise.resolve(); + } + + const quickPick = this.quickInputService.createQuickPick(); + quickPick.placeholder = nls.localize('moveFocusedView.selectDestination', "Select a destination area for the view..."); + quickPick.autoFocusOnList = true; + + quickPick.items = [ + { + id: 'sidebar', + label: nls.localize('sidebar', "Sidebar") + }, + { + id: 'panel', + label: nls.localize('panel', "Panel") + } + ]; + + quickPick.onDidAccept(() => { + const destination = quickPick.selectedItems[0]; + + if (destination.id === 'panel') { + quickPick.hide(); + this.viewDescriptorService.moveViewToLocation(viewDescriptor!, ViewContainerLocation.Panel); + this.viewsService.openView(focusedView, true); + + return; + } else if (destination.id === 'sidebar') { + quickPick.placeholder = nls.localize('moveFocusedView.selectDestinationContainer', "Select a destination view group..."); + quickPick.items = this.viewletService.getViewlets().map(viewlet => { + return { + id: viewlet.id, + label: viewlet.name + }; + }); + + return; + } else if (destination.id) { + quickPick.hide(); + this.viewDescriptorService.moveViewsToContainer([viewDescriptor], viewContainerRegistry.get(destination.id)!); + this.viewsService.openView(focusedView, true); + return; + } + + quickPick.hide(); + }); + + quickPick.show(); + + return Promise.resolve(); + } +} + +registry.registerWorkbenchAction(SyncActionDescriptor.create(MoveFocusedViewAction, MoveFocusedViewAction.ID, MoveFocusedViewAction.LABEL), 'View: Move Focused View', viewCategory); + // --- Resize View From 89cafb24e5d75a0c8d561a6cdd95e93b5679e152 Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Mon, 24 Feb 2020 22:56:37 -0500 Subject: [PATCH 114/200] Localizes timeline strings --- extensions/git/src/commands.ts | 20 ++-- extensions/git/src/repository.ts | 44 +++---- extensions/git/src/timelineProvider.ts | 67 +++-------- src/vs/base/common/date.ts | 109 ++++++++++++++---- .../contrib/timeline/browser/timelinePane.ts | 12 +- 5 files changed, 138 insertions(+), 114 deletions(-) diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 2cd44bc0f05..ad4d47485e7 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -430,25 +430,25 @@ export class CommandCenter { case Status.INDEX_MODIFIED: case Status.INDEX_RENAMED: case Status.INDEX_ADDED: - return `${basename} (Index)`; + return localize('git.title.index', '{0} (Index)', basename); case Status.MODIFIED: case Status.BOTH_ADDED: case Status.BOTH_MODIFIED: - return `${basename} (Working Tree)`; + return localize('git.title.workingTree', '{0} (Working Tree)', basename); case Status.DELETED_BY_US: - return `${basename} (Theirs)`; + return localize('git.title.theirs', '{0} (Theirs)', basename); case Status.DELETED_BY_THEM: - return `${basename} (Ours)`; + return localize('git.title.ours', '{0} (Ours)', basename); case Status.UNTRACKED: + return localize('git.title.untracked', '{0} (Untracked)', basename); - return `${basename} (Untracked)`; + default: + return ''; } - - return ''; } @command('git.clone') @@ -2348,12 +2348,12 @@ export class CommandCenter { let title; if ((item.previousRef === 'HEAD' || item.previousRef === '~') && item.ref === '') { - title = `${basename} (Working Tree)`; + title = localize('git.title.workingTree', '{0} (Working Tree)', basename); } else if (item.previousRef === 'HEAD' && item.ref === '~') { - title = `${basename} (Index)`; + title = localize('git.title.index', '{0} (Index)', basename); } else { - title = `${basename} (${item.shortPreviousRef}) \u27f7 ${basename} (${item.shortRef})`; + title = localize('git.title.diffRefs', '{0} ({1}) \u27f7 {0} ({2})', basename, item.shortPreviousRef, item.shortRef); } return commands.executeCommand('vscode.diff', toGitUri(uri, item.previousRef), item.ref === '' ? uri : toGitUri(uri, item.ref), title); diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 4f22b0784bb..4f1d21796ab 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -40,6 +40,29 @@ export const enum ResourceGroupType { export class Resource implements SourceControlResourceState { + static getStatusText(type: Status) { + switch (type) { + case Status.INDEX_MODIFIED: return localize('index modified', "Index Modified"); + case Status.MODIFIED: return localize('modified', "Modified"); + case Status.INDEX_ADDED: return localize('index added', "Index Added"); + case Status.INDEX_DELETED: return localize('index deleted', "Index Deleted"); + case Status.DELETED: return localize('deleted', "Deleted"); + case Status.INDEX_RENAMED: return localize('index renamed', "Index Renamed"); + case Status.INDEX_COPIED: return localize('index copied', "Index Copied"); + case Status.UNTRACKED: return localize('untracked', "Untracked"); + case Status.IGNORED: return localize('ignored', "Ignored"); + case Status.INTENT_TO_ADD: return localize('intent to add', "Intent to Add"); + case Status.BOTH_DELETED: return localize('both deleted', "Both Deleted"); + case Status.ADDED_BY_US: return localize('added by us', "Added By Us"); + case Status.DELETED_BY_THEM: return localize('deleted by them', "Deleted By Them"); + case Status.ADDED_BY_THEM: return localize('added by them', "Added By Them"); + case Status.DELETED_BY_US: return localize('deleted by us', "Deleted By Us"); + case Status.BOTH_ADDED: return localize('both added', "Both Added"); + case Status.BOTH_MODIFIED: return localize('both modified', "Both Modified"); + default: return ''; + } + } + @memoize get resourceUri(): Uri { if (this.renameResourceUri && (this._type === Status.MODIFIED || this._type === Status.DELETED || this._type === Status.INDEX_RENAMED || this._type === Status.INDEX_COPIED)) { @@ -110,26 +133,7 @@ export class Resource implements SourceControlResourceState { } private get tooltip(): string { - switch (this.type) { - case Status.INDEX_MODIFIED: return localize('index modified', "Index Modified"); - case Status.MODIFIED: return localize('modified', "Modified"); - case Status.INDEX_ADDED: return localize('index added', "Index Added"); - case Status.INDEX_DELETED: return localize('index deleted', "Index Deleted"); - case Status.DELETED: return localize('deleted', "Deleted"); - case Status.INDEX_RENAMED: return localize('index renamed', "Index Renamed"); - case Status.INDEX_COPIED: return localize('index copied', "Index Copied"); - case Status.UNTRACKED: return localize('untracked', "Untracked"); - case Status.IGNORED: return localize('ignored', "Ignored"); - case Status.INTENT_TO_ADD: return localize('intent to add', "Intent to Add"); - case Status.BOTH_DELETED: return localize('both deleted', "Both Deleted"); - case Status.ADDED_BY_US: return localize('added by us', "Added By Us"); - case Status.DELETED_BY_THEM: return localize('deleted by them', "Deleted By Them"); - case Status.ADDED_BY_THEM: return localize('added by them', "Added By Them"); - case Status.DELETED_BY_US: return localize('deleted by us', "Deleted By Us"); - case Status.BOTH_ADDED: return localize('both added', "Both Added"); - case Status.BOTH_MODIFIED: return localize('both modified', "Both Modified"); - default: return ''; - } + return Resource.getStatusText(this.type); } private get strikeThrough(): boolean { diff --git a/extensions/git/src/timelineProvider.ts b/extensions/git/src/timelineProvider.ts index f0a95d61a34..3fe8e306c95 100644 --- a/extensions/git/src/timelineProvider.ts +++ b/extensions/git/src/timelineProvider.ts @@ -3,17 +3,18 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as nls from 'vscode-nls'; import * as dayjs from 'dayjs'; import * as advancedFormat from 'dayjs/plugin/advancedFormat'; import { CancellationToken, Disposable, Event, EventEmitter, ThemeIcon, Timeline, TimelineChangeEvent, TimelineItem, TimelineOptions, TimelineProvider, Uri, workspace } from 'vscode'; import { Model } from './model'; -import { Repository } from './repository'; +import { Repository, Resource } from './repository'; import { debounce } from './decorators'; -import { Status } from './api/git'; dayjs.extend(advancedFormat); -// TODO[ECA]: Localize all the strings +const localize = nls.loadMessageBundle(); + // TODO[ECA]: Localize or use a setting for date format export class GitTimelineItem extends TimelineItem { @@ -68,7 +69,7 @@ export class GitTimelineProvider implements TimelineProvider { } readonly id = 'git-history'; - readonly label = 'Git History'; + readonly label = localize('git.timeline.source', 'Git History'); private _disposable: Disposable; @@ -171,38 +172,18 @@ export class GitTimelineProvider implements TimelineProvider { }); if (options.cursor === undefined || options.before) { + const you = localize('git.timeline.you', 'You'); + const index = repo.indexGroup.resourceStates.find(r => r.resourceUri.fsPath === uri.fsPath); if (index) { const date = this._repoStatusDate ?? new Date(); dateFormatter = dayjs(date); - let status; - switch (index.type) { - case Status.INDEX_MODIFIED: - status = 'Modified'; - break; - case Status.INDEX_ADDED: - status = 'Added'; - break; - case Status.INDEX_DELETED: - status = 'Deleted'; - break; - case Status.INDEX_RENAMED: - status = 'Renamed'; - break; - case Status.INDEX_COPIED: - status = 'Copied'; - break; - default: - status = ''; - break; - } - - const item = new GitTimelineItem('~', 'HEAD', 'Staged Changes', date.getTime(), 'index', 'git:file:index'); + const item = new GitTimelineItem('~', 'HEAD', localize('git.timeline.stagedChanges', 'Staged Changes'), date.getTime(), 'index', 'git:file:index'); // TODO[ECA]: Replace with a better icon -- reflecting its status maybe? item.iconPath = new (ThemeIcon as any)('git-commit'); - item.description = 'You'; - item.detail = `You \u2014 Index\n${dateFormatter.format('MMMM Do, YYYY h:mma')}\n${status}`; + item.description = you; + item.detail = localize('git.timeline.detail', '{0} \u2014 {1}\n{2}\n\n{3}', you, localize('git.index', 'Index'), dateFormatter.format('MMMM Do, YYYY h:mma'), Resource.getStatusText(index.type)); item.command = { title: 'Open Comparison', command: 'git.timeline.openDiff', @@ -217,33 +198,11 @@ export class GitTimelineProvider implements TimelineProvider { const date = new Date(); dateFormatter = dayjs(date); - let status; - switch (working.type) { - case Status.INDEX_MODIFIED: - status = 'Modified'; - break; - case Status.INDEX_ADDED: - status = 'Added'; - break; - case Status.INDEX_DELETED: - status = 'Deleted'; - break; - case Status.INDEX_RENAMED: - status = 'Renamed'; - break; - case Status.INDEX_COPIED: - status = 'Copied'; - break; - default: - status = ''; - break; - } - - const item = new GitTimelineItem('', index ? '~' : 'HEAD', 'Uncommited Changes', date.getTime(), 'working', 'git:file:working'); + const item = new GitTimelineItem('', index ? '~' : 'HEAD', localize('git.timeline.uncommitedChanges', 'Uncommited Changes'), date.getTime(), 'working', 'git:file:working'); // TODO[ECA]: Replace with a better icon -- reflecting its status maybe? item.iconPath = new (ThemeIcon as any)('git-commit'); - item.description = 'You'; - item.detail = `You \u2014 Working Tree\n${dateFormatter.format('MMMM Do, YYYY h:mma')}\n${status}`; + item.description = you; + item.detail = localize('git.timeline.detail', '{0} \u2014 {1}\n{2}\n\n{3}', you, localize('git.workingTree', 'Working Tree'), dateFormatter.format('MMMM Do, YYYY h:mma'), Resource.getStatusText(working.type)); item.command = { title: 'Open Comparison', command: 'git.timeline.openDiff', diff --git a/src/vs/base/common/date.ts b/src/vs/base/common/date.ts index ec970c13103..4655bc663ee 100644 --- a/src/vs/base/common/date.ts +++ b/src/vs/base/common/date.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { pad } from './strings'; +import { localize } from 'vs/nls'; const minute = 60; const hour = minute * 60; @@ -12,7 +13,6 @@ const week = day * 7; const month = day * 30; const year = day * 365; -// TODO[ECA]: Localize strings export function fromNow(date: number | Date, appendAgoLabel?: boolean): string { if (typeof date !== 'number') { date = date.getTime(); @@ -20,36 +20,99 @@ export function fromNow(date: number | Date, appendAgoLabel?: boolean): string { const seconds = Math.round((new Date().getTime() - date) / 1000); if (seconds < 30) { - return 'now'; + return localize('date.fromNow.now', 'now'); } let value: number; - let unit: string; if (seconds < minute) { value = seconds; - unit = 'sec'; - } else if (seconds < hour) { - value = Math.floor(seconds / minute); - unit = 'min'; - } else if (seconds < day) { - value = Math.floor(seconds / hour); - unit = 'hr'; - } else if (seconds < week) { - value = Math.floor(seconds / day); - unit = 'day'; - } else if (seconds < month) { - value = Math.floor(seconds / week); - unit = 'wk'; - } else if (seconds < year) { - value = Math.floor(seconds / month); - unit = 'mo'; - } else { - value = Math.floor(seconds / year); - unit = 'yr'; + + if (appendAgoLabel) { + return value === 1 + ? localize('date.fromNow.seconds.singular.ago', '{0} sec ago', value) + : localize('date.fromNow.seconds.plural.ago', '{0} secs ago', value); + } else { + return value === 1 + ? localize('date.fromNow.seconds.singular', '{0} sec', value) + : localize('date.fromNow.seconds.plural', '{0} secs', value); + } } - return `${value} ${unit}${value === 1 ? '' : 's'}${appendAgoLabel ? ' ago' : ''}`; + if (seconds < hour) { + value = Math.floor(seconds / minute); + if (appendAgoLabel) { + return value === 1 + ? localize('date.fromNow.minutes.singular.ago', '{0} min ago', value) + : localize('date.fromNow.minutes.plural.ago', '{0} mins ago', value); + } else { + return value === 1 + ? localize('date.fromNow.minutes.singular', '{0} min', value) + : localize('date.fromNow.minutes.plural', '{0} mins', value); + } + } + if (seconds < day) { + value = Math.floor(seconds / hour); + if (appendAgoLabel) { + return value === 1 + ? localize('date.fromNow.hours.singular.ago', '{0} hr ago', value) + : localize('date.fromNow.hours.plural.ago', '{0} hrs ago', value); + } else { + return value === 1 + ? localize('date.fromNow.hours.singular', '{0} hr', value) + : localize('date.fromNow.hours.plural', '{0} hrs', value); + } + } + + if (seconds < week) { + value = Math.floor(seconds / day); + if (appendAgoLabel) { + return value === 1 + ? localize('date.fromNow.days.singular.ago', '{0} day ago', value) + : localize('date.fromNow.days.plural.ago', '{0} days ago', value); + } else { + return value === 1 + ? localize('date.fromNow.days.singular', '{0} day', value) + : localize('date.fromNow.days.plural', '{0} days', value); + } + } + + if (seconds < month) { + value = Math.floor(seconds / week); + if (appendAgoLabel) { + return value === 1 + ? localize('date.fromNow.weeks.singular.ago', '{0} wk ago', value) + : localize('date.fromNow.weeks.plural.ago', '{0} wks ago', value); + } else { + return value === 1 + ? localize('date.fromNow.weeks.singular', '{0} wk', value) + : localize('date.fromNow.weeks.plural', '{0} wks', value); + } + } + + if (seconds < year) { + value = Math.floor(seconds / month); + if (appendAgoLabel) { + return value === 1 + ? localize('date.fromNow.months.singular.ago', '{0} mo ago', value) + : localize('date.fromNow.months.plural.ago', '{0} mos ago', value); + } else { + return value === 1 + ? localize('date.fromNow.months.singular', '{0} mo', value) + : localize('date.fromNow.months.plural', '{0} mos', value); + } + } + + value = Math.floor(seconds / year); + if (appendAgoLabel) { + return value === 1 + ? localize('date.fromNow.years.singular.ago', '{0} yr ago', value) + : localize('date.fromNow.years.plural.ago', '{0} yrs ago', value); + } else { + return value === 1 + ? localize('date.fromNow.years.singular', '{0} yr', value) + : localize('date.fromNow.years.plural', '{0} yrs', value); + } } export function toLocalISOString(date: Date): string { diff --git a/src/vs/workbench/contrib/timeline/browser/timelinePane.ts b/src/vs/workbench/contrib/timeline/browser/timelinePane.ts index 62e956ab2fb..d438a1b739c 100644 --- a/src/vs/workbench/contrib/timeline/browser/timelinePane.ts +++ b/src/vs/workbench/contrib/timeline/browser/timelinePane.ts @@ -38,8 +38,6 @@ import { MenuItemAction, IMenuService, MenuId } from 'vs/platform/actions/common import { fromNow } from 'vs/base/common/date'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -// TODO[ECA]: Localize all the strings - const InitialPageSize = 20; const SubsequentPageSize = 40; @@ -240,7 +238,7 @@ export class TimelinePane extends ViewPane { // TODO[ECA]: Are these the right the list of schemes to exclude? Is there a better way? if (this._uri && (this._uri.scheme === 'vscode-settings' || this._uri.scheme === 'webview-panel' || this._uri.scheme === 'walkThrough')) { - this.message = 'The active editor cannot provide timeline information.'; + this.message = localize('timeline.editorCannotProvideTimeline', 'The active editor cannot provide timeline information.'); this._tree.setChildren(null, undefined); return; @@ -253,7 +251,7 @@ export class TimelinePane extends ViewPane { } this._tree.setChildren(null, undefined); - this.message = `Loading timeline for ${basename(uri.fsPath)}...`; + this.message = localize('timeline.loading', 'Loading timeline for ${0}...', basename(uri.fsPath)); }, 500, this._uri); } } @@ -410,7 +408,7 @@ export class TimelinePane extends ViewPane { this._items.push({ element: { handle: 'vscode-command:loadMore', - label: 'Load more', + label: localize('timeline.loadMore', 'Load more'), timestamp: 0 } as CommandItem }); @@ -512,7 +510,7 @@ export class TimelinePane extends ViewPane { } if (this._items.length === 0) { - this.message = 'No timeline information was provided.'; + this.message = localize('timeline.noTimelineInfo', 'No timeline information was provided.'); } else { this.message = undefined; } @@ -556,7 +554,7 @@ export class TimelinePane extends ViewPane { this._messageElement = DOM.append(this._container, DOM.$('.message')); DOM.addClass(this._messageElement, 'timeline-subtle'); - this.message = 'The active editor cannot provide timeline information.'; + this.message = localize('timeline.editorCannotProvideTimeline', 'The active editor cannot provide timeline information.'); this._treeElement = document.createElement('div'); DOM.addClasses(this._treeElement, 'customview-tree', 'file-icon-themable-tree', 'hide-arrows'); From 96b4cfa0f13f0427d3119eaada329d5dbbbde2a1 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 25 Feb 2020 13:28:06 +0100 Subject: [PATCH 115/200] remove leftovers --- src/vs/workbench/contrib/scm/browser/repositoryPane.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/repositoryPane.ts b/src/vs/workbench/contrib/scm/browser/repositoryPane.ts index 69b9619c90a..e13d4f79beb 100644 --- a/src/vs/workbench/contrib/scm/browser/repositoryPane.ts +++ b/src/vs/workbench/contrib/scm/browser/repositoryPane.ts @@ -621,8 +621,6 @@ export class RepositoryPane extends ViewPane { protected contextKeyService: IContextKeyService; private commitTemplate = ''; - shouldShowWelcome() { return true; } - constructor( readonly repository: ISCMRepository, options: IViewPaneOptions, From bcfaafb1ce960ee2eee78a4ed4d8f0e4118ef31c Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 25 Feb 2020 14:04:23 +0100 Subject: [PATCH 116/200] editors - fix issue with revert not closing editors if opened multiple times --- src/vs/workbench/services/editor/browser/editorService.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index 2583458a30d..0b97fbf1218 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -1034,6 +1034,10 @@ export class EditorService extends Disposable implements EditorServiceImpl { return true; // might have been disposed from from the revert already } + if (!editor.isDirty() && !options?.force) { + return true; // skip over non-dirty editors unless we force to revert + } + // Use revert as a hint to pin the editor this.editorGroupService.getGroup(groupId)?.pinEditor(editor); From 8a172c196d53b890fc3dbe52d5d90f517db8e9ec Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 25 Feb 2020 15:29:10 +0100 Subject: [PATCH 117/200] editors - improve closeAll action to ignore save/revert result and rather check for dirty state --- src/vs/workbench/browser/parts/editor/editorActions.ts | 7 +++---- src/vs/workbench/services/editor/browser/editorService.ts | 4 ---- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorActions.ts b/src/vs/workbench/browser/parts/editor/editorActions.ts index 5aeb9a3efba..4d1f372052f 100644 --- a/src/vs/workbench/browser/parts/editor/editorActions.ts +++ b/src/vs/workbench/browser/parts/editor/editorActions.ts @@ -642,14 +642,13 @@ export abstract class BaseCloseAllAction extends Action { return; } - let saveOrRevert: boolean; if (confirm === ConfirmResult.DONT_SAVE) { - saveOrRevert = await this.editorService.revertAll({ soft: true, includeUntitled: true }); + await this.editorService.revertAll({ soft: true, includeUntitled: true }); } else { - saveOrRevert = await this.editorService.saveAll({ reason: SaveReason.EXPLICIT, includeUntitled: true }); + await this.editorService.saveAll({ reason: SaveReason.EXPLICIT, includeUntitled: true }); } - if (saveOrRevert) { + if (!this.workingCopyService.hasDirty) { return this.doCloseAll(); } } diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index 0b97fbf1218..2583458a30d 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -1034,10 +1034,6 @@ export class EditorService extends Disposable implements EditorServiceImpl { return true; // might have been disposed from from the revert already } - if (!editor.isDirty() && !options?.force) { - return true; // skip over non-dirty editors unless we force to revert - } - // Use revert as a hint to pin the editor this.editorGroupService.getGroup(groupId)?.pinEditor(editor); From 4c7b326ae9960d44795c45242ebe46988a99da97 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Tue, 25 Feb 2020 15:57:32 +0100 Subject: [PATCH 118/200] fix json validation validation --- src/vs/workbench/api/common/jsonValidationExtensionPoint.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/api/common/jsonValidationExtensionPoint.ts b/src/vs/workbench/api/common/jsonValidationExtensionPoint.ts index 99c5c717bcc..c3f09c4cdb1 100644 --- a/src/vs/workbench/api/common/jsonValidationExtensionPoint.ts +++ b/src/vs/workbench/api/common/jsonValidationExtensionPoint.ts @@ -69,8 +69,8 @@ export class JSONValidationExtensionPoint { } catch (e) { collector.error(nls.localize('invalid.url.fileschema', "'configuration.jsonValidation.url' is an invalid relative URL: {0}", e.message)); } - } else if (!strings.startsWith(uri, 'http://') && !strings.startsWith(uri, 'https://')) { - collector.error(nls.localize('invalid.url.schema', "'configuration.jsonValidation.url' must start with 'http:', 'https:' or './' to reference schemas located in the extension")); + } else if (!/^[^:/?#]+:\/\//.test(uri)) { + collector.error(nls.localize('invalid.url.schema', "'configuration.jsonValidation.url' must be an absolute URL or start with './' to reference schemas located in the extension.")); return; } }); From b304296e82305e082d8520c0027641e12167b9d3 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Tue, 25 Feb 2020 07:24:44 -0800 Subject: [PATCH 119/200] debug: update js-debug-nightly to "2020.2.2507" @ 2020-02-25T15:24:10.260Z --- build/builtInExtensions.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/builtInExtensions.json b/build/builtInExtensions.json index e93b6dfbab6..84091196b00 100644 --- a/build/builtInExtensions.json +++ b/build/builtInExtensions.json @@ -46,7 +46,7 @@ }, { "name": "ms-vscode.js-debug-nightly", - "version": "2020.2.2117", + "version": "2020.2.2507", "forQualities": [ "insider" ], From 20b4e047c6c43223ce9f66055fa5f58eab8aa36b Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 25 Feb 2020 16:30:36 +0100 Subject: [PATCH 120/200] test - fix smoketest for web running out of sources --- src/vs/platform/product/common/product.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/product/common/product.ts b/src/vs/platform/product/common/product.ts index 804d1138561..da88376513b 100644 --- a/src/vs/platform/product/common/product.ts +++ b/src/vs/platform/product/common/product.ts @@ -21,7 +21,7 @@ if (isWeb) { // Running out of sources if (Object.keys(product).length === 0) { assign(product, { - version: '1.41.0-dev', + version: '1.43.0-dev', nameLong: 'Visual Studio Code Web Dev', nameShort: 'VSCode Web Dev', urlProtocol: 'code-oss' From ee15fadc5104709c5f579a2317f17e789f719c13 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Tue, 25 Feb 2020 17:11:31 +0100 Subject: [PATCH 121/200] Change the casing of dockerFile to dockerfile (microsoft/vscode-remote-release#2422) --- .../schemas/devContainer.schema.json | 50 +++++++++++-------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/extensions/configuration-editing/schemas/devContainer.schema.json b/extensions/configuration-editing/schemas/devContainer.schema.json index 3cff1bd58f8..9cb171915fc 100644 --- a/extensions/configuration-editing/schemas/devContainer.schema.json +++ b/extensions/configuration-editing/schemas/devContainer.schema.json @@ -142,23 +142,7 @@ } } }, - "dockerFileAndContext": { - "type": "object", - "properties": { - "dockerFile": { - "type": "string", - "description": "The location of the Dockerfile that defines the contents of the container. The path is relative to the folder containing the `devcontainer.json` file." - }, - "context": { - "type": "string", - "description": "The location of the context folder for building the Docker image. The path is relative to the folder containing the `devcontainer.json` file." - } - }, - "required": [ - "dockerFile" - ] - }, - "dockerFileContainer": { + "dockerfileContainer": { "oneOf": [ { "type": "object", @@ -168,7 +152,20 @@ "description": "Docker build-related options.", "allOf": [ { - "$ref": "#/definitions/dockerFileAndContext" + "type": "object", + "properties": { + "dockerfile": { + "type": "string", + "description": "The location of the Dockerfile that defines the contents of the container. The path is relative to the folder containing the `devcontainer.json` file." + }, + "context": { + "type": "string", + "description": "The location of the context folder for building the Docker image. The path is relative to the folder containing the `devcontainer.json` file." + } + }, + "required": [ + "dockerfile" + ] }, { "$ref": "#/definitions/buildOptions" @@ -183,7 +180,20 @@ { "allOf": [ { - "$ref": "#/definitions/dockerFileAndContext" + "type": "object", + "properties": { + "dockerFile": { + "type": "string", + "description": "The location of the Dockerfile that defines the contents of the container. The path is relative to the folder containing the `devcontainer.json` file." + }, + "context": { + "type": "string", + "description": "The location of the context folder for building the Docker image. The path is relative to the folder containing the `devcontainer.json` file." + } + }, + "required": [ + "dockerFile" + ] }, { "type": "object", @@ -280,7 +290,7 @@ { "oneOf": [ { - "$ref": "#/definitions/dockerFileContainer" + "$ref": "#/definitions/dockerfileContainer" }, { "$ref": "#/definitions/imageContainer" From 166541c4ae66f64eb775b5a9d1456fedfd64d198 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Tue, 25 Feb 2020 08:47:21 -0800 Subject: [PATCH 122/200] Fix #11847 --- src/vs/vscode.d.ts | 13 ++++++++++- src/vs/vscode.proposed.d.ts | 22 ------------------- .../api/common/extHostTypeConverters.ts | 17 +++++++++----- 3 files changed, 23 insertions(+), 29 deletions(-) diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 1d01f238f0d..ead7e54c39f 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -4675,7 +4675,18 @@ declare module 'vscode' { * A code or identifier for this diagnostic. * Should be used for later processing, e.g. when providing [code actions](#CodeActionContext). */ - code?: string | number; + code?: string | number | { + /** + * A code or identifier for this diagnostic. + * Should be used for later processing, e.g. when providing [code actions](#CodeActionContext). + */ + value: string | number; + + /** + * A target URI to open with more information about the diagnostic error. + */ + target: Uri; + }; /** * An array of related diagnostic information, e.g. when symbol-names within diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 157c8732986..f396efd212e 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1522,28 +1522,6 @@ declare module 'vscode' { //#endregion - //#region Diagnostic links https://github.com/microsoft/vscode/issues/11847 - - export interface Diagnostic { - /** - * Will be merged into `Diagnostic#code` - */ - code2?: { - /** - * A code or identifier for this diagnostic. - * Should be used for later processing, e.g. when providing [code actions](#CodeActionContext). - */ - value: string | number; - - /** - * A target URI to open with more information about the diagnostic error. - */ - target: Uri; - } - } - - //#endregion - //#region eamodio - timeline: https://github.com/microsoft/vscode/issues/84297 export class TimelineItem { diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 6027377e166..38154ceca2e 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -127,12 +127,17 @@ export namespace DiagnosticTag { export namespace Diagnostic { export function from(value: vscode.Diagnostic): IMarkerData { - let code: string | { value: string; target: URI } | undefined = isString(value.code) || isNumber(value.code) ? String(value.code) : undefined; - if (value.code2) { - code = { - value: String(value.code2.value), - target: value.code2.target - }; + let code: string | { value: string; target: URI } | undefined; + + if (value.code) { + if (isString(value.code) || isNumber(value.code)) { + code = String(value.code); + } else { + code = { + value: String(value.code.value), + target: value.code.target, + }; + } } return { From 63257ce557f95f672fa61270d16b67f64bde7e21 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 25 Feb 2020 18:17:51 +0100 Subject: [PATCH 123/200] Fix #91413 --- .../userDataSync/common/settingsMerge.ts | 4 +- .../userDataSync/common/userDataSync.ts | 47 ++++++------------- .../browser/userDataSync.contribution.ts | 42 +---------------- 3 files changed, 17 insertions(+), 76 deletions(-) diff --git a/src/vs/platform/userDataSync/common/settingsMerge.ts b/src/vs/platform/userDataSync/common/settingsMerge.ts index 83e87e281d3..cd74d415f26 100644 --- a/src/vs/platform/userDataSync/common/settingsMerge.ts +++ b/src/vs/platform/userDataSync/common/settingsMerge.ts @@ -10,7 +10,7 @@ import { values } from 'vs/base/common/map'; import { IStringDictionary } from 'vs/base/common/collections'; import { FormattingOptions, Edit, getEOL } from 'vs/base/common/jsonFormatter'; import * as contentUtil from 'vs/platform/userDataSync/common/content'; -import { IConflictSetting } from 'vs/platform/userDataSync/common/userDataSync'; +import { IConflictSetting, getDisallowedIgnoredSettings } from 'vs/platform/userDataSync/common/userDataSync'; import { firstIndex, distinct } from 'vs/base/common/arrays'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { startsWith } from 'vs/base/common/strings'; @@ -32,7 +32,7 @@ export function getIgnoredSettings(defaultIgnoredSettings: string[], configurati } else { value = configurationService.getValue('sync.ignoredSettings'); } - const added: string[] = [], removed: string[] = []; + const added: string[] = [], removed: string[] = [...getDisallowedIgnoredSettings()]; if (Array.isArray(value)) { for (const key of value) { if (startsWith(key, '-')) { diff --git a/src/vs/platform/userDataSync/common/userDataSync.ts b/src/vs/platform/userDataSync/common/userDataSync.ts index 5e511488ee4..23bd81b6526 100644 --- a/src/vs/platform/userDataSync/common/userDataSync.ts +++ b/src/vs/platform/userDataSync/common/userDataSync.ts @@ -21,6 +21,7 @@ import { URI } from 'vs/base/common/uri'; import { isEqual, joinPath } from 'vs/base/common/resources'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IProductService } from 'vs/platform/product/common/productService'; +import { distinct } from 'vs/base/common/arrays'; export const CONFIGURATION_SYNC_STORE_KEY = 'configurationSync.store'; @@ -37,10 +38,16 @@ export interface ISyncConfiguration { } } +export function getDisallowedIgnoredSettings(): string[] { + const allSettings = Registry.as(ConfigurationExtensions.Configuration).getConfigurationProperties(); + return Object.keys(allSettings).filter(setting => !!allSettings[setting].disallowSyncIgnore); +} + export function getDefaultIgnoredSettings(): string[] { const allSettings = Registry.as(ConfigurationExtensions.Configuration).getConfigurationProperties(); const machineSettings = Object.keys(allSettings).filter(setting => allSettings[setting].scope === ConfigurationScope.MACHINE || allSettings[setting].scope === ConfigurationScope.MACHINE_OVERRIDABLE); - return [CONFIGURATION_SYNC_STORE_KEY, ...machineSettings]; + const disallowedSettings = getDisallowedIgnoredSettings(); + return distinct([CONFIGURATION_SYNC_STORE_KEY, ...machineSettings, ...disallowedSettings]); } export function registerConfiguration(): IDisposable { @@ -53,36 +60,6 @@ export function registerConfiguration(): IDisposable { title: localize('sync', "Sync"), type: 'object', properties: { - 'sync.enable': { - type: 'boolean', - default: false, - scope: ConfigurationScope.APPLICATION, - deprecationMessage: 'deprecated' - }, - 'sync.enableSettings': { - type: 'boolean', - default: true, - scope: ConfigurationScope.APPLICATION, - deprecationMessage: 'deprecated' - }, - 'sync.enableKeybindings': { - type: 'boolean', - default: true, - scope: ConfigurationScope.APPLICATION, - deprecationMessage: 'Deprecated' - }, - 'sync.enableUIState': { - type: 'boolean', - default: true, - scope: ConfigurationScope.APPLICATION, - deprecationMessage: 'deprecated' - }, - 'sync.enableExtensions': { - type: 'boolean', - default: true, - scope: ConfigurationScope.APPLICATION, - deprecationMessage: 'deprecated' - }, 'sync.keybindingsPerPlatform': { type: 'boolean', description: localize('sync.keybindingsPerPlatform', "Synchronize keybindings per platform."), @@ -115,11 +92,15 @@ export function registerConfiguration(): IDisposable { }); const jsonRegistry = Registry.as(JSONExtensions.JSONContribution); const registerIgnoredSettingsSchema = () => { - const defaultIgnoreSettings = getDefaultIgnoredSettings().filter(s => s !== CONFIGURATION_SYNC_STORE_KEY); + const disallowedIgnoredSettings = getDisallowedIgnoredSettings(); + const defaultIgnoredSettings = getDefaultIgnoredSettings().filter(s => s !== CONFIGURATION_SYNC_STORE_KEY); + const settings = Object.keys(allSettings.properties).filter(setting => defaultIgnoredSettings.indexOf(setting) === -1); + const ignoredSettings = defaultIgnoredSettings.filter(setting => disallowedIgnoredSettings.indexOf(setting) === -1); + console.log(ignoredSettings); const ignoredSettingsSchema: IJSONSchema = { items: { type: 'string', - enum: [...Object.keys(allSettings.properties).filter(setting => defaultIgnoreSettings.indexOf(setting) === -1), ...defaultIgnoreSettings.map(setting => `-${setting}`)] + enum: [...settings, ...ignoredSettings.map(setting => `-${setting}`)] }, }; jsonRegistry.registerSchema(ignoredSettingsSchemaId, ignoredSettingsSchema); diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.contribution.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.contribution.ts index dbe351f2e2e..367dd0b6fb3 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.contribution.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.contribution.ts @@ -3,50 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { Registry } from 'vs/platform/registry/common/platform'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { UserDataSyncWorkbenchContribution } from 'vs/workbench/contrib/userDataSync/browser/userDataSync'; -import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; -import { IUserDataSyncEnablementService, getUserDataSyncStore } from 'vs/platform/userDataSync/common/userDataSync'; -import { IProductService } from 'vs/platform/product/common/productService'; - -class UserDataSyncSettingsMigrationContribution implements IWorkbenchContribution { - - constructor( - @IProductService productService: IProductService, - @IConfigurationService private readonly configurationService: IConfigurationService, - @IUserDataSyncEnablementService userDataSyncEnablementService: IUserDataSyncEnablementService, - ) { - if (getUserDataSyncStore(productService, configurationService)) { - if (!configurationService.getValue('sync.enableSettings')) { - userDataSyncEnablementService.setResourceEnablement('settings', false); - } - if (!configurationService.getValue('sync.enableKeybindings')) { - userDataSyncEnablementService.setResourceEnablement('keybindings', false); - } - if (!configurationService.getValue('sync.enableUIState')) { - userDataSyncEnablementService.setResourceEnablement('globalState', false); - } - if (!configurationService.getValue('sync.enableExtensions')) { - userDataSyncEnablementService.setResourceEnablement('extensions', false); - } - if (configurationService.getValue('sync.enable')) { - userDataSyncEnablementService.setEnablement(true); - } - this.removeFromConfiguration(); - } - } - - private async removeFromConfiguration(): Promise { - await this.configurationService.updateValue('sync.enable', undefined, {}, ConfigurationTarget.USER, true); - await this.configurationService.updateValue('sync.enableSettings', undefined, {}, ConfigurationTarget.USER, true); - await this.configurationService.updateValue('sync.enableKeybindings', undefined, {}, ConfigurationTarget.USER, true); - await this.configurationService.updateValue('sync.enableUIState', undefined, {}, ConfigurationTarget.USER, true); - await this.configurationService.updateValue('sync.enableExtensions', undefined, {}, ConfigurationTarget.USER, true); - } -} const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); workbenchRegistry.registerWorkbenchContribution(UserDataSyncWorkbenchContribution, LifecyclePhase.Ready); -workbenchRegistry.registerWorkbenchContribution(UserDataSyncSettingsMigrationContribution, LifecyclePhase.Ready); From dbbd3adf67f672019ed9769c49c369916484bf06 Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Mon, 24 Feb 2020 11:15:42 -0800 Subject: [PATCH 124/200] Fix trailing comma in MSA auth provider --- extensions/vscode-account/src/AADHelper.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/vscode-account/src/AADHelper.ts b/extensions/vscode-account/src/AADHelper.ts index 94bc3695dc4..5f193828141 100644 --- a/extensions/vscode-account/src/AADHelper.ts +++ b/extensions/vscode-account/src/AADHelper.ts @@ -295,8 +295,8 @@ export class AzureActiveDirectoryService { private getCallbackEnvironment(callbackUri: vscode.Uri): string { switch (callbackUri.authority) { - case 'online.visualstudio.com,': - return 'vso'; + case 'online.visualstudio.com': + return 'vso,'; case 'online-ppe.core.vsengsaas.visualstudio.com': return 'vsoppe,'; case 'online.dev.core.vsengsaas.visualstudio.com': @@ -397,7 +397,7 @@ export class AzureActiveDirectoryService { const claims = this.getTokenClaims(json.access_token); return { expiresIn: json.expires_in, - expiresAt: Date.now() + json.expires_in * 1000, + expiresAt: json.expires_in ? Date.now() + json.expires_in * 1000 : undefined, accessToken: json.access_token, refreshToken: json.refresh_token, scope, From 5a03b23bf0a235afdd069d829877b2f412dcd601 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Tue, 25 Feb 2020 10:29:20 -0800 Subject: [PATCH 125/200] Add command to clean search editor state --- .../workbench/contrib/searchEditor/browser/constants.ts | 1 + .../searchEditor/browser/searchEditor.contribution.ts | 9 +++++++++ .../contrib/searchEditor/browser/searchEditor.ts | 4 ++++ 3 files changed, 14 insertions(+) diff --git a/src/vs/workbench/contrib/searchEditor/browser/constants.ts b/src/vs/workbench/contrib/searchEditor/browser/constants.ts index d2d0e3c3537..7b1b1d34825 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/constants.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/constants.ts @@ -13,6 +13,7 @@ export const ToggleSearchEditorWholeWordCommandId = 'toggleSearchEditorWholeWord export const ToggleSearchEditorRegexCommandId = 'toggleSearchEditorRegex'; export const ToggleSearchEditorContextLinesCommandId = 'toggleSearchEditorContextLines'; export const RerunSearchEditorSearchCommandId = 'rerunSearchEditorSearch'; +export const CleanSearchEditorStateCommandId = 'cleanSearchEditorState'; export const SelectAllSearchEditorMatchesCommandId = 'selectAllSearchEditorMatches'; export const InSearchEditor = new RawContextKey('inSearchEditor', false); diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts index ec7fcb3c879..1f10071bcfd 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts @@ -162,6 +162,15 @@ CommandsRegistry.registerCommand( activeControl.triggerSearch({ resetCursor: false }); } }); + +CommandsRegistry.registerCommand( + SearchEditorConstants.CleanSearchEditorStateCommandId, + (accessor: ServicesAccessor) => { + const activeControl = accessor.get(IEditorService).activeControl; + if (activeControl instanceof SearchEditor) { + activeControl.cleanState(); + } + }); //#endregion //#region Actions diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts index d7557b789ba..8f26a359018 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts @@ -297,6 +297,10 @@ export class SearchEditor extends BaseTextEditor { this.toggleIncludesExcludes(); } + cleanState() { + this.getInput()?.setDirty(false); + } + private get searchConfig(): ISearchConfigurationProperties { return this.configurationService.getValue('search'); } From 858457d2e6188755bb54349436e7c825e46237bd Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Tue, 25 Feb 2020 10:38:32 -0800 Subject: [PATCH 126/200] Fix rehydrating of GitHub auth provider data --- .../github-authentication/src/github.ts | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/extensions/github-authentication/src/github.ts b/extensions/github-authentication/src/github.ts index fe4dcaa6d1f..0f46d20d81b 100644 --- a/extensions/github-authentication/src/github.ts +++ b/extensions/github-authentication/src/github.ts @@ -10,6 +10,13 @@ import Logger from './common/logger'; export const onDidChangeSessions = new vscode.EventEmitter(); +interface SessionData { + id: string; + accountName: string; + scopes: string[]; + accessToken: string; +} + export class GitHubAuthenticationProvider { private _sessions: vscode.AuthenticationSession[] = []; private _githubServer = new GitHubServer(); @@ -58,7 +65,15 @@ export class GitHubAuthenticationProvider { const storedSessions = await keychain.getToken(); if (storedSessions) { try { - return JSON.parse(storedSessions); + const sessionData: SessionData[] = JSON.parse(storedSessions); + return sessionData.map(session => { + return { + id: session.id, + accountName: session.accountName, + scopes: session.scopes, + accessToken: () => Promise.resolve(session.accessToken) + }; + }); } catch (e) { Logger.error(`Error reading sessions: ${e}`); } @@ -68,7 +83,17 @@ export class GitHubAuthenticationProvider { } private async storeSessions(): Promise { - await keychain.setToken(JSON.stringify(this._sessions)); + const sessionData: SessionData[] = await Promise.all(this._sessions.map(async session => { + const resolvedAccessToken = await session.accessToken(); + return { + id: session.id, + accountName: session.accountName, + scopes: session.scopes, + accessToken: resolvedAccessToken + }; + })); + + await keychain.setToken(JSON.stringify(sessionData)); } get sessions(): vscode.AuthenticationSession[] { From 8291f8c152ebe6d54e98f1844c69e458863f5a45 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 25 Feb 2020 10:51:47 -0800 Subject: [PATCH 127/200] Polishing of custom editor api proposal for 1.43 release For #77131 --- extensions/image-preview/src/extension.ts | 2 +- extensions/image-preview/src/preview.ts | 10 +- .../src/features/previewManager.ts | 12 +-- src/vs/vscode.proposed.d.ts | 74 ++++----------- .../workbench/api/common/extHost.api.impl.ts | 12 +-- src/vs/workbench/api/common/extHostWebview.ts | 93 +++++++++---------- 6 files changed, 74 insertions(+), 129 deletions(-) diff --git a/extensions/image-preview/src/extension.ts b/extensions/image-preview/src/extension.ts index dc0d5c33956..cbb487e094a 100644 --- a/extensions/image-preview/src/extension.ts +++ b/extensions/image-preview/src/extension.ts @@ -23,7 +23,7 @@ export function activate(context: vscode.ExtensionContext) { const previewManager = new PreviewManager(extensionRoot, sizeStatusBarEntry, binarySizeStatusBarEntry, zoomStatusBarEntry); - context.subscriptions.push(vscode.window.registerWebviewCustomEditorProvider(PreviewManager.viewType, previewManager)); + context.subscriptions.push(vscode.window.registerCustomEditorProvider(PreviewManager.viewType, previewManager)); context.subscriptions.push(vscode.commands.registerCommand('imagePreview.zoomIn', () => { previewManager.activePreview?.zoomIn(); diff --git a/extensions/image-preview/src/preview.ts b/extensions/image-preview/src/preview.ts index 92268c53b39..fc43a136a01 100644 --- a/extensions/image-preview/src/preview.ts +++ b/extensions/image-preview/src/preview.ts @@ -13,7 +13,7 @@ import { BinarySizeStatusBarEntry } from './binarySizeStatusBarEntry'; const localize = nls.loadMessageBundle(); -export class PreviewManager implements vscode.WebviewCustomEditorProvider { +export class PreviewManager implements vscode.CustomEditorProvider { public static readonly viewType = 'imagePreview.previewEditor'; @@ -27,12 +27,12 @@ export class PreviewManager implements vscode.WebviewCustomEditorProvider { private readonly zoomStatusBarEntry: ZoomStatusBarEntry, ) { } - public async provideWebviewCustomEditorDocument(resource: vscode.Uri) { - return vscode.window.createWebviewEditorCustomDocument(PreviewManager.viewType, resource, undefined, {}); + public async resolveCustomDocument(_document: vscode.CustomDocument): Promise { + return {}; } - public async resolveWebviewCustomEditor( - document: vscode.WebviewEditorCustomDocument, + public async resolveCustomEditor( + document: vscode.CustomDocument, webviewEditor: vscode.WebviewPanel, ): Promise { const preview = new Preview(this.extensionRoot, document.uri, webviewEditor, this.sizeStatusBarEntry, this.binarySizeStatusBarEntry, this.zoomStatusBarEntry); diff --git a/extensions/markdown-language-features/src/features/previewManager.ts b/extensions/markdown-language-features/src/features/previewManager.ts index 75dfaaa6ef1..68d68118309 100644 --- a/extensions/markdown-language-features/src/features/previewManager.ts +++ b/extensions/markdown-language-features/src/features/previewManager.ts @@ -52,7 +52,7 @@ class PreviewStore extends Disposable { } } -export class MarkdownPreviewManager extends Disposable implements vscode.WebviewPanelSerializer, vscode.WebviewCustomEditorProvider { +export class MarkdownPreviewManager extends Disposable implements vscode.WebviewPanelSerializer, vscode.CustomEditorProvider { private static readonly markdownPreviewActiveContextKey = 'markdownPreviewFocus'; private readonly _topmostLineMonitor = new TopmostLineMonitor(); @@ -70,7 +70,7 @@ export class MarkdownPreviewManager extends Disposable implements vscode.Webview ) { super(); this._register(vscode.window.registerWebviewPanelSerializer(DynamicMarkdownPreview.viewType, this)); - this._register(vscode.window.registerWebviewCustomEditorProvider('vscode.markdown.preview.editor', this)); + this._register(vscode.window.registerCustomEditorProvider('vscode.markdown.preview.editor', this)); } public refresh() { @@ -148,12 +148,12 @@ export class MarkdownPreviewManager extends Disposable implements vscode.Webview this.registerDynamicPreview(preview); } - public async provideWebviewCustomEditorDocument(resource: vscode.Uri) { - return vscode.window.createWebviewEditorCustomDocument('vscode.markdown.preview.editor', resource, undefined, {}); + public async resolveCustomDocument(_document: vscode.CustomDocument): Promise { + return {}; } - public async resolveWebviewCustomEditor( - document: vscode.WebviewEditorCustomDocument, + public async resolveCustomEditor( + document: vscode.CustomDocument, webview: vscode.WebviewPanel ): Promise { const preview = DynamicMarkdownPreview.revive( diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index f396efd212e..50c03acaa3f 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1188,22 +1188,21 @@ declare module 'vscode' { //#region Custom editors: https://github.com/microsoft/vscode/issues/77131 // TODO: - // - Naming! // - Think about where a rename would live. - // - Think about handling go to line? + // - Think about handling go to line? (add other editor options? reveal?) // - Should we expose edits? // - More properties from `TextDocument`? /** * Defines the capabilities of a custom webview editor. */ - interface WebviewCustomEditorCapabilities { + interface CustomEditorCapabilities { /** * Defines the editing capability of a custom webview document. * * When not provided, the document is considered readonly. */ - readonly editing?: WebviewCustomEditorEditingCapability; + readonly editing?: CustomEditorEditingCapability; } /** @@ -1212,7 +1211,7 @@ declare module 'vscode' { * * @param EditType Type of edits. */ - interface WebviewCustomEditorEditingCapability { + interface CustomEditorEditingCapability { /** * Save the resource. * @@ -1286,7 +1285,7 @@ declare module 'vscode' { * * @param UserDataType Type of custom object that extensions can store on the document. */ - interface WebviewEditorCustomDocument { + interface CustomDocument { /** * The associated viewType for this document. */ @@ -1305,7 +1304,7 @@ declare module 'vscode' { /** * Custom data that an extension can store on the document. */ - readonly userData: UserDataType; + userData?: UserDataType; // TODO: Should we expose edits here? // This could be helpful for tracking the life cycle of edits @@ -1320,13 +1319,13 @@ declare module 'vscode' { * You should use custom text based editors when dealing with binary files or more complex scenarios. For simple text * based documents, use [`WebviewTextEditorProvider`](#WebviewTextEditorProvider) instead. */ - export interface WebviewCustomEditorProvider { + export interface CustomEditorProvider { /** * Create the model for a given * * @param document Resource being resolved. */ - provideWebviewCustomEditorDocument(resource: Uri): Thenable; + resolveCustomDocument(document: CustomDocument): Thenable; /** * Resolve a webview editor for a given resource. @@ -1339,7 +1338,7 @@ declare module 'vscode' { * * @return Thenable indicating that the webview editor has been resolved. */ - resolveWebviewCustomEditor(document: WebviewEditorCustomDocument, webview: WebviewPanel): Thenable; + resolveCustomEditor(document: CustomDocument, webview: WebviewPanel): Thenable; } /** @@ -1352,7 +1351,7 @@ declare module 'vscode' { * You should use text based webview editors when dealing with text based file formats, such as `xml` or `json`. * For binary files or more specialized use cases, see [WebviewCustomEditorProvider](#WebviewCustomEditorProvider). */ - export interface WebviewTextEditorProvider { + export interface CustomTextEditorProvider { /** * Resolve a webview editor for a given resource. * @@ -1364,64 +1363,25 @@ declare module 'vscode' { * * @return Thenable indicating that the webview editor has been resolved. */ - resolveWebviewTextEditor(document: TextDocument, webview: WebviewPanel): Thenable; + resolveCustomTextEditor(document: TextDocument, webview: WebviewPanel): Thenable; } namespace window { /** - * Register a new provider for text based webview editors. - * - * @param viewType Type of the webview editor provider. This should match the `viewType` from the - * `package.json` contributions - * @param provider Provider that resolves webview editors. - * @param webviewOptions Content settings for the webview panels that provider is given. - * - * @return Disposable that unregisters the `WebviewTextEditorProvider`. - */ - export function registerWebviewTextEditorProvider( - viewType: string, - provider: WebviewTextEditorProvider, - webviewOptions?: WebviewPanelOptions, - ): Disposable; - - /** - * Register a new provider for custom webview editors. + * Register a new provider for a custom editor. * * @param viewType Type of the webview editor provider. This should match the `viewType` from the * `package.json` contributions. - * @param provider Provider that resolves webview editors. - * @param webviewOptions Content settings for the webview panels that provider is given. + * @param provider Provider that resolves editors. + * @param webviewOptions Content settings for the webview panels that the provider is given. * - * @return Disposable that unregisters the `WebviewCustomEditorProvider`. + * @return Disposable that unregisters the provider. */ - export function registerWebviewCustomEditorProvider( + export function registerCustomEditorProvider( viewType: string, - provider: WebviewCustomEditorProvider, + provider: CustomEditorProvider | CustomTextEditorProvider, webviewOptions?: WebviewPanelOptions, ): Disposable; - - /** - * Create a new `WebviewEditorCustomDocument`. - * - * Note that this method only creates a custom document object. To have it be registered with VS Code, you - * must return the document from `WebviewCustomEditorProvider.provideWebviewCustomEditorDocument`. - * - * @param viewType Type of the webview editor provider. This should match the `viewType` from the - * `package.json` contributions. - * @param uri The document's resource. - * @param userData Custom data attached to the document. - * @param capabilities Controls the editing functionality of a webview editor. This allows the webview - * editor to hook into standard editor events such as `undo` or `save`. - * - * WebviewEditors that do not have `editingCapability` are considered to be readonly. Users can still interact - * with readonly editors, but these editors will not integrate with VS Code's standard editor functionality. - */ - export function createWebviewEditorCustomDocument( - viewType: string, - uri: Uri, - userData: UserDataType, - capabilities: WebviewCustomEditorCapabilities - ): WebviewEditorCustomDocument; } //#endregion diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 534d36a30b8..cb57cc82270 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -561,17 +561,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I registerWebviewPanelSerializer: (viewType: string, serializer: vscode.WebviewPanelSerializer) => { return extHostWebviews.registerWebviewPanelSerializer(extension, viewType, serializer); }, - registerWebviewTextEditorProvider: (viewType: string, provider: vscode.WebviewTextEditorProvider, options?: vscode.WebviewPanelOptions) => { + registerCustomEditorProvider: (viewType: string, provider: vscode.CustomEditorProvider | vscode.CustomTextEditorProvider, options?: vscode.WebviewPanelOptions) => { checkProposedApiEnabled(extension); - return extHostWebviews.registerWebviewTextEditorProvider(extension, viewType, provider, options); - }, - registerWebviewCustomEditorProvider: (viewType: string, provider: vscode.WebviewCustomEditorProvider, options?: vscode.WebviewPanelOptions) => { - checkProposedApiEnabled(extension); - return extHostWebviews.registerWebviewCustomEditorProvider(extension, viewType, provider, options); - }, - createWebviewEditorCustomDocument: (viewType: string, resource: vscode.Uri, userData: UserDataType, capabilities: vscode.WebviewCustomEditorCapabilities) => { - checkProposedApiEnabled(extension); - return extHostWebviews.createWebviewEditorCustomDocument(viewType, resource, userData, capabilities); + return extHostWebviews.registerCustomEditorProvider(extension, viewType, provider, options); }, registerDecorationProvider(provider: vscode.DecorationProvider) { checkProposedApiEnabled(extension); diff --git a/src/vs/workbench/api/common/extHostWebview.ts b/src/vs/workbench/api/common/extHostWebview.ts index a898dbba4fc..dd398b214c2 100644 --- a/src/vs/workbench/api/common/extHostWebview.ts +++ b/src/vs/workbench/api/common/extHostWebview.ts @@ -247,22 +247,30 @@ export class ExtHostWebviewEditor extends Disposable implements vscode.WebviewPa type EditType = unknown; -class WebviewEditorCustomDocument extends Disposable implements vscode.WebviewEditorCustomDocument { +class WebviewEditorCustomDocument extends Disposable implements vscode.CustomDocument { private _currentEditIndex: number = -1; private _savePoint: number = -1; private readonly _edits: Array = []; + public userData: unknown; + + public _capabilities?: vscode.CustomEditorCapabilities; + constructor( private readonly _proxy: MainThreadWebviewsShape, public readonly viewType: string, public readonly uri: vscode.Uri, - public readonly userData: unknown, - public readonly _capabilities: vscode.WebviewCustomEditorCapabilities, ) { super(); + } - // Hook up events - _capabilities.editing?.onDidEdit(edit => { + _setCapabilities(capabilities: vscode.CustomEditorCapabilities) { + if (this._capabilities) { + throw new Error('Capabilities already provided'); + } + + this._capabilities = capabilities; + capabilities.editing?.onDidEdit(edit => { this.pushEdit(edit, this); }); } @@ -356,12 +364,12 @@ class WebviewEditorCustomDocument extends Disposable implements vscode.WebviewEd return this.getEditingCapability().saveAs(target); } - backup(cancellation: CancellationToken): boolean | PromiseLike { - throw new Error('Method not implemented.'); + backup(cancellation: CancellationToken) { + return this.getEditingCapability().backup(cancellation); } - private getEditingCapability(): vscode.WebviewCustomEditorEditingCapability { - if (!this._capabilities.editing) { + private getEditingCapability(): vscode.CustomEditorEditingCapability { + if (!this._capabilities?.editing) { throw new Error('Document is not editable'); } return this._capabilities.editing; @@ -401,21 +409,21 @@ const enum WebviewEditorType { type ProviderEntry = { readonly extension: IExtensionDescription; readonly type: WebviewEditorType.Text; - readonly provider: vscode.WebviewTextEditorProvider; + readonly provider: vscode.CustomTextEditorProvider; } | { readonly extension: IExtensionDescription; readonly type: WebviewEditorType.Custom; - readonly provider: vscode.WebviewCustomEditorProvider; + readonly provider: vscode.CustomEditorProvider; }; class EditorProviderStore { private readonly _providers = new Map(); - public addTextProvider(viewType: string, extension: IExtensionDescription, provider: vscode.WebviewTextEditorProvider): vscode.Disposable { + public addTextProvider(viewType: string, extension: IExtensionDescription, provider: vscode.CustomTextEditorProvider): vscode.Disposable { return this.add(WebviewEditorType.Text, viewType, extension, provider); } - public addCustomProvider(viewType: string, extension: IExtensionDescription, provider: vscode.WebviewCustomEditorProvider): vscode.Disposable { + public addCustomProvider(viewType: string, extension: IExtensionDescription, provider: vscode.CustomEditorProvider): vscode.Disposable { return this.add(WebviewEditorType.Custom, viewType, extension, provider); } @@ -423,7 +431,7 @@ class EditorProviderStore { return this._providers.get(viewType); } - private add(type: WebviewEditorType, viewType: string, extension: IExtensionDescription, provider: vscode.WebviewTextEditorProvider | vscode.WebviewCustomEditorProvider): vscode.Disposable { + private add(type: WebviewEditorType, viewType: string, extension: IExtensionDescription, provider: vscode.CustomTextEditorProvider | vscode.CustomEditorProvider): vscode.Disposable { if (this._providers.has(viewType)) { throw new Error(`Provider for viewType:${viewType} already registered`); } @@ -501,43 +509,26 @@ export class ExtHostWebviews implements ExtHostWebviewsShape { }); } - public registerWebviewTextEditorProvider( + public registerCustomEditorProvider( extension: IExtensionDescription, viewType: string, - provider: vscode.WebviewTextEditorProvider, + provider: vscode.CustomEditorProvider | vscode.CustomTextEditorProvider, options: vscode.WebviewPanelOptions | undefined = {} ): vscode.Disposable { - const unregisterProvider = this._editorProviders.addTextProvider(viewType, extension, provider); - this._proxy.$registerTextEditorProvider(toExtensionData(extension), viewType, options); + let disposable: vscode.Disposable; + if ('resolveCustomTextEditor' in provider) { + disposable = this._editorProviders.addTextProvider(viewType, extension, provider); + this._proxy.$registerTextEditorProvider(toExtensionData(extension), viewType, options); + } else { + disposable = this._editorProviders.addCustomProvider(viewType, extension, provider); + this._proxy.$registerCustomEditorProvider(toExtensionData(extension), viewType, options); + } - return new VSCodeDisposable(() => { - unregisterProvider.dispose(); - this._proxy.$unregisterEditorProvider(viewType); - }); - } - - public registerWebviewCustomEditorProvider( - extension: IExtensionDescription, - viewType: string, - provider: vscode.WebviewCustomEditorProvider, - options: vscode.WebviewPanelOptions | undefined = {}, - ): vscode.Disposable { - const unregisterProvider = this._editorProviders.addCustomProvider(viewType, extension, provider); - this._proxy.$registerCustomEditorProvider(toExtensionData(extension), viewType, options); - - return new VSCodeDisposable(() => { - unregisterProvider.dispose(); - this._proxy.$unregisterEditorProvider(viewType); - }); - } - - public createWebviewEditorCustomDocument( - viewType: string, - resource: vscode.Uri, - userData: UserDataType, - capabilities: vscode.WebviewCustomEditorCapabilities, - ): vscode.WebviewEditorCustomDocument { - return Object.seal(new WebviewEditorCustomDocument(this._proxy, viewType, resource, userData, capabilities) as vscode.WebviewEditorCustomDocument); + return VSCodeDisposable.from( + disposable, + new VSCodeDisposable(() => { + this._proxy.$unregisterEditorProvider(viewType); + })); } public $onMessage( @@ -631,10 +622,12 @@ export class ExtHostWebviews implements ExtHostWebviewsShape { } const revivedResource = URI.revive(resource); - const document = await entry.provider.provideWebviewCustomEditorDocument(revivedResource) as WebviewEditorCustomDocument; + const document = Object.seal(new WebviewEditorCustomDocument(this._proxy, viewType, revivedResource)); + const capabilities = await entry.provider.resolveCustomDocument(document); + document._setCapabilities(capabilities); this._documents.add(document); return { - editable: !!document._capabilities.editing + editable: !!capabilities.editing }; } @@ -677,13 +670,13 @@ export class ExtHostWebviews implements ExtHostWebviewsShape { case WebviewEditorType.Custom: { const document = this.getDocument(viewType, revivedResource); - return entry.provider.resolveWebviewCustomEditor(document, revivedPanel); + return entry.provider.resolveCustomEditor(document, revivedPanel); } case WebviewEditorType.Text: { await this._extHostDocuments.ensureDocumentData(revivedResource); const document = this._extHostDocuments.getDocument(revivedResource); - return entry.provider.resolveWebviewTextEditor(document, revivedPanel); + return entry.provider.resolveCustomTextEditor(document, revivedPanel); } default: { From dc1e7871b8dd987ca2b25b9d983c2d31ec9039d7 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 25 Feb 2020 10:55:19 -0800 Subject: [PATCH 128/200] Rename custom editor contribution point Renaming to match new API proposal --- extensions/image-preview/package.json | 4 ++-- extensions/image-preview/package.nls.json | 2 +- extensions/markdown-language-features/package.json | 2 +- src/vs/platform/extensions/common/extensions.ts | 2 +- src/vs/workbench/api/common/extHostWebview.ts | 2 +- .../workbench/contrib/customEditor/browser/extensionPoint.ts | 4 ++-- .../workbench/contrib/extensions/browser/extensionEditor.ts | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/extensions/image-preview/package.json b/extensions/image-preview/package.json index a0664f98e3d..998f3799f4d 100644 --- a/extensions/image-preview/package.json +++ b/extensions/image-preview/package.json @@ -22,10 +22,10 @@ "onCommand:imagePreview.zoomOut" ], "contributes": { - "webviewEditors": [ + "customEditors": [ { "viewType": "imagePreview.previewEditor", - "displayName": "%webviewEditors.displayName%", + "displayName": "%customEditors.displayName%", "priority": "builtin", "selector": [ { diff --git a/extensions/image-preview/package.nls.json b/extensions/image-preview/package.nls.json index 304b1df9a3d..d1860bc2fb5 100644 --- a/extensions/image-preview/package.nls.json +++ b/extensions/image-preview/package.nls.json @@ -1,7 +1,7 @@ { "displayName": "Image Preview", "description": "Provides VS Code's built-in image preview", - "webviewEditors.displayName": "Image Preview", + "customEditors.displayName": "Image Preview", "command.zoomIn": "Zoom in", "command.zoomOut": "Zoom out" } diff --git a/extensions/markdown-language-features/package.json b/extensions/markdown-language-features/package.json index fa3f06ca1cf..f13d46a4b1c 100644 --- a/extensions/markdown-language-features/package.json +++ b/extensions/markdown-language-features/package.json @@ -309,7 +309,7 @@ "markdown.previewScripts": [ "./media/index.js" ], - "webviewEditors": [ + "customEditors": [ { "viewType": "vscode.markdown.preview.editor", "displayName": "(Experimental) VS Code Markdown Preview", diff --git a/src/vs/platform/extensions/common/extensions.ts b/src/vs/platform/extensions/common/extensions.ts index 4a9c4489708..87d90b14746 100644 --- a/src/vs/platform/extensions/common/extensions.ts +++ b/src/vs/platform/extensions/common/extensions.ts @@ -123,7 +123,7 @@ export interface IExtensionContributions { views?: { [location: string]: IView[] }; colors?: IColor[]; localizations?: ILocalization[]; - readonly webviewEditors?: readonly IWebviewEditor[]; + readonly customEditors?: readonly IWebviewEditor[]; readonly codeActions?: readonly ICodeActionContribution[]; } diff --git a/src/vs/workbench/api/common/extHostWebview.ts b/src/vs/workbench/api/common/extHostWebview.ts index dd398b214c2..121f1615990 100644 --- a/src/vs/workbench/api/common/extHostWebview.ts +++ b/src/vs/workbench/api/common/extHostWebview.ts @@ -254,7 +254,7 @@ class WebviewEditorCustomDocument extends Disposable implements vscode.CustomDoc public userData: unknown; - public _capabilities?: vscode.CustomEditorCapabilities; + public _capabilities?: vscode.CustomEditorCapabilities = undefined; constructor( private readonly _proxy: MainThreadWebviewsShape, diff --git a/src/vs/workbench/contrib/customEditor/browser/extensionPoint.ts b/src/vs/workbench/contrib/customEditor/browser/extensionPoint.ts index 2a126dabefe..7c7a65b2d63 100644 --- a/src/vs/workbench/contrib/customEditor/browser/extensionPoint.ts +++ b/src/vs/workbench/contrib/customEditor/browser/extensionPoint.ts @@ -24,7 +24,7 @@ interface IWebviewEditorsExtensionPoint { } const webviewEditorsContribution: IJSONSchema = { - description: nls.localize('contributes.webviewEditors', 'Contributes webview editors.'), + description: nls.localize('contributes.customEditors', 'Contributed custom editors.'), type: 'array', defaultSnippets: [{ body: [{ viewType: '', displayName: '' }] }], items: { @@ -76,7 +76,7 @@ const webviewEditorsContribution: IJSONSchema = { }; export const webviewEditorsExtensionPoint = ExtensionsRegistry.registerExtensionPoint({ - extensionPoint: 'webviewEditors', + extensionPoint: 'customEditors', deps: [languagesExtPoint], jsonSchema: webviewEditorsContribution }); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index be1a3fa8093..39ada55824f 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -1060,7 +1060,7 @@ export class ExtensionEditor extends BaseEditor { } private renderCustomEditors(container: HTMLElement, manifest: IExtensionManifest, onDetailsToggle: Function): boolean { - const webviewEditors = manifest.contributes?.webviewEditors || []; + const webviewEditors = manifest.contributes?.customEditors || []; if (!webviewEditors.length) { return false; } From eb354fa8c3ba6d290199de2bfe52f98ad9475f39 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 25 Feb 2020 12:10:35 -0800 Subject: [PATCH 129/200] Move 'reopen with' to end of context menu Fixes #91397 --- src/vs/workbench/contrib/customEditor/browser/commands.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/customEditor/browser/commands.ts b/src/vs/workbench/contrib/customEditor/browser/commands.ts index 87c12f0e206..7741a847772 100644 --- a/src/vs/workbench/contrib/customEditor/browser/commands.ts +++ b/src/vs/workbench/contrib/customEditor/browser/commands.ts @@ -89,7 +89,7 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { title: REOPEN_WITH_TITLE, category: viewCategory, }, - group: '3_open', + group: '6_reopen', order: 20, when: CONTEXT_HAS_CUSTOM_EDITORS, }); From 8bb8f214e7ce0f9c779a3f24ae35789927c1ff1d Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 25 Feb 2020 12:24:55 -0800 Subject: [PATCH 130/200] Use a customEditors context key that lists custom editors This allows commands/contributions to enable/disable themselves based matching against the list of custom editors --- .../workbench/contrib/customEditor/browser/commands.ts | 8 ++++---- .../contrib/customEditor/browser/customEditors.ts | 10 +++++----- .../contrib/customEditor/common/customEditor.ts | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/contrib/customEditor/browser/commands.ts b/src/vs/workbench/contrib/customEditor/browser/commands.ts index 7741a847772..a6b3efb63be 100644 --- a/src/vs/workbench/contrib/customEditor/browser/commands.ts +++ b/src/vs/workbench/contrib/customEditor/browser/commands.ts @@ -17,7 +17,7 @@ import { EditorViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/comm import { IEditorCommandsContext } from 'vs/workbench/common/editor'; import { CustomEditorInput } from 'vs/workbench/contrib/customEditor/browser/customEditorInput'; import { defaultEditorId } from 'vs/workbench/contrib/customEditor/browser/customEditors'; -import { CONTEXT_FOCUSED_CUSTOM_EDITOR_IS_EDITABLE, CONTEXT_HAS_CUSTOM_EDITORS, ICustomEditorService } from 'vs/workbench/contrib/customEditor/common/customEditor'; +import { CONTEXT_FOCUSED_CUSTOM_EDITOR_IS_EDITABLE, CONTEXT_CUSTOM_EDITORS, ICustomEditorService } from 'vs/workbench/contrib/customEditor/common/customEditor'; import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import type { ITextEditorOptions } from 'vs/platform/editor/common/editor'; @@ -80,7 +80,7 @@ MenuRegistry.appendMenuItem(MenuId.CommandPalette, { title: REOPEN_WITH_TITLE, category: viewCategory, }, - when: CONTEXT_HAS_CUSTOM_EDITORS, + when: CONTEXT_CUSTOM_EDITORS.notEqualsTo(''), }); MenuRegistry.appendMenuItem(MenuId.EditorTitle, { @@ -91,7 +91,7 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { }, group: '6_reopen', order: 20, - when: CONTEXT_HAS_CUSTOM_EDITORS, + when: CONTEXT_CUSTOM_EDITORS.notEqualsTo(''), }); // #endregion @@ -155,7 +155,7 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { constructor() { super({ id: ToggleCustomEditorCommand.ID, - precondition: CONTEXT_HAS_CUSTOM_EDITORS, + precondition: CONTEXT_CUSTOM_EDITORS, }); } diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts index 7ce0a933cb7..e69de1daeb0 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts @@ -25,7 +25,7 @@ import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { EditorInput, EditorOptions, IEditor, IEditorInput } from 'vs/workbench/common/editor'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import { webviewEditorsExtensionPoint } from 'vs/workbench/contrib/customEditor/browser/extensionPoint'; -import { CONTEXT_FOCUSED_CUSTOM_EDITOR_IS_EDITABLE, CONTEXT_HAS_CUSTOM_EDITORS, CustomEditorInfo, CustomEditorInfoCollection, CustomEditorPriority, CustomEditorSelector, ICustomEditor, ICustomEditorService } from 'vs/workbench/contrib/customEditor/common/customEditor'; +import { CONTEXT_FOCUSED_CUSTOM_EDITOR_IS_EDITABLE, CONTEXT_CUSTOM_EDITORS, CustomEditorInfo, CustomEditorInfoCollection, CustomEditorPriority, CustomEditorSelector, ICustomEditor, ICustomEditorService } from 'vs/workbench/contrib/customEditor/common/customEditor'; import { CustomEditorModelManager } from 'vs/workbench/contrib/customEditor/common/customEditorModelManager'; import { IWebviewService, webviewHasOwnEditFunctionsContext } from 'vs/workbench/contrib/webview/browser/webview'; import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; @@ -98,7 +98,7 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ private readonly _models: CustomEditorModelManager; - private readonly _hasCustomEditor: IContextKey; + private readonly _customEditorContextKey: IContextKey; private readonly _focusedCustomEditorIsEditable: IContextKey; private readonly _webviewHasOwnEditFunctions: IContextKey; @@ -118,7 +118,7 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ this._models = new CustomEditorModelManager(workingCopyService, labelService); - this._hasCustomEditor = CONTEXT_HAS_CUSTOM_EDITORS.bindTo(contextKeyService); + this._customEditorContextKey = CONTEXT_CUSTOM_EDITORS.bindTo(contextKeyService); this._focusedCustomEditorIsEditable = CONTEXT_FOCUSED_CUSTOM_EDITOR_IS_EDITABLE.bindTo(contextKeyService); this._webviewHasOwnEditFunctions = webviewHasOwnEditFunctionsContext.bindTo(contextKeyService); @@ -310,7 +310,7 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ const activeControl = this.editorService.activeControl; const resource = activeControl?.input.resource; if (!resource) { - this._hasCustomEditor.reset(); + this._customEditorContextKey.reset(); this._focusedCustomEditorIsEditable.reset(); this._webviewHasOwnEditFunctions.reset(); return; @@ -320,7 +320,7 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ ...this.getContributedCustomEditors(resource).allEditors, ...this.getUserConfiguredCustomEditors(resource).allEditors, ]; - this._hasCustomEditor.set(possibleEditors.length > 0); + this._customEditorContextKey.set(possibleEditors.map(x => x.id).join(',')); this._focusedCustomEditorIsEditable.set(activeControl?.input instanceof CustomEditorInput); this._webviewHasOwnEditFunctions.set(possibleEditors.length > 0); } diff --git a/src/vs/workbench/contrib/customEditor/common/customEditor.ts b/src/vs/workbench/contrib/customEditor/common/customEditor.ts index 96247937c20..a0e84261e3a 100644 --- a/src/vs/workbench/contrib/customEditor/common/customEditor.ts +++ b/src/vs/workbench/contrib/customEditor/common/customEditor.ts @@ -18,7 +18,7 @@ import { IWorkingCopy } from 'vs/workbench/services/workingCopy/common/workingCo export const ICustomEditorService = createDecorator('customEditorService'); -export const CONTEXT_HAS_CUSTOM_EDITORS = new RawContextKey('hasCustomEditors', false); +export const CONTEXT_CUSTOM_EDITORS = new RawContextKey('customEditors', ''); export const CONTEXT_FOCUSED_CUSTOM_EDITOR_IS_EDITABLE = new RawContextKey('focusedCustomEditorIsEditable', false); export interface ICustomEditor { From 14cf2b82bf1e1e2e1e229cb16d7bf2ebae790434 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 25 Feb 2020 13:45:31 -0800 Subject: [PATCH 131/200] Always dispatch to keybinding service when in chord mode in terminal Fixes #91238 --- .../platform/keybinding/common/abstractKeybindingService.ts | 4 ++++ src/vs/platform/keybinding/common/keybinding.ts | 2 ++ src/vs/platform/keybinding/common/keybindingResolver.ts | 5 +++++ .../platform/keybinding/test/common/mockKeybindingService.ts | 2 ++ .../workbench/contrib/terminal/browser/terminalInstance.ts | 4 ++-- 5 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/keybinding/common/abstractKeybindingService.ts b/src/vs/platform/keybinding/common/abstractKeybindingService.ts index 6440a346f59..1fce530772a 100644 --- a/src/vs/platform/keybinding/common/abstractKeybindingService.ts +++ b/src/vs/platform/keybinding/common/abstractKeybindingService.ts @@ -35,6 +35,10 @@ export abstract class AbstractKeybindingService extends Disposable implements IK private _currentChordChecker: IntervalTimer; private _currentChordStatusMessage: IDisposable | null; + public get inChordMode(): boolean { + return !!this._currentChord; + } + constructor( private _contextKeyService: IContextKeyService, protected _commandService: ICommandService, diff --git a/src/vs/platform/keybinding/common/keybinding.ts b/src/vs/platform/keybinding/common/keybinding.ts index e11bd846c43..17ab82b8184 100644 --- a/src/vs/platform/keybinding/common/keybinding.ts +++ b/src/vs/platform/keybinding/common/keybinding.ts @@ -50,6 +50,8 @@ export const IKeybindingService = createDecorator('keybindin export interface IKeybindingService { _serviceBrand: undefined; + readonly inChordMode: boolean; + onDidUpdateKeybindings: Event; /** diff --git a/src/vs/platform/keybinding/common/keybindingResolver.ts b/src/vs/platform/keybinding/common/keybindingResolver.ts index 2ae709887b3..439e42d9948 100644 --- a/src/vs/platform/keybinding/common/keybindingResolver.ts +++ b/src/vs/platform/keybinding/common/keybindingResolver.ts @@ -11,7 +11,10 @@ import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKe import { keys } from 'vs/base/common/map'; export interface IResolveResult { + /** Whether the resolved keybinding is entering a chord */ enterChord: boolean; + /** Whether the resolved keybinding is leaving (and executing) a chord */ + leaveChord: boolean; commandId: string | null; commandArgs: any; bubble: boolean; @@ -285,6 +288,7 @@ export class KeybindingResolver { if (currentChord === null && result.keypressParts.length > 1 && result.keypressParts[1] !== null) { return { enterChord: true, + leaveChord: false, commandId: null, commandArgs: null, bubble: false @@ -293,6 +297,7 @@ export class KeybindingResolver { return { enterChord: false, + leaveChord: result.keypressParts.length > 1, commandId: result.command, commandArgs: result.commandArgs, bubble: result.bubble diff --git a/src/vs/platform/keybinding/test/common/mockKeybindingService.ts b/src/vs/platform/keybinding/test/common/mockKeybindingService.ts index c2062a4e4b4..8fcb795eda0 100644 --- a/src/vs/platform/keybinding/test/common/mockKeybindingService.ts +++ b/src/vs/platform/keybinding/test/common/mockKeybindingService.ts @@ -71,6 +71,8 @@ export class MockContextKeyService implements IContextKeyService { export class MockKeybindingService implements IKeybindingService { public _serviceBrand: undefined; + public readonly inChordMode: boolean = false; + public get onDidUpdateKeybindings(): Event { return Event.None; } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index cec072d72b3..a391706e51f 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -602,8 +602,8 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { // Respect chords if the allowChords setting is set and it's not Escape. Escape is // handled specially for Zen Mode's Escape, Escape chord, plus it's important in // terminals generally - const allowChords = resolveResult && resolveResult.enterChord && this._configHelper.config.allowChords && event.key !== 'Escape'; - if (allowChords || resolveResult && this._skipTerminalCommands.some(k => k === resolveResult.commandId)) { + const allowChords = resolveResult?.enterChord && this._configHelper.config.allowChords && event.key !== 'Escape'; + if (this._keybindingService.inChordMode || allowChords || resolveResult && this._skipTerminalCommands.some(k => k === resolveResult.commandId)) { event.preventDefault(); return false; } From a0dbbaa4559af59969f81d790506c7fce97ec8f0 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Tue, 25 Feb 2020 15:44:38 -0800 Subject: [PATCH 132/200] pluralization support to re close #89463 --- .../searchEditor/browser/searchEditorSerialization.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditorSerialization.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditorSerialization.ts index eca8524c25f..0e89108e1e8 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditorSerialization.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditorSerialization.ts @@ -211,9 +211,12 @@ export const serializeSearchResultForEditor = ? contentPatternToSearchResultHeader(searchResult.query, rawIncludePattern, rawExcludePattern, contextLines) : []; + const filecount = searchResult.fileCount() > 1 ? localize('numFiles', "{0} files", searchResult.fileCount()) : localize('oneFile', "1 file"); + const resultcount = searchResult.count() > 1 ? localize('numResults', "{0} results", searchResult.count()) : localize('oneResult', "1 result"); + const info = [ searchResult.count() - ? localize('resultCount', "{0} results in {1} files", searchResult.count(), searchResult.fileCount()) + ? `${filecount} - ${resultcount}` : localize('noResults', "No Results"), '']; From dbe62be3266ffb6772a7f3db0bf61d63f4aa7f65 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Tue, 25 Feb 2020 17:21:59 -0800 Subject: [PATCH 133/200] Fix #91343 --- src/vs/workbench/contrib/search/common/searchModel.ts | 9 +++++++++ .../searchEditor/browser/searchEditorSerialization.ts | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/search/common/searchModel.ts b/src/vs/workbench/contrib/search/common/searchModel.ts index bea090b9701..1505579db92 100644 --- a/src/vs/workbench/contrib/search/common/searchModel.ts +++ b/src/vs/workbench/contrib/search/common/searchModel.ts @@ -141,6 +141,15 @@ export class Match { return thisMatchPreviewLines.join('\n'); } + rangeInPreview() { + // convert to editor's base 1 positions. + return { + ...this._fullPreviewRange, + startColumn: this._fullPreviewRange.startColumn + 1, + endColumn: this._fullPreviewRange.endColumn + 1 + }; + } + fullPreviewLines(): string[] { return this._fullPreviewLines.slice(this._fullPreviewRange.startLineNumber, this._fullPreviewRange.endLineNumber + 1); } diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditorSerialization.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditorSerialization.ts index 0e89108e1e8..389faee3931 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditorSerialization.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditorSerialization.ts @@ -42,7 +42,7 @@ const matchToSearchResultFormat = (match: Match): { line: string, ranges: Range[ const rangeOnThisLine = ({ start, end }: { start?: number; end?: number; }) => new Range(1, (start ?? 1) + prefixOffset, 1, (end ?? sourceLine.length + 1) + prefixOffset); - const matchRange = match.range(); + const matchRange = match.rangeInPreview(); const matchIsSingleLine = matchRange.startLineNumber === matchRange.endLineNumber; let lineRange; From b0be0672c210f1fa76109ce675365bb785657e84 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Tue, 25 Feb 2020 20:28:18 -0800 Subject: [PATCH 134/200] Fix #91344. --- .../contrib/searchEditor/browser/constants.ts | 1 + .../browser/searchEditor.contribution.ts | 15 ++++++++++----- .../searchEditor/browser/searchEditorInput.ts | 4 ++-- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/searchEditor/browser/constants.ts b/src/vs/workbench/contrib/searchEditor/browser/constants.ts index 7b1b1d34825..3de68eda5a0 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/constants.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/constants.ts @@ -19,5 +19,6 @@ export const SelectAllSearchEditorMatchesCommandId = 'selectAllSearchEditorMatch export const InSearchEditor = new RawContextKey('inSearchEditor', false); export const SearchEditorScheme = 'search-editor'; +export const SearchEditorBodyScheme = 'search-editor-body'; export const SearchEditorFindMatchClass = 'seaarchEditorFindMatch'; diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts index 1f10071bcfd..57b857a860d 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts @@ -21,7 +21,6 @@ import { EditorDescriptor, Extensions as EditorExtensions, IEditorRegistry } fro import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { Extensions as EditorInputExtensions, IEditorInputFactory, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor'; -import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput'; import * as SearchConstants from 'vs/workbench/contrib/search/common/constants'; import * as SearchEditorConstants from 'vs/workbench/contrib/searchEditor/browser/constants'; import { SearchEditor } from 'vs/workbench/contrib/searchEditor/browser/searchEditor'; @@ -30,6 +29,7 @@ import { getOrMakeSearchEditorInput, SearchEditorInput } from 'vs/workbench/cont import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; +import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput'; //#region Editor Descriptior Registry.as(EditorExtensions.Editors).registerEditor( @@ -54,10 +54,15 @@ class SearchEditorContribution implements IWorkbenchContribution { ) { this.editorService.overrideOpenEditor((editor, options, group) => { - const resource = editor.resource; - if (!resource || - !(endsWith(resource.path, '.code-search') || resource.scheme === SearchEditorConstants.SearchEditorScheme) || - !(editor instanceof FileEditorInput || (resource.scheme === SearchEditorConstants.SearchEditorScheme))) { + let resource = editor.resource; + if (!resource) { return undefined; } + + if (resource.scheme === SearchEditorConstants.SearchEditorBodyScheme) { + resource = resource.with({ scheme: SearchEditorConstants.SearchEditorScheme }); + } + + if (resource.scheme !== SearchEditorConstants.SearchEditorScheme + && !(endsWith(resource.path, '.code-search') && editor instanceof FileEditorInput)) { return undefined; } diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditorInput.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditorInput.ts index 389caf35844..44b819f8abc 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditorInput.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditorInput.ts @@ -18,7 +18,7 @@ import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { EditorInput, GroupIdentifier, IEditorInput, IRevertOptions, ISaveOptions, IMoveResult } from 'vs/workbench/common/editor'; -import { SearchEditorFindMatchClass, SearchEditorScheme } from 'vs/workbench/contrib/searchEditor/browser/constants'; +import { SearchEditorFindMatchClass, SearchEditorScheme, SearchEditorBodyScheme } from 'vs/workbench/contrib/searchEditor/browser/constants'; import { extractSearchQuery, serializeSearchConfiguration } from 'vs/workbench/contrib/searchEditor/browser/searchEditorSerialization'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; @@ -339,7 +339,7 @@ export const getOrMakeSearchEditorInput = ( } } - const contentsModelURI = uri.with({ scheme: 'search-editor-body' }); + const contentsModelURI = uri.with({ scheme: SearchEditorBodyScheme }); const headerModelURI = uri.with({ scheme: 'search-editor-header' }); const contentsModel = modelService.getModel(contentsModelURI) ?? modelService.createModel('', modeService.create('search-result'), contentsModelURI); const headerModel = modelService.getModel(headerModelURI) ?? modelService.createModel('', modeService.create('search-result'), headerModelURI); From 091b317bc64dc9252dafe6c1c96fddc692da847d Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 26 Feb 2020 07:37:29 +0100 Subject: [PATCH 135/200] Notification bell with badge and "no notifications" hover (fix #91444) --- .../browser/parts/notifications/notificationsStatus.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/notifications/notificationsStatus.ts b/src/vs/workbench/browser/parts/notifications/notificationsStatus.ts index 71a82ef3aae..1bdde771ccb 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsStatus.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsStatus.ts @@ -46,7 +46,7 @@ export class NotificationsStatus extends Disposable { if (!this.isNotificationsCenterVisible) { if (e.kind === NotificationChangeType.ADD) { this.newNotificationsCount++; - } else if (e.kind === NotificationChangeType.REMOVE) { + } else if (e.kind === NotificationChangeType.REMOVE && this.newNotificationsCount > 0) { this.newNotificationsCount--; } } From 7cfbe2e6af6ab5e33864cae5242488d1cff8464c Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 26 Feb 2020 07:52:49 +0100 Subject: [PATCH 136/200] Notification: X button shows up if progress is collapsed (fix #91414) --- .../notification/common/notification.ts | 24 +++++++++++++++++++ src/vs/workbench/common/notifications.ts | 21 ++++++++++++++-- .../progress/browser/progressService.ts | 3 ++- .../test/common/notifications.test.ts | 9 +++++-- 4 files changed, 52 insertions(+), 5 deletions(-) diff --git a/src/vs/platform/notification/common/notification.ts b/src/vs/platform/notification/common/notification.ts index aff1675b214..a81672c8311 100644 --- a/src/vs/platform/notification/common/notification.ts +++ b/src/vs/platform/notification/common/notification.ts @@ -101,6 +101,12 @@ export interface INotification extends INotificationProperties { * this usecase and much easier to use! */ actions?: INotificationActions; + + /** + * The initial set of progress properties for the notification. To update progress + * later on, access the `INotificationHandle.progress` property. + */ + progress?: INotificationProgressProperties; } export interface INotificationActions { @@ -119,6 +125,24 @@ export interface INotificationActions { secondary?: ReadonlyArray; } +export interface INotificationProgressProperties { + + /** + * Causes the progress bar to spin infinitley. + */ + infinite?: boolean; + + /** + * Indicate the total amount of work. + */ + total?: number; + + /** + * Indicate that a specific chunk of work is done. + */ + worked?: number; +} + export interface INotificationProgress { /** diff --git a/src/vs/workbench/common/notifications.ts b/src/vs/workbench/common/notifications.ts index 353baa3bd6e..73f0ef74053 100644 --- a/src/vs/workbench/common/notifications.ts +++ b/src/vs/workbench/common/notifications.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { INotification, INotificationHandle, INotificationActions, INotificationProgress, NoOpNotification, Severity, NotificationMessage, IPromptChoice, IStatusMessageOptions, NotificationsFilter } from 'vs/platform/notification/common/notification'; +import { INotification, INotificationHandle, INotificationActions, INotificationProgress, NoOpNotification, Severity, NotificationMessage, IPromptChoice, IStatusMessageOptions, NotificationsFilter, INotificationProgressProperties } from 'vs/platform/notification/common/notification'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { Event, Emitter } from 'vs/base/common/event'; import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; @@ -435,7 +435,7 @@ export class NotificationViewItem extends Disposable implements INotificationVie actions = { primary: notification.message.actions }; } - return new NotificationViewItem(severity, notification.sticky, notification.silent || filter === NotificationsFilter.SILENT || (filter === NotificationsFilter.ERROR && notification.severity !== Severity.Error), message, notification.source, actions); + return new NotificationViewItem(severity, notification.sticky, notification.silent || filter === NotificationsFilter.SILENT || (filter === NotificationsFilter.ERROR && notification.severity !== Severity.Error), message, notification.source, notification.progress, actions); } private static parseNotificationMessage(input: NotificationMessage): INotificationMessage | undefined { @@ -472,13 +472,30 @@ export class NotificationViewItem extends Disposable implements INotificationVie private _silent: boolean | undefined, private _message: INotificationMessage, private _source: string | undefined, + progress: INotificationProgressProperties | undefined, actions?: INotificationActions ) { super(); + if (progress) { + this.setProgress(progress); + } + this.setActions(actions); } + private setProgress(progress: INotificationProgressProperties): void { + if (progress.infinite) { + this.progress.infinite(); + } else if (progress.total) { + this.progress.total(progress.total); + + if (progress.worked) { + this.progress.worked(progress.worked); + } + } + } + private setActions(actions: INotificationActions = { primary: [], secondary: [] }): void { if (!Array.isArray(actions.primary)) { actions.primary = []; diff --git a/src/vs/workbench/services/progress/browser/progressService.ts b/src/vs/workbench/services/progress/browser/progressService.ts index 296e4f47bf5..6d4723b34e3 100644 --- a/src/vs/workbench/services/progress/browser/progressService.ts +++ b/src/vs/workbench/services/progress/browser/progressService.ts @@ -233,7 +233,8 @@ export class ProgressService extends Disposable implements IProgressService { severity: Severity.Info, message, source: options.source, - actions: { primary: primaryActions, secondary: secondaryActions } + actions: { primary: primaryActions, secondary: secondaryActions }, + progress: typeof increment === 'number' && increment >= 0 ? { total: 100, worked: increment } : { infinite: true } }); updateProgress(handle, increment); diff --git a/src/vs/workbench/test/common/notifications.test.ts b/src/vs/workbench/test/common/notifications.test.ts index 8984e0ab869..7eefd25cb8e 100644 --- a/src/vs/workbench/test/common/notifications.test.ts +++ b/src/vs/workbench/test/common/notifications.test.ts @@ -23,6 +23,7 @@ suite('Notifications', () => { let item3 = NotificationViewItem.create({ severity: Severity.Info, message: 'Info Message' })!; let item4 = NotificationViewItem.create({ severity: Severity.Error, message: 'Error Message', source: 'Source' })!; let item5 = NotificationViewItem.create({ severity: Severity.Error, message: 'Error Message', actions: { primary: [new Action('id', 'label')] } })!; + let item6 = NotificationViewItem.create({ severity: Severity.Error, message: 'Error Message', actions: { primary: [new Action('id', 'label')] }, progress: { infinite: true } })!; assert.equal(item1.equals(item1), true); assert.equal(item2.equals(item2), true); @@ -35,6 +36,10 @@ suite('Notifications', () => { assert.equal(item1.equals(item4), false); assert.equal(item1.equals(item5), false); + // Progress + assert.equal(item1.hasProgress, false); + assert.equal(item6.hasProgress, true); + // Message Box assert.equal(item5.canCollapse, false); assert.equal(item5.expanded, true); @@ -102,8 +107,8 @@ suite('Notifications', () => { assert.equal(called, 1); // Error with Action - let item6 = NotificationViewItem.create({ severity: Severity.Error, message: createErrorWithActions('Hello Error', { actions: [new Action('id', 'label')] }) })!; - assert.equal(item6.actions!.primary!.length, 1); + let item7 = NotificationViewItem.create({ severity: Severity.Error, message: createErrorWithActions('Hello Error', { actions: [new Action('id', 'label')] }) })!; + assert.equal(item7.actions!.primary!.length, 1); // Filter let item8 = NotificationViewItem.create({ severity: Severity.Error, message: 'Error Message' }, NotificationsFilter.SILENT)!; From 3de5a8a6a2ea77f08174f1c5341248401a377973 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 26 Feb 2020 08:36:51 +0100 Subject: [PATCH 137/200] Adding already disposed disposable to DisposableStore in textFileEditorModel.ts (fix #91396) --- .../textfile/common/textFileEditorModel.ts | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index 7afb0645e24..67332750e50 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -251,6 +251,13 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil async load(options?: ITextFileLoadOptions): Promise { this.logService.trace('[text file model] load() - enter', this.resource.toString(true)); + // Return early if we are disposed + if (this.isDisposed()) { + this.logService.trace('[text file model] load() - exit - without loading because model is disposed', this.resource.toString(true)); + + return this; + } + // It is very important to not reload the model when the model is dirty. // We also only want to reload the model from the disk if no save is pending // to avoid data loss. @@ -359,7 +366,14 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } private loadFromContent(content: ITextFileStreamContent, options?: ITextFileLoadOptions, fromBackup?: boolean): TextFileEditorModel { - this.logService.trace('[text file model] load() - resolved content', this.resource.toString(true)); + this.logService.trace('[text file model] loadFromContent() - enter', this.resource.toString(true)); + + // Return early if we are disposed + if (this.isDisposed()) { + this.logService.trace('[text file model] loadFromContent() - exit - because model is disposed', this.resource.toString(true)); + + return this; + } // Update our resolved disk stat model this.updateLastResolvedFileStat({ @@ -405,7 +419,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } private doCreateTextModel(resource: URI, value: ITextBufferFactory, fromBackup: boolean): void { - this.logService.trace('[text file model] load() - created text editor model', this.resource.toString(true)); + this.logService.trace('[text file model] doCreateTextModel()', this.resource.toString(true)); // Create model const textModel = this.createTextEditorModel(value, resource, this.preferredMode); @@ -420,7 +434,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } private doUpdateTextModel(value: ITextBufferFactory): void { - this.logService.trace('[text file model] load() - updated text editor model', this.resource.toString(true)); + this.logService.trace('[text file model] doUpdateTextModel()', this.resource.toString(true)); // Update model value in a block that ignores content change events for dirty tracking this.ignoreDirtyOnModelContentChange = true; @@ -703,7 +717,6 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } private handleSaveSuccess(stat: IFileStatWithMetadata, versionId: number, options: ITextFileSaveOptions): void { - this.logService.trace(`[text file model] doSave(${versionId}) - after write()`, this.resource.toString(true)); // Updated resolved stat with updated stat this.updateLastResolvedFileStat(stat); From afcaaa613186832493bb1813dee048890a3c2a2f Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 26 Feb 2020 09:04:03 +0100 Subject: [PATCH 138/200] smoke - use tree kill also here --- test/automation/package.json | 3 ++- test/automation/src/playwrightDriver.ts | 12 +++++------- test/automation/yarn.lock | 5 +++++ 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/test/automation/package.json b/test/automation/package.json index a3f3baf2f40..8fced3ba824 100644 --- a/test/automation/package.json +++ b/test/automation/package.json @@ -28,7 +28,8 @@ "concurrently": "^3.5.1", "cpx": "^1.5.0", "typescript": "3.7.5", - "watch": "^1.0.2" + "watch": "^1.0.2", + "tree-kill": "1.2.2" }, "dependencies": { "mkdirp": "^0.5.1", diff --git a/test/automation/src/playwrightDriver.ts b/test/automation/src/playwrightDriver.ts index 40ee4c82851..cc0c5547db3 100644 --- a/test/automation/src/playwrightDriver.ts +++ b/test/automation/src/playwrightDriver.ts @@ -10,6 +10,7 @@ import { mkdir } from 'fs'; import { promisify } from 'util'; import { IDriver, IDisposable } from './driver'; import { URI } from 'vscode-uri'; +import * as kill from 'tree-kill'; const width = 1200; const height = 800; @@ -93,6 +94,7 @@ let workspacePath: string | undefined; export async function launch(userDataDir: string, _workspacePath: string, codeServerPath = process.env.VSCODE_REMOTE_SERVER_PATH): Promise { workspacePath = _workspacePath; + const agentFolder = userDataDir; await promisify(mkdir)(agentFolder); const env = { @@ -121,7 +123,7 @@ export async function launch(userDataDir: string, _workspacePath: string, codeSe function teardown(): void { if (server) { - server.kill(); + kill(server.pid); server = undefined; } } @@ -137,13 +139,9 @@ function waitForEndpoint(): Promise { }); } -export function connect(engine: 'chromium' | 'webkit' | 'firefox' = 'chromium'): Promise<{ client: IDisposable, driver: IDriver }> { +export function connect(browserType: 'chromium' | 'webkit' | 'firefox' = 'chromium'): Promise<{ client: IDisposable, driver: IDriver }> { return new Promise(async (c) => { - const browser = await playwright[engine].launch({ - // Run in Edge dev on macOS - // executablePath: '/Applications/Microsoft\ Edge\ Dev.app/Contents/MacOS/Microsoft\ Edge\ Dev', - headless: false - }); + const browser = await playwright[browserType].launch({ headless: false, dumpio: true }); const context = await browser.newContext(); const page = await context.newPage(); await page.setViewportSize({ width, height }); diff --git a/test/automation/yarn.lock b/test/automation/yarn.lock index 98c63c91e0d..11785373d40 100644 --- a/test/automation/yarn.lock +++ b/test/automation/yarn.lock @@ -1634,6 +1634,11 @@ to-regex@^3.0.1, to-regex@^3.0.2: regex-not "^1.0.2" safe-regex "^1.1.0" +tree-kill@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" + integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== + tree-kill@^1.1.0: version "1.2.1" resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.1.tgz#5398f374e2f292b9dcc7b2e71e30a5c3bb6c743a" From fd407adeda50c39e50ed7a7d6227abae479ce256 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 26 Feb 2020 10:40:35 +0100 Subject: [PATCH 139/200] Fixes #91051: Have Shift+Insert paste from the clipboard by default (like Ctrl+V) --- src/vs/editor/contrib/clipboard/clipboard.ts | 1 + .../electron-browser/selectionClipboard.ts | 13 +------------ 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/src/vs/editor/contrib/clipboard/clipboard.ts b/src/vs/editor/contrib/clipboard/clipboard.ts index cb106b53b82..321543523a0 100644 --- a/src/vs/editor/contrib/clipboard/clipboard.ts +++ b/src/vs/editor/contrib/clipboard/clipboard.ts @@ -161,6 +161,7 @@ class ExecCommandPasteAction extends ExecCommandAction { kbExpr: EditorContextKeys.textInputFocus, primary: KeyMod.CtrlCmd | KeyCode.KEY_V, win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_V, secondary: [KeyMod.Shift | KeyCode.Insert] }, + linux: { primary: KeyMod.CtrlCmd | KeyCode.KEY_V, secondary: [KeyMod.Shift | KeyCode.Insert] }, weight: KeybindingWeight.EditorContrib }; // Do not bind paste keybindings in the browser, diff --git a/src/vs/workbench/contrib/codeEditor/electron-browser/selectionClipboard.ts b/src/vs/workbench/contrib/codeEditor/electron-browser/selectionClipboard.ts index 536d0f75c5c..592605c0759 100644 --- a/src/vs/workbench/contrib/codeEditor/electron-browser/selectionClipboard.ts +++ b/src/vs/workbench/contrib/codeEditor/electron-browser/selectionClipboard.ts @@ -20,10 +20,7 @@ import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; export class SelectionClipboard extends Disposable implements IEditorContribution { private static readonly SELECTION_LENGTH_LIMIT = 65536; @@ -119,15 +116,7 @@ class PasteSelectionClipboardAction extends EditorAction { id: 'editor.action.selectionClipboardPaste', label: nls.localize('actions.pasteSelectionClipboard', "Paste Selection Clipboard"), alias: 'Paste Selection Clipboard', - precondition: EditorContextKeys.writable, - kbOpts: { - kbExpr: ContextKeyExpr.and( - EditorContextKeys.editorTextFocus, - ContextKeyExpr.has('config.editor.selectionClipboard') - ), - primary: KeyMod.Shift | KeyCode.Insert, - weight: KeybindingWeight.EditorContrib - } + precondition: EditorContextKeys.writable }); } From b9809ceca805e44e9809e9023a4017c6d7d3ab2d Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 26 Feb 2020 11:06:26 +0100 Subject: [PATCH 140/200] fixes #90478 --- src/vs/workbench/contrib/debug/browser/debug.contribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index 30ae2db8bbf..d01fecdbb3c 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -267,7 +267,7 @@ configurationRegistry.registerConfiguration({ default: true }, 'debug.onTaskErrors': { - enum: ['debugAnyway', 'showErrors', 'prompt', 'cancel'], + enum: ['debugAnyway', 'showErrors', 'prompt', 'abort'], enumDescriptions: [nls.localize('debugAnyway', "Ignore task errors and start debugging."), nls.localize('showErrors', "Show the Problems view and do not start debugging."), nls.localize('prompt', "Prompt user."), nls.localize('cancel', "Cancel debugging.")], description: nls.localize('debug.onTaskErrors', "Controls what to do when errors are encountered after running a preLaunchTask."), default: 'prompt' From ebcd432491259c91013bb0ff0284610f7f84504d Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 26 Feb 2020 11:39:05 +0100 Subject: [PATCH 141/200] docs for #91532 --- test/integration/browser/README.md | 1 + test/smoke/README.md | 1 + 2 files changed, 2 insertions(+) diff --git a/test/integration/browser/README.md b/test/integration/browser/README.md index 10a55f7de17..8b36a3c172c 100644 --- a/test/integration/browser/README.md +++ b/test/integration/browser/README.md @@ -14,6 +14,7 @@ All integration tests run in an Electron instance. You can specify to run the te ## Run (inside browser) + yarn gulp mixin-server resources/server/test/test-web-integration.[sh|bat] --browser [chromium|webkit] [--debug] All integration tests run in a browser instance as specified by the command line arguments. diff --git a/test/smoke/README.md b/test/smoke/README.md index 8b41d05ed08..8836886be6b 100644 --- a/test/smoke/README.md +++ b/test/smoke/README.md @@ -13,6 +13,7 @@ yarn --cwd test/automation yarn smoketest # Dev (Web) +yarn gulp mixin-server yarn smoketest --web --browser [chromium|firefox|webkit] # Build (Electron) From f0066fe680e8c1de0c201a3da68201ce0f52af87 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 26 Feb 2020 11:55:43 +0100 Subject: [PATCH 142/200] automation: refactor web tests not to use outPath --- test/automation/src/code.ts | 116 ++++++++++++++++++------------------ 1 file changed, 58 insertions(+), 58 deletions(-) diff --git a/test/automation/src/code.ts b/test/automation/src/code.ts index 3b5a667f81f..dcd75fe1e6f 100644 --- a/test/automation/src/code.ts +++ b/test/automation/src/code.ts @@ -115,9 +115,6 @@ async function createDriverHandle(): Promise { } export async function spawn(options: SpawnOptions): Promise { - const codePath = options.codePath; - const electronPath = codePath ? getBuildElectronPath(codePath) : getDevElectronPath(); - const outPath = codePath ? getBuildOutPath(codePath) : getDevOutPath(); const handle = await createDriverHandle(); let child: cp.ChildProcess | undefined; @@ -126,63 +123,66 @@ export async function spawn(options: SpawnOptions): Promise { if (options.web) { await launch(options.userDataDir, options.workspacePath, options.codePath); connectDriver = connectPlaywrightDriver.bind(connectPlaywrightDriver, options.browser); - } else { - const env = process.env; - - const args = [ - options.workspacePath, - '--skip-getting-started', - '--skip-release-notes', - '--sticky-quickopen', - '--disable-telemetry', - '--disable-updates', - '--disable-crash-reporter', - `--extensions-dir=${options.extensionsPath}`, - `--user-data-dir=${options.userDataDir}`, - '--driver', handle - ]; - - if (options.remote) { - // Replace workspace path with URI - args[0] = `--${options.workspacePath.endsWith('.code-workspace') ? 'file' : 'folder'}-uri=vscode-remote://test+test/${URI.file(options.workspacePath).path}`; - - if (codePath) { - // running against a build: copy the test resolver extension - const testResolverExtPath = path.join(options.extensionsPath, 'vscode-test-resolver'); - if (!fs.existsSync(testResolverExtPath)) { - const orig = path.join(repoPath, 'extensions', 'vscode-test-resolver'); - await new Promise((c, e) => ncp(orig, testResolverExtPath, err => err ? e(err) : c())); - } - } - args.push('--enable-proposed-api=vscode.vscode-test-resolver'); - const remoteDataDir = `${options.userDataDir}-server`; - mkdirp.sync(remoteDataDir); - env['TESTRESOLVER_DATA_FOLDER'] = remoteDataDir; - } - - if (!codePath) { - args.unshift(repoPath); - } - - if (options.verbose) { - args.push('--driver-verbose'); - } - - if (options.log) { - args.push('--log', options.log); - } - - if (options.extraArgs) { - args.push(...options.extraArgs); - } - - const spawnOptions: cp.SpawnOptions = { env }; - child = cp.spawn(electronPath, args, spawnOptions); - instances.add(child); - child.once('exit', () => instances.delete(child!)); - connectDriver = connectElectronDriver; + return connect(connectDriver, child, '', handle, options.logger); } + const env = process.env; + const codePath = options.codePath; + const outPath = codePath ? getBuildOutPath(codePath) : getDevOutPath(); + + const args = [ + options.workspacePath, + '--skip-getting-started', + '--skip-release-notes', + '--sticky-quickopen', + '--disable-telemetry', + '--disable-updates', + '--disable-crash-reporter', + `--extensions-dir=${options.extensionsPath}`, + `--user-data-dir=${options.userDataDir}`, + '--driver', handle + ]; + + if (options.remote) { + // Replace workspace path with URI + args[0] = `--${options.workspacePath.endsWith('.code-workspace') ? 'file' : 'folder'}-uri=vscode-remote://test+test/${URI.file(options.workspacePath).path}`; + + if (codePath) { + // running against a build: copy the test resolver extension + const testResolverExtPath = path.join(options.extensionsPath, 'vscode-test-resolver'); + if (!fs.existsSync(testResolverExtPath)) { + const orig = path.join(repoPath, 'extensions', 'vscode-test-resolver'); + await new Promise((c, e) => ncp(orig, testResolverExtPath, err => err ? e(err) : c())); + } + } + args.push('--enable-proposed-api=vscode.vscode-test-resolver'); + const remoteDataDir = `${options.userDataDir}-server`; + mkdirp.sync(remoteDataDir); + env['TESTRESOLVER_DATA_FOLDER'] = remoteDataDir; + } + + if (!codePath) { + args.unshift(repoPath); + } + + if (options.verbose) { + args.push('--driver-verbose'); + } + + if (options.log) { + args.push('--log', options.log); + } + + if (options.extraArgs) { + args.push(...options.extraArgs); + } + + const electronPath = codePath ? getBuildElectronPath(codePath) : getDevElectronPath(); + const spawnOptions: cp.SpawnOptions = { env }; + child = cp.spawn(electronPath, args, spawnOptions); + instances.add(child); + child.once('exit', () => instances.delete(child!)); + connectDriver = connectElectronDriver; return connect(connectDriver, child, outPath, handle, options.logger); } From 2cb430b396bc2032042449a856c44e4830ebdac7 Mon Sep 17 00:00:00 2001 From: Dirk Baeumer Date: Wed, 26 Feb 2020 11:54:44 +0100 Subject: [PATCH 143/200] Fixes #91433: Smoke Test: Localization Test Failure --- test/smoke/src/areas/workbench/localization.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/smoke/src/areas/workbench/localization.test.ts b/test/smoke/src/areas/workbench/localization.test.ts index 5db5fe2b74a..07e9199080e 100644 --- a/test/smoke/src/areas/workbench/localization.test.ts +++ b/test/smoke/src/areas/workbench/localization.test.ts @@ -37,10 +37,10 @@ export function setup() { await app.workbench.scm.waitForTitle(title => /quellcodeverwaltung/i.test(title)); await app.workbench.debug.openDebugViewlet(); - await app.workbench.debug.waitForTitle(title => /debug/i.test(title)); + await app.workbench.debug.waitForTitle(title => /starten/i.test(title)); - // await app.workbench.extensions.openExtensionsViewlet(); - // await app.workbench.extensions.waitForTitle(title => /erweiterungen/i.test(title)); + await app.workbench.extensions.openExtensionsViewlet(); + await app.workbench.extensions.waitForTitle(title => /extensions/i.test(title)); }); }); } From cb2acf650c15e667e911dbd99341588616e259f2 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 26 Feb 2020 11:32:19 +0100 Subject: [PATCH 144/200] Clarify StateDelta usage --- .../browser/parts/editor/editorStatus.ts | 54 +++++++++---------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorStatus.ts b/src/vs/workbench/browser/parts/editor/editorStatus.ts index 18ba6d61dcb..394bb32fa84 100644 --- a/src/vs/workbench/browser/parts/editor/editorStatus.ts +++ b/src/vs/workbench/browser/parts/editor/editorStatus.ts @@ -170,16 +170,16 @@ class StateChange { } } -interface StateDelta { - selectionStatus?: string; - mode?: string; - encoding?: string; - EOL?: string; - indentation?: string; - tabFocusMode?: boolean; - screenReaderMode?: boolean; - metadata?: string | undefined; -} +type StateDelta = ( + { type: 'selectionStatus'; selectionStatus: string | undefined; } + | { type: 'mode'; mode: string | undefined; } + | { type: 'encoding'; encoding: string | undefined; } + | { type: 'EOL'; EOL: string | undefined; } + | { type: 'indentation'; indentation: string | undefined; } + | { type: 'tabFocusMode'; tabFocusMode: boolean; } + | { type: 'screenReaderMode'; screenReaderMode: boolean; } + | { type: 'metadata'; metadata: string | undefined; } +); class State { @@ -210,56 +210,56 @@ class State { update(update: StateDelta): StateChange { const change = new StateChange(); - if ('selectionStatus' in update) { + if (update.type === 'selectionStatus') { if (this._selectionStatus !== update.selectionStatus) { this._selectionStatus = update.selectionStatus; change.selectionStatus = true; } } - if ('indentation' in update) { + if (update.type === 'indentation') { if (this._indentation !== update.indentation) { this._indentation = update.indentation; change.indentation = true; } } - if ('mode' in update) { + if (update.type === 'mode') { if (this._mode !== update.mode) { this._mode = update.mode; change.mode = true; } } - if ('encoding' in update) { + if (update.type === 'encoding') { if (this._encoding !== update.encoding) { this._encoding = update.encoding; change.encoding = true; } } - if ('EOL' in update) { + if (update.type === 'EOL') { if (this._EOL !== update.EOL) { this._EOL = update.EOL; change.EOL = true; } } - if ('tabFocusMode' in update) { + if (update.type === 'tabFocusMode') { if (this._tabFocusMode !== update.tabFocusMode) { this._tabFocusMode = update.tabFocusMode; change.tabFocusMode = true; } } - if ('screenReaderMode' in update) { + if (update.type === 'screenReaderMode') { if (this._screenReaderMode !== update.screenReaderMode) { this._screenReaderMode = update.screenReaderMode; change.screenReaderMode = true; } } - if ('metadata' in update) { + if (update.type === 'metadata') { if (this._metadata !== update.metadata) { this._metadata = update.metadata; change.metadata = true; @@ -665,14 +665,14 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution { } private onModeChange(editorWidget: ICodeEditor | undefined, editorInput: IEditorInput | undefined): void { - let info: StateDelta = { mode: undefined }; + let info: StateDelta = { type: 'mode', mode: undefined }; // We only support text based editors if (editorWidget && editorInput && toEditorWithModeSupport(editorInput)) { const textModel = editorWidget.getModel(); if (textModel) { const modeId = textModel.getLanguageIdentifier().language; - info = { mode: withNullAsUndefined(this.modeService.getLanguageName(modeId)) }; + info.mode = withNullAsUndefined(this.modeService.getLanguageName(modeId)); } } @@ -680,7 +680,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution { } private onIndentationChange(editorWidget: ICodeEditor | undefined): void { - const update: StateDelta = { indentation: undefined }; + const update: StateDelta = { type: 'indentation', indentation: undefined }; if (editorWidget) { const model = editorWidget.getModel(); @@ -698,7 +698,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution { } private onMetadataChange(editor: IBaseEditor | undefined): void { - const update: StateDelta = { metadata: undefined }; + const update: StateDelta = { type: 'metadata', metadata: undefined }; if (editor instanceof BaseBinaryResourceEditor || editor instanceof BinaryResourceDiffEditor) { update.metadata = editor.getMetadata(); @@ -730,7 +730,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution { this.screenReaderNotification.close(); } - this.updateState({ screenReaderMode: screenReaderMode }); + this.updateState({ type: 'screenReaderMode', screenReaderMode: screenReaderMode }); } private onSelectionChange(editorWidget: ICodeEditor | undefined): void { @@ -770,11 +770,11 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution { } } - this.updateState({ selectionStatus: this.getSelectionLabel(info) }); + this.updateState({ type: 'selectionStatus', selectionStatus: this.getSelectionLabel(info) }); } private onEOLChange(editorWidget: ICodeEditor | undefined): void { - const info: StateDelta = { EOL: undefined }; + const info: StateDelta = { type: 'EOL', EOL: undefined }; if (editorWidget && !editorWidget.getOption(EditorOption.readOnly)) { const codeEditorModel = editorWidget.getModel(); @@ -791,7 +791,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution { return; } - const info: StateDelta = { encoding: undefined }; + const info: StateDelta = { type: 'encoding', encoding: undefined }; // We only support text based editors that have a model associated // This ensures we do not show the encoding picker while an editor @@ -825,7 +825,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution { } private onTabFocusModeChange(): void { - const info: StateDelta = { tabFocusMode: TabFocus.getTabFocusMode() }; + const info: StateDelta = { type: 'tabFocusMode', tabFocusMode: TabFocus.getTabFocusMode() }; this.updateState(info); } From 279b5482851fd4641bfadbf517b2511cf0965de6 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 26 Feb 2020 11:58:09 +0100 Subject: [PATCH 145/200] Fixes #91362: Render Column Selection mode in the status bar when enabled --- .../browser/parts/editor/editorStatus.ts | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/vs/workbench/browser/parts/editor/editorStatus.ts b/src/vs/workbench/browser/parts/editor/editorStatus.ts index 394bb32fa84..34ae3b128ab 100644 --- a/src/vs/workbench/browser/parts/editor/editorStatus.ts +++ b/src/vs/workbench/browser/parts/editor/editorStatus.ts @@ -144,6 +144,7 @@ class StateChange { encoding: boolean = false; EOL: boolean = false; tabFocusMode: boolean = false; + columnSelectionMode: boolean = false; screenReaderMode: boolean = false; metadata: boolean = false; @@ -154,6 +155,7 @@ class StateChange { this.encoding = this.encoding || other.encoding; this.EOL = this.EOL || other.EOL; this.tabFocusMode = this.tabFocusMode || other.tabFocusMode; + this.columnSelectionMode = this.columnSelectionMode || other.columnSelectionMode; this.screenReaderMode = this.screenReaderMode || other.screenReaderMode; this.metadata = this.metadata || other.metadata; } @@ -165,6 +167,7 @@ class StateChange { || this.encoding || this.EOL || this.tabFocusMode + || this.columnSelectionMode || this.screenReaderMode || this.metadata; } @@ -177,6 +180,7 @@ type StateDelta = ( | { type: 'EOL'; EOL: string | undefined; } | { type: 'indentation'; indentation: string | undefined; } | { type: 'tabFocusMode'; tabFocusMode: boolean; } + | { type: 'columnSelectionMode'; columnSelectionMode: boolean; } | { type: 'screenReaderMode'; screenReaderMode: boolean; } | { type: 'metadata'; metadata: string | undefined; } ); @@ -201,6 +205,9 @@ class State { private _tabFocusMode: boolean | undefined; get tabFocusMode(): boolean | undefined { return this._tabFocusMode; } + private _columnSelectionMode: boolean | undefined; + get columnSelectionMode(): boolean | undefined { return this._columnSelectionMode; } + private _screenReaderMode: boolean | undefined; get screenReaderMode(): boolean | undefined { return this._screenReaderMode; } @@ -252,6 +259,13 @@ class State { } } + if (update.type === 'columnSelectionMode') { + if (this._columnSelectionMode !== update.columnSelectionMode) { + this._columnSelectionMode = update.columnSelectionMode; + change.columnSelectionMode = true; + } + } + if (update.type === 'screenReaderMode') { if (this._screenReaderMode !== update.screenReaderMode) { this._screenReaderMode = update.screenReaderMode; @@ -279,6 +293,7 @@ const nlsEOLCRLF = nls.localize('endOfLineCarriageReturnLineFeed', "CRLF"); export class EditorStatus extends Disposable implements IWorkbenchContribution { private readonly tabFocusModeElement = this._register(new MutableDisposable()); + private readonly columnSelectionModeElement = this._register(new MutableDisposable()); private readonly screenRedearModeElement = this._register(new MutableDisposable()); private readonly indentationElement = this._register(new MutableDisposable()); private readonly selectionElement = this._register(new MutableDisposable()); @@ -399,6 +414,22 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution { } } + private updateColumnSelectionModeElement(visible: boolean): void { + if (visible) { + if (!this.columnSelectionModeElement.value) { + this.columnSelectionModeElement.value = this.statusbarService.addEntry({ + text: nls.localize('columnSelectionModeEnabled', "Column Selection"), + tooltip: nls.localize('disableColumnSelectionMode', "Disable Column Selection Mode"), + command: 'editor.action.toggleColumnSelection', + backgroundColor: themeColorFromId(STATUS_BAR_PROMINENT_ITEM_BACKGROUND), + color: themeColorFromId(STATUS_BAR_PROMINENT_ITEM_FOREGROUND) + }, 'status.editor.columnSelectionMode', nls.localize('status.editor.columnSelectionMode', "Column Selection Mode"), StatusbarAlignment.RIGHT, 100.8); + } + } else { + this.columnSelectionModeElement.clear(); + } + } + private updateScreenReaderModeElement(visible: boolean): void { if (visible) { if (!this.screenRedearModeElement.value) { @@ -541,6 +572,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution { private doRenderNow(changed: StateChange): void { this.updateTabFocusModeElement(!!this.state.tabFocusMode); + this.updateColumnSelectionModeElement(!!this.state.columnSelectionMode); this.updateScreenReaderModeElement(!!this.state.screenReaderMode); this.updateIndentationElement(this.state.indentation); this.updateSelectionElement(this.state.selectionStatus && !this.state.screenReaderMode ? this.state.selectionStatus : undefined); @@ -580,6 +612,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution { const activeCodeEditor = activeControl ? withNullAsUndefined(getCodeEditor(activeControl.getControl())) : undefined; // Update all states + this.onColumnSelectionModeChange(activeCodeEditor); this.onScreenReaderModeChange(activeCodeEditor); this.onSelectionChange(activeCodeEditor); this.onModeChange(activeCodeEditor, activeInput); @@ -597,6 +630,9 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution { // Hook Listener for Configuration changes this.activeEditorListeners.add(activeCodeEditor.onDidChangeConfiguration((event: ConfigurationChangedEvent) => { + if (event.hasChanged(EditorOption.columnSelection)) { + this.onColumnSelectionModeChange(activeCodeEditor); + } if (event.hasChanged(EditorOption.accessibilitySupport)) { this.onScreenReaderModeChange(activeCodeEditor); } @@ -707,6 +743,16 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution { this.updateState(update); } + private onColumnSelectionModeChange(editorWidget: ICodeEditor | undefined): void { + const info: StateDelta = { type: 'columnSelectionMode', columnSelectionMode: false }; + + if (editorWidget && editorWidget.getOption(EditorOption.columnSelection)) { + info.columnSelectionMode = true; + } + + this.updateState(info); + } + private onScreenReaderModeChange(editorWidget: ICodeEditor | undefined): void { let screenReaderMode = false; From 29c2c6079e50de139ef5936ad66364a8e9f9f727 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 26 Feb 2020 12:03:05 +0100 Subject: [PATCH 146/200] smoke - do not restore windows --- test/automation/src/code.ts | 1 + test/smoke/src/main.ts | 35 +++++++++++++---------------------- 2 files changed, 14 insertions(+), 22 deletions(-) diff --git a/test/automation/src/code.ts b/test/automation/src/code.ts index dcd75fe1e6f..34bd09b739e 100644 --- a/test/automation/src/code.ts +++ b/test/automation/src/code.ts @@ -140,6 +140,7 @@ export async function spawn(options: SpawnOptions): Promise { '--disable-crash-reporter', `--extensions-dir=${options.extensionsPath}`, `--user-data-dir=${options.userDataDir}`, + `--disable-restore-windows`, '--driver', handle ]; diff --git a/test/smoke/src/main.ts b/test/smoke/src/main.ts index 8122d235289..a1273ba379d 100644 --- a/test/smoke/src/main.ts +++ b/test/smoke/src/main.ts @@ -57,8 +57,7 @@ const opts = minimist(args, { boolean: [ 'verbose', 'remote', - 'web', - 'ci' + 'web' ], default: { verbose: false @@ -299,24 +298,16 @@ describe(`VSCode Smoke Tests (${opts.web ? 'Web' : 'Electron'})`, () => { }); } - // CI only tests (must be reliable) - if (opts.ci) { - // TODO@Ben figure out tests that can run continously and reliably - } - - // Non-CI execution (all tests) - else { - if (!opts.web) { setupDataMigrationTests(opts['stable-build'], testDataPath); } - if (!opts.web) { setupDataLossTests(); } - if (!opts.web) { setupDataPreferencesTests(); } - setupDataSearchTests(); - setupDataCSSTests(); - setupDataEditorTests(); - setupDataStatusbarTests(!!opts.web); - if (!opts.web) { setupDataExtensionTests(); } - setupTerminalTests(); - if (!opts.web) { setupDataMultirootTests(); } - if (!opts.web) { setupDataLocalizationTests(); } - if (!opts.web) { setupLaunchTests(); } - } + if (!opts.web) { setupDataMigrationTests(opts['stable-build'], testDataPath); } + if (!opts.web) { setupDataLossTests(); } + if (!opts.web) { setupDataPreferencesTests(); } + setupDataSearchTests(); + setupDataCSSTests(); + setupDataEditorTests(); + setupDataStatusbarTests(!!opts.web); + if (!opts.web) { setupDataExtensionTests(); } + setupTerminalTests(); + if (!opts.web) { setupDataMultirootTests(); } + if (!opts.web) { setupDataLocalizationTests(); } + if (!opts.web) { setupLaunchTests(); } }); From da48773ebda5cb575f3b3e983c7bd9a0ef97aad6 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 26 Feb 2020 12:07:26 +0100 Subject: [PATCH 147/200] notifications - change close icon to chevron (fix #91446) --- .../browser/parts/notifications/notificationsActions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/notifications/notificationsActions.ts b/src/vs/workbench/browser/parts/notifications/notificationsActions.ts index f6e1ee43461..82301a0448e 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsActions.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsActions.ts @@ -63,7 +63,7 @@ export class HideNotificationsCenterAction extends Action { label: string, @ICommandService private readonly commandService: ICommandService ) { - super(id, label, 'codicon-close'); + super(id, label, 'codicon-chevron-down'); } run(notification: INotificationViewItem): Promise { From e2849363a412bdc4be2ee421512ce836e501f067 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 26 Feb 2020 12:14:26 +0100 Subject: [PATCH 148/200] Alerts are too verbose when problems panel is open. Do not use aria-live fixes #91166 --- src/vs/workbench/contrib/markers/browser/markersView.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/contrib/markers/browser/markersView.ts b/src/vs/workbench/contrib/markers/browser/markersView.ts index 9b4453be046..bb4a5763759 100644 --- a/src/vs/workbench/contrib/markers/browser/markersView.ts +++ b/src/vs/workbench/contrib/markers/browser/markersView.ts @@ -349,7 +349,6 @@ export class MarkersView extends ViewPane implements IMarkerFilterController { private createArialLabelElement(parent: HTMLElement): void { this.ariaLabelElement = dom.append(parent, dom.$('')); this.ariaLabelElement.setAttribute('id', 'markers-panel-arialabel'); - this.ariaLabelElement.setAttribute('aria-live', 'polite'); } private createTree(parent: HTMLElement): void { From 292d56a3efac42d825c2b5dfd726ae0e511cab5e Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 26 Feb 2020 12:22:15 +0100 Subject: [PATCH 149/200] Task run options not getting configured Discovered through the instance limit testing, this was actually a problem that would hit any runOption Fixes #91438 --- .../contrib/tasks/common/taskConfiguration.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/vs/workbench/contrib/tasks/common/taskConfiguration.ts b/src/vs/workbench/contrib/tasks/common/taskConfiguration.ts index 8b290a74f53..14666552147 100644 --- a/src/vs/workbench/contrib/tasks/common/taskConfiguration.ts +++ b/src/vs/workbench/contrib/tasks/common/taskConfiguration.ts @@ -681,6 +681,7 @@ export namespace RunOnOptions { } export namespace RunOptions { + const properties: MetaData[] = [{ property: 'reevaluateOnRerun' }, { property: 'runOn' }, { property: 'instanceLimit' }]; export function fromConfiguration(value: RunOptionsConfig | undefined): Tasks.RunOptions { return { reevaluateOnRerun: value ? value.reevaluateOnRerun : true, @@ -688,6 +689,14 @@ export namespace RunOptions { instanceLimit: value ? value.instanceLimit : 1 }; } + + export function assignProperties(target: Tasks.RunOptions, source: Tasks.RunOptions | undefined): Tasks.RunOptions { + return _assignProperties(target, source, properties)!; + } + + export function fillProperties(target: Tasks.RunOptions, source: Tasks.RunOptions | undefined): Tasks.RunOptions { + return _fillProperties(target, source, properties)!; + } } interface ParseContext { @@ -1609,6 +1618,7 @@ namespace CustomTask { result.command.presentation = CommandConfiguration.PresentationOptions.assignProperties( result.command.presentation!, configuredProps.configurationProperties.presentation)!; result.command.options = CommandOptions.assignProperties(result.command.options, configuredProps.configurationProperties.options); + result.runOptions = RunOptions.assignProperties(result.runOptions, configuredProps.runOptions); let contributedConfigProps: Tasks.ConfigurationProperties = contributedTask.configurationProperties; fillProperty(resultConfigProps, contributedConfigProps, 'group'); @@ -1621,6 +1631,7 @@ namespace CustomTask { result.command.presentation = CommandConfiguration.PresentationOptions.fillProperties( result.command.presentation!, contributedConfigProps.presentation)!; result.command.options = CommandOptions.fillProperties(result.command.options, contributedConfigProps.options); + result.runOptions = RunOptions.fillProperties(result.runOptions, contributedTask.runOptions); if (contributedTask.hasDefinedMatchers === true) { result.hasDefinedMatchers = true; From a980d877773120a7aec99238701bad4418d3f566 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 26 Feb 2020 12:26:06 +0100 Subject: [PATCH 150/200] Polish description of task input password Fixes #91399 --- src/vs/base/parts/quickinput/common/quickInput.ts | 2 +- src/vs/vscode.d.ts | 2 +- .../configurationResolver/common/configurationResolverSchema.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/base/parts/quickinput/common/quickInput.ts b/src/vs/base/parts/quickinput/common/quickInput.ts index 8cbf10553e7..531551f95b0 100644 --- a/src/vs/base/parts/quickinput/common/quickInput.ts +++ b/src/vs/base/parts/quickinput/common/quickInput.ts @@ -113,7 +113,7 @@ export interface IInputOptions { placeHolder?: string; /** - * set to true to show a password prompt that will not show the typed value + * Controls if a password input is shown. Password input hides the typed text. */ password?: boolean; diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index ead7e54c39f..52c6a90f94e 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -1806,7 +1806,7 @@ declare module 'vscode' { placeHolder?: string; /** - * Set to `true` to show a password prompt that will not show the typed value. + * Controls if a password input is shown. Password input hides the typed text. */ password?: boolean; diff --git a/src/vs/workbench/services/configurationResolver/common/configurationResolverSchema.ts b/src/vs/workbench/services/configurationResolver/common/configurationResolverSchema.ts index 6350d247915..2fceba9e4ac 100644 --- a/src/vs/workbench/services/configurationResolver/common/configurationResolverSchema.ts +++ b/src/vs/workbench/services/configurationResolver/common/configurationResolverSchema.ts @@ -46,7 +46,7 @@ export const inputsSchema: IJSONSchema = { }, password: { type: 'boolean', - description: nls.localize('JsonSchema.input.password', "Set to true to show a password prompt that will not show the typed value."), + description: nls.localize('JsonSchema.input.password', "Controls if a password input is shown. Password input hides the typed text."), }, } }, From 98d8caf7b531948d33de2b4e1e2db2157e94b23a Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 26 Feb 2020 13:54:53 +0100 Subject: [PATCH 151/200] Fixes #91361: Update selection when toggling column selection mode --- .../browser/toggleColumnSelection.ts | 57 +++++++++++++++++-- 1 file changed, 53 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/codeEditor/browser/toggleColumnSelection.ts b/src/vs/workbench/contrib/codeEditor/browser/toggleColumnSelection.ts index 6624e94c7bd..4fcee43f63a 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/toggleColumnSelection.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/toggleColumnSelection.ts @@ -10,6 +10,13 @@ import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configur import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; +import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; +import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { CoreNavigationCommands } from 'vs/editor/browser/controller/coreCommands'; +import { Position } from 'vs/editor/common/core/position'; +import { Selection } from 'vs/editor/common/core/selection'; +import { CursorColumns } from 'vs/editor/common/controller/cursorCommon'; export class ToggleColumnSelectionAction extends Action { public static readonly ID = 'editor.action.toggleColumnSelection'; @@ -18,14 +25,56 @@ export class ToggleColumnSelectionAction extends Action { constructor( id: string, label: string, - @IConfigurationService private readonly _configurationService: IConfigurationService + @IConfigurationService private readonly _configurationService: IConfigurationService, + @ICodeEditorService private readonly _codeEditorService: ICodeEditorService ) { super(id, label); } - public run(): Promise { - const newValue = !this._configurationService.getValue('editor.columnSelection'); - return this._configurationService.updateValue('editor.columnSelection', newValue, ConfigurationTarget.USER); + private _getCodeEditor(): ICodeEditor | null { + const codeEditor = this._codeEditorService.getFocusedCodeEditor(); + if (codeEditor) { + return codeEditor; + } + return this._codeEditorService.getActiveCodeEditor(); + } + + public async run(): Promise { + const oldValue = this._configurationService.getValue('editor.columnSelection'); + const codeEditor = this._getCodeEditor(); + await this._configurationService.updateValue('editor.columnSelection', !oldValue, ConfigurationTarget.USER); + const newValue = this._configurationService.getValue('editor.columnSelection'); + if (!codeEditor || codeEditor !== this._getCodeEditor() || oldValue === newValue || !codeEditor.hasModel()) { + return; + } + const cursors = codeEditor._getCursors(); + if (codeEditor.getOption(EditorOption.columnSelection)) { + const selection = codeEditor.getSelection(); + const modelSelectionStart = new Position(selection.selectionStartLineNumber, selection.selectionStartColumn); + const viewSelectionStart = cursors.context.convertModelPositionToViewPosition(modelSelectionStart); + const modelPosition = new Position(selection.positionLineNumber, selection.positionColumn); + const viewPosition = cursors.context.convertModelPositionToViewPosition(modelPosition); + + CoreNavigationCommands.MoveTo.runCoreEditorCommand(cursors, { + position: modelSelectionStart, + viewPosition: viewSelectionStart + }); + const visibleColumn = CursorColumns.visibleColumnFromColumn2(cursors.context.config, cursors.context.viewModel, viewPosition); + CoreNavigationCommands.ColumnSelect.runCoreEditorCommand(cursors, { + position: modelPosition, + viewPosition: viewPosition, + doColumnSelect: true, + mouseColumn: visibleColumn + 1 + }); + } else { + const columnSelectData = cursors.getColumnSelectData(); + const fromViewColumn = CursorColumns.columnFromVisibleColumn2(cursors.context.config, cursors.context.viewModel, columnSelectData.fromViewLineNumber, columnSelectData.fromViewVisualColumn); + const fromPosition = cursors.context.convertViewPositionToModelPosition(columnSelectData.fromViewLineNumber, fromViewColumn); + const toViewColumn = CursorColumns.columnFromVisibleColumn2(cursors.context.config, cursors.context.viewModel, columnSelectData.toViewLineNumber, columnSelectData.toViewVisualColumn); + const toPosition = cursors.context.convertViewPositionToModelPosition(columnSelectData.toViewLineNumber, toViewColumn); + + codeEditor.setSelection(new Selection(fromPosition.lineNumber, fromPosition.column, toPosition.lineNumber, toPosition.column)); + } } } From e864a4d4a502c95da022a4136909c5ca6989d8e2 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 26 Feb 2020 14:02:14 +0100 Subject: [PATCH 152/200] #91545 update labels --- src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index 3af86359029..c7c125df49a 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -608,7 +608,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo } const result = await this.dialogService.show( Severity.Info, - localize('firs time sync', "First time Sync"), + localize('firs time sync', "Sync"), [ localize('merge', "Merge"), localize('cancel', "Cancel"), @@ -616,7 +616,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo ], { cancelId: 1, - detail: localize('first time sync detail', "Synchronizing from this device for the first time.\nWould you like to merge or replace with the data from the cloud?"), + detail: localize('first time sync detail', "It looks like this is the first time sync is set up.\nWould you like to merge or replace with the data from the cloud?"), } ); switch (result.choice) { From f85095715c403df09c8229fa3f022bb6e8cad609 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 26 Feb 2020 14:04:10 +0100 Subject: [PATCH 153/200] #91544 Change label --- src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index c7c125df49a..91cb9aa468c 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -853,7 +853,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo constructor() { super({ id: 'workbench.userData.actions.syncStatus', - title: localize('sync is on', "Sync is on"), + title: localize('sync is on', "Sync..."), menu: [ { id: MenuId.GlobalActivity, From 70b9b4b7f0988b0b99a5374726c4bc6171669f19 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 26 Feb 2020 14:21:44 +0100 Subject: [PATCH 154/200] Fixes #91369: Pass the IBulkEditOptions.label through to the IUndoRedo service --- .../services/bulkEdit/browser/bulkEditService.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts b/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts index b7f29e36862..e5b490f3187 100644 --- a/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts +++ b/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts @@ -130,6 +130,7 @@ class BulkEditModel implements IDisposable { private _tasks: ModelEditTask[] | undefined; constructor( + private readonly _label: string | undefined, private readonly _editor: ICodeEditor | undefined, private readonly _progress: IProgress, edits: WorkspaceTextEdit[], @@ -232,7 +233,7 @@ class BulkEditModel implements IDisposable { } const multiModelEditStackElement = new MultiModelEditStackElement( - localize('workspaceEdit', "Workspace Edit"), + this._label || localize('workspaceEdit', "Workspace Edit"), tasks.map(t => new EditStackElement(t.model, t.getBeforeCursorState())) ); this._undoRedoService.pushElement(multiModelEditStackElement); @@ -250,11 +251,13 @@ type Edit = WorkspaceFileEdit | WorkspaceTextEdit; class BulkEdit { + private readonly _label: string | undefined; private readonly _edits: Edit[] = []; private readonly _editor: ICodeEditor | undefined; private readonly _progress: IProgress; constructor( + label: string | undefined, editor: ICodeEditor | undefined, progress: IProgress | undefined, edits: Edit[], @@ -265,6 +268,7 @@ class BulkEdit { @IWorkingCopyFileService private readonly _workingCopyFileService: IWorkingCopyFileService, @IConfigurationService private readonly _configurationService: IConfigurationService ) { + this._label = label; this._editor = editor; this._progress = progress || Progress.None; this._edits = edits; @@ -361,7 +365,7 @@ class BulkEdit { private async _performTextEdits(edits: WorkspaceTextEdit[], progress: IProgress): Promise { this._logService.debug('_performTextEdits', JSON.stringify(edits)); - const model = this._instaService.createInstance(BulkEditModel, this._editor, progress, edits); + const model = this._instaService.createInstance(BulkEditModel, this._label, this._editor, progress, edits); await model.prepare(); @@ -439,7 +443,7 @@ export class BulkEditService implements IBulkEditService { // If the code editor is readonly still allow bulk edits to be applied #68549 codeEditor = undefined; } - const bulkEdit = this._instaService.createInstance(BulkEdit, codeEditor, options?.progress, edits); + const bulkEdit = this._instaService.createInstance(BulkEdit, options?.label, codeEditor, options?.progress, edits); return bulkEdit.perform().then(() => { return { ariaSummary: bulkEdit.ariaMessage() }; }).catch(err => { From 70d7091d75d3156578362292962bb81fc1293767 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 26 Feb 2020 14:33:42 +0100 Subject: [PATCH 155/200] fixes #91426 --- .../contrib/debug/browser/startView.ts | 52 +++++++++---------- .../contrib/files/browser/explorerViewlet.ts | 12 ++--- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/startView.ts b/src/vs/workbench/contrib/debug/browser/startView.ts index f6b3133dc28..38ef45e83b5 100644 --- a/src/vs/workbench/contrib/debug/browser/startView.ts +++ b/src/vs/workbench/contrib/debug/browser/startView.ts @@ -75,35 +75,35 @@ export class StartView extends ViewPane { }; this._register(editorService.onDidActiveEditorChange(setContextKey)); this._register(this.debugService.getConfigurationManager().onDidRegisterDebugger(setContextKey)); - this.registerViews(); + setContextKey(); + + const debugKeybinding = this.keybindingService.lookupKeybinding(StartAction.ID); + debugKeybindingLabel = debugKeybinding ? ` (${debugKeybinding.getLabel()})` : ''; } shouldShowWelcome(): boolean { return true; } - - private registerViews(): void { - const viewsRegistry = Registry.as(Extensions.ViewsRegistry); - viewsRegistry.registerViewWelcomeContent(StartView.ID, { - content: localize('openAFileWhichCanBeDebugged', "[Open a file](command:{0}) which can be debugged or run.", isMacintosh ? OpenFileFolderAction.ID : OpenFileAction.ID), - when: CONTEXT_DEBUGGER_INTERESTED_IN_ACTIVE_EDITOR.toNegated() - }); - - const debugKeybinding = this.keybindingService.lookupKeybinding(StartAction.ID); - const debugKeybindingLabel = debugKeybinding ? ` (${debugKeybinding.getLabel()})` : ''; - viewsRegistry.registerViewWelcomeContent(StartView.ID, { - content: localize('runAndDebugAction', "[Run and Debug{0}](command:{1})", debugKeybindingLabel, StartAction.ID), - preconditions: [CONTEXT_DEBUGGER_INTERESTED_IN_ACTIVE_EDITOR] - }); - - viewsRegistry.registerViewWelcomeContent(StartView.ID, { - content: localize('customizeRunAndDebug', "To customize Run and Debug [create a launch.json file](command:{0}).", ConfigureAction.ID), - when: WorkbenchStateContext.notEqualsTo('empty') - }); - - viewsRegistry.registerViewWelcomeContent(StartView.ID, { - content: localize('customizeRunAndDebugOpenFolder', "To customize Run and Debug, [open a folder](command:{0}) and create a launch.json file.", isMacintosh ? OpenFileFolderAction.ID : OpenFolderAction.ID), - when: WorkbenchStateContext.isEqualTo('empty') - }); - } } + +const viewsRegistry = Registry.as(Extensions.ViewsRegistry); +viewsRegistry.registerViewWelcomeContent(StartView.ID, { + content: localize('openAFileWhichCanBeDebugged', "[Open a file](command:{0}) which can be debugged or run.", isMacintosh ? OpenFileFolderAction.ID : OpenFileAction.ID), + when: CONTEXT_DEBUGGER_INTERESTED_IN_ACTIVE_EDITOR.toNegated() +}); + +let debugKeybindingLabel = ''; +viewsRegistry.registerViewWelcomeContent(StartView.ID, { + content: localize('runAndDebugAction', "[Run and Debug{0}](command:{1})", debugKeybindingLabel, StartAction.ID), + preconditions: [CONTEXT_DEBUGGER_INTERESTED_IN_ACTIVE_EDITOR] +}); + +viewsRegistry.registerViewWelcomeContent(StartView.ID, { + content: localize('customizeRunAndDebug', "To customize Run and Debug [create a launch.json file](command:{0}).", ConfigureAction.ID), + when: WorkbenchStateContext.notEqualsTo('empty') +}); + +viewsRegistry.registerViewWelcomeContent(StartView.ID, { + content: localize('customizeRunAndDebugOpenFolder', "To customize Run and Debug, [open a folder](command:{0}) and create a launch.json file.", isMacintosh ? OpenFileFolderAction.ID : OpenFolderAction.ID), + when: WorkbenchStateContext.isEqualTo('empty') +}); diff --git a/src/vs/workbench/contrib/files/browser/explorerViewlet.ts b/src/vs/workbench/contrib/files/browser/explorerViewlet.ts index 720b01e66ed..ba4f2cae30f 100644 --- a/src/vs/workbench/contrib/files/browser/explorerViewlet.ts +++ b/src/vs/workbench/contrib/files/browser/explorerViewlet.ts @@ -65,21 +65,21 @@ export class ExplorerViewletViewsContribution extends Disposable implements IWor private registerViews(): void { const viewsRegistry = Registry.as(Extensions.ViewsRegistry); - viewsRegistry.registerViewWelcomeContent(EmptyView.ID, { + this._register(viewsRegistry.registerViewWelcomeContent(EmptyView.ID, { content: localize('noWorkspaceHelp', "You have not yet added a folder to the workspace.\n[Add Folder](command:{0})", AddRootFolderAction.ID), when: WorkbenchStateContext.isEqualTo('workspace') - }); + })); const commandId = isMacintosh ? OpenFileFolderAction.ID : OpenFolderAction.ID; - viewsRegistry.registerViewWelcomeContent(EmptyView.ID, { + this._register(viewsRegistry.registerViewWelcomeContent(EmptyView.ID, { content: localize('remoteNoFolderHelp', "Connected to remote.\n[Open Folder](command:{0})", commandId), when: ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('workspace'), RemoteNameContext.notEqualsTo(''), IsWebContext.toNegated()) - }); + })); - viewsRegistry.registerViewWelcomeContent(EmptyView.ID, { + this._register(viewsRegistry.registerViewWelcomeContent(EmptyView.ID, { content: localize('noFolderHelp', "You have not yet opened a folder.\n[Open Folder](command:{0})", commandId), when: ContextKeyExpr.or(ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('workspace'), RemoteNameContext.isEqualTo('')), ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('workspace'), IsWebContext)) - }); + })); const viewDescriptors = viewsRegistry.getViews(VIEW_CONTAINER); From 46a973511b506e757e173d5a82112dbd9e1e2512 Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Wed, 26 Feb 2020 14:52:20 +0100 Subject: [PATCH 156/200] node-debug@1.43.2 --- build/builtInExtensions.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/builtInExtensions.json b/build/builtInExtensions.json index 84091196b00..b0483145e01 100644 --- a/build/builtInExtensions.json +++ b/build/builtInExtensions.json @@ -1,7 +1,7 @@ [ { "name": "ms-vscode.node-debug", - "version": "1.43.1", + "version": "1.43.2", "repo": "https://github.com/Microsoft/vscode-node-debug", "metadata": { "id": "b6ded8fb-a0a0-4c1c-acbd-ab2a3bc995a6", From ebafcb93181a8d610153734d2ab1c90dc5117d3c Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Wed, 26 Feb 2020 14:54:49 +0100 Subject: [PATCH 157/200] update DAP to final version --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 7910be5d9ae..46535c5aa3c 100644 --- a/package.json +++ b/package.json @@ -156,7 +156,7 @@ "vinyl": "^2.0.0", "vinyl-fs": "^3.0.0", "vsce": "1.48.0", - "vscode-debugprotocol": "1.39.0-pre.0", + "vscode-debugprotocol": "1.39.0", "vscode-nls-dev": "^3.3.1", "webpack": "^4.16.5", "webpack-cli": "^3.3.8", diff --git a/yarn.lock b/yarn.lock index 9050b677d92..a820c6344ac 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9638,10 +9638,10 @@ vsce@1.48.0: yauzl "^2.3.1" yazl "^2.2.2" -vscode-debugprotocol@1.39.0-pre.0: - version "1.39.0-pre.0" - resolved "https://registry.yarnpkg.com/vscode-debugprotocol/-/vscode-debugprotocol-1.39.0-pre.0.tgz#67843631a3c53f2d5282f75ab5996e1408c3958c" - integrity sha512-VpoD8m0gOo2Ag5dEpNT9sAI6BBxIyCxEk2dhGIBegxnlOuiB1SVxMgo1tmsvNYcRCpf9eng27kZ6d6FGoPpIAg== +vscode-debugprotocol@1.39.0: + version "1.39.0" + resolved "https://registry.yarnpkg.com/vscode-debugprotocol/-/vscode-debugprotocol-1.39.0.tgz#0c639178d0d5ea7de7903b6478b53d2bc0d77461" + integrity sha512-Wkvgtuz90vjtQBcvw9Z+BYa4dA6W+sHwHMpqvJVNmwWSuT3JZdl0XDhZNLqtMXkVF4okxtAe0MmbupPSt+gnAQ== vscode-minimist@^1.2.2: version "1.2.2" From dc43bc617b16b015d05d1a9bf6e02a2b8d4eabd6 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 26 Feb 2020 15:02:55 +0100 Subject: [PATCH 158/200] Fixes #91371: Do something (the local file) even when a cross file undo/redo cannt be applied --- src/vs/platform/undoRedo/common/undoRedoService.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/platform/undoRedo/common/undoRedoService.ts b/src/vs/platform/undoRedo/common/undoRedoService.ts index 1f5191e2a74..516dd459aee 100644 --- a/src/vs/platform/undoRedo/common/undoRedoService.ts +++ b/src/vs/platform/undoRedo/common/undoRedoService.ts @@ -260,7 +260,7 @@ export class UndoRedoService implements IUndoRedoService { this._splitPastWorkspaceElement(element, element.removedResources.set); const message = nls.localize('cannotWorkspaceUndo', "Could not undo '{0}' across all files. {1}", element.label, element.removedResources.createMessage()); this._notificationService.info(message); - return; + return this.undo(resource); } // this must be the last past element in all the impacted resources! @@ -281,7 +281,7 @@ export class UndoRedoService implements IUndoRedoService { const paths = cannotUndoDueToResources.map(r => r.scheme === Schemas.file ? r.fsPath : r.path); const message = nls.localize('cannotWorkspaceUndoDueToChanges', "Could not undo '{0}' across all files because changes were made to {1}", element.label, paths.join(', ')); this._notificationService.info(message); - return; + return this.undo(resource); } return this._dialogService.show( @@ -344,7 +344,7 @@ export class UndoRedoService implements IUndoRedoService { this._splitFutureWorkspaceElement(element, element.removedResources.set); const message = nls.localize('cannotWorkspaceRedo', "Could not redo '{0}' across all files. {1}", element.label, element.removedResources.createMessage()); this._notificationService.info(message); - return; + return this.redo(resource); } // this must be the last future element in all the impacted resources! @@ -365,7 +365,7 @@ export class UndoRedoService implements IUndoRedoService { const paths = cannotRedoDueToResources.map(r => r.scheme === Schemas.file ? r.fsPath : r.path); const message = nls.localize('cannotWorkspaceRedoDueToChanges', "Could not redo '{0}' across all files because changes were made to {1}", element.label, paths.join(', ')); this._notificationService.info(message); - return; + return this.redo(resource); } for (const editStack of affectedEditStacks) { From 0c2947fda5a354eb85f7a4c3bc4fe77d53aedd1c Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 26 Feb 2020 15:35:15 +0100 Subject: [PATCH 159/200] Fixes #91557: Move settings-related menu entries to the end of the Selection menu --- .../contrib/codeEditor/browser/toggleColumnSelection.ts | 4 ++-- .../contrib/codeEditor/browser/toggleMultiCursorModifier.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/codeEditor/browser/toggleColumnSelection.ts b/src/vs/workbench/contrib/codeEditor/browser/toggleColumnSelection.ts index 4fcee43f63a..fac092022e2 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/toggleColumnSelection.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/toggleColumnSelection.ts @@ -82,11 +82,11 @@ const registry = Registry.as(ActionExtensions.Workbenc registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleColumnSelectionAction, ToggleColumnSelectionAction.ID, ToggleColumnSelectionAction.LABEL), 'View: Toggle Column Selection Mode', nls.localize('view', "View")); MenuRegistry.appendMenuItem(MenuId.MenubarSelectionMenu, { - group: '3_multi', + group: '4_config', command: { id: ToggleColumnSelectionAction.ID, title: nls.localize({ key: 'miColumnSelection', comment: ['&& denotes a mnemonic'] }, "Column &&Selection Mode"), toggled: ContextKeyExpr.equals('config.editor.columnSelection', true) }, - order: 1.5 + order: 2 }); diff --git a/src/vs/workbench/contrib/codeEditor/browser/toggleMultiCursorModifier.ts b/src/vs/workbench/contrib/codeEditor/browser/toggleMultiCursorModifier.ts index 05df06aa388..bc210c5a355 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/toggleMultiCursorModifier.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/toggleMultiCursorModifier.ts @@ -70,7 +70,7 @@ Registry.as(WorkbenchExtensions.Workbench).regi const registry = Registry.as(Extensions.WorkbenchActions); registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleMultiCursorModifierAction, ToggleMultiCursorModifierAction.ID, ToggleMultiCursorModifierAction.LABEL), 'Toggle Multi-Cursor Modifier'); MenuRegistry.appendMenuItem(MenuId.MenubarSelectionMenu, { - group: '3_multi', + group: '4_config', command: { id: ToggleMultiCursorModifierAction.ID, title: nls.localize('miMultiCursorAlt', "Switch to Alt+Click for Multi-Cursor") @@ -79,7 +79,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarSelectionMenu, { order: 1 }); MenuRegistry.appendMenuItem(MenuId.MenubarSelectionMenu, { - group: '3_multi', + group: '4_config', command: { id: ToggleMultiCursorModifierAction.ID, title: ( From 622ddc0d6794450de81bdd4fd020dd219be1b926 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 26 Feb 2020 06:40:46 -0800 Subject: [PATCH 160/200] Fix backwards layout in terminal Fixes #91580 Fixes #91135 Fixes #91119 --- src/vs/workbench/contrib/terminal/browser/terminalView.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalView.ts b/src/vs/workbench/contrib/terminal/browser/terminalView.ts index c0d8a8bc8f9..12bc4fab7ff 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalView.ts @@ -114,7 +114,7 @@ export class TerminalViewPane extends ViewPane { })); // Force another layout (first is setContainers) since config has changed - this.layoutBody(this._terminalContainer.offsetWidth, this._terminalContainer.offsetHeight); + this.layoutBody(this._terminalContainer.offsetHeight, this._terminalContainer.offsetWidth); } protected layoutBody(height: number, width: number): void { @@ -321,7 +321,7 @@ export class TerminalViewPane extends ViewPane { } // TODO: Can we support ligatures? // dom.toggleClass(this._parentDomElement, 'enable-ligatures', this._terminalService.configHelper.config.fontLigatures); - this.layoutBody(this._parentDomElement.offsetWidth, this._parentDomElement.offsetHeight); + this.layoutBody(this._parentDomElement.offsetHeight, this._parentDomElement.offsetWidth); } } From fed69adea3fccae29fb04f6a0144da4acad9f8f0 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 26 Feb 2020 15:48:37 +0100 Subject: [PATCH 161/200] debug session: use queue to make sure output messages get processed in correct order fixes #91416 --- .../contrib/debug/browser/debugSession.ts | 100 +++++++++--------- 1 file changed, 48 insertions(+), 52 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugSession.ts b/src/vs/workbench/contrib/debug/browser/debugSession.ts index 3c9ba78f0d6..214d3763448 100644 --- a/src/vs/workbench/contrib/debug/browser/debugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/debugSession.ts @@ -19,7 +19,7 @@ import { RawDebugSession } from 'vs/workbench/contrib/debug/browser/rawDebugSess import { IProductService } from 'vs/platform/product/common/productService'; import { IWorkspaceFolder, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { RunOnceScheduler } from 'vs/base/common/async'; +import { RunOnceScheduler, Queue } from 'vs/base/common/async'; import { generateUuid } from 'vs/base/common/uuid'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug'; @@ -807,62 +807,58 @@ export class DebugSession implements IDebugSession { this._onDidChangeState.fire(); })); - let outpuPromises: Promise[] = []; + const outputQueue = new Queue(); this.rawListeners.push(this.raw.onDidOutput(async event => { - if (!event.body || !this.raw) { - return; - } - - const outputSeverity = event.body.category === 'stderr' ? severity.Error : event.body.category === 'console' ? severity.Warning : severity.Info; - if (event.body.category === 'telemetry') { - // only log telemetry events from debug adapter if the debug extension provided the telemetry key - // and the user opted in telemetry - if (this.raw.customTelemetryService && this.telemetryService.isOptedIn) { - // __GDPR__TODO__ We're sending events in the name of the debug extension and we can not ensure that those are declared correctly. - this.raw.customTelemetryService.publicLog(event.body.output, event.body.data); - } - - return; - } - - // Make sure to append output in the correct order by properly waiting on preivous promises #33822 - const waitFor = outpuPromises.slice(); - const source = event.body.source && event.body.line ? { - lineNumber: event.body.line, - column: event.body.column ? event.body.column : 1, - source: this.getSource(event.body.source) - } : undefined; - - if (event.body.group === 'start' || event.body.group === 'startCollapsed') { - const expanded = event.body.group === 'start'; - this.repl.startGroup(event.body.output || '', expanded, source); - return; - } - if (event.body.group === 'end') { - this.repl.endGroup(); - if (!event.body.output) { - // Only return if the end event does not have additional output in it + outputQueue.queue(async () => { + if (!event.body || !this.raw) { return; } - } - if (event.body.variablesReference) { - const container = new ExpressionContainer(this, undefined, event.body.variablesReference, generateUuid()); - outpuPromises.push(container.getChildren().then(async children => { - await Promise.all(waitFor); - children.forEach(child => { - // Since we can not display multiple trees in a row, we are displaying these variables one after the other (ignoring their names) - (child).name = null; - this.appendToRepl(child, outputSeverity, source); + const outputSeverity = event.body.category === 'stderr' ? severity.Error : event.body.category === 'console' ? severity.Warning : severity.Info; + if (event.body.category === 'telemetry') { + // only log telemetry events from debug adapter if the debug extension provided the telemetry key + // and the user opted in telemetry + if (this.raw.customTelemetryService && this.telemetryService.isOptedIn) { + // __GDPR__TODO__ We're sending events in the name of the debug extension and we can not ensure that those are declared correctly. + this.raw.customTelemetryService.publicLog(event.body.output, event.body.data); + } + + return; + } + + // Make sure to append output in the correct order by properly waiting on preivous promises #33822 + const source = event.body.source && event.body.line ? { + lineNumber: event.body.line, + column: event.body.column ? event.body.column : 1, + source: this.getSource(event.body.source) + } : undefined; + + if (event.body.group === 'start' || event.body.group === 'startCollapsed') { + const expanded = event.body.group === 'start'; + this.repl.startGroup(event.body.output || '', expanded, source); + return; + } + if (event.body.group === 'end') { + this.repl.endGroup(); + if (!event.body.output) { + // Only return if the end event does not have additional output in it + return; + } + } + + if (event.body.variablesReference) { + const container = new ExpressionContainer(this, undefined, event.body.variablesReference, generateUuid()); + await container.getChildren().then(children => { + children.forEach(child => { + // Since we can not display multiple trees in a row, we are displaying these variables one after the other (ignoring their names) + (child).name = null; + this.appendToRepl(child, outputSeverity, source); + }); }); - })); - } else if (typeof event.body.output === 'string') { - await Promise.all(waitFor); - this.appendToRepl(event.body.output, outputSeverity, source); - } - - await Promise.all(outpuPromises); - outpuPromises = []; + } else if (typeof event.body.output === 'string') { + this.appendToRepl(event.body.output, outputSeverity, source); + } + }); })); this.rawListeners.push(this.raw.onDidBreakpoint(event => { From 1c270b9a692212d7ae7e3f8425226686ecf5f4b3 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 26 Feb 2020 16:08:12 +0100 Subject: [PATCH 162/200] fixes #91322 --- .../contrib/debug/browser/debugConfigurationManager.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts index 29dcf72e752..1a4ec6cf4d7 100644 --- a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts +++ b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts @@ -519,18 +519,17 @@ abstract class AbstractLaunch { if (!config || (!Array.isArray(config.configurations) && !Array.isArray(config.compounds))) { return []; } else { - const names: string[] = []; + const configurations: (IConfig | ICompound)[] = []; if (config.configurations) { - names.push(...config.configurations.filter(cfg => cfg && typeof cfg.name === 'string').map(cfg => cfg.name)); + configurations.push(...config.configurations.filter(cfg => cfg && typeof cfg.name === 'string')); } if (includeCompounds && config.compounds) { if (config.compounds) { - names.push(...config.compounds.filter(compound => typeof compound.name === 'string' && compound.configurations && compound.configurations.length) - .map(compound => compound.name)); + configurations.push(...config.compounds.filter(compound => typeof compound.name === 'string' && compound.configurations && compound.configurations.length)); } } - return names; + return getVisibleAndSorted(configurations).map(c => c.name); } } From 872e4fb7014a56700b0671d6c79d4981e8fa995c Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 26 Feb 2020 14:06:57 +0100 Subject: [PATCH 163/200] Fix #91502 --- src/vs/platform/userDataSync/common/extensionsSync.ts | 2 +- src/vs/platform/userDataSync/common/globalStateSync.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/userDataSync/common/extensionsSync.ts b/src/vs/platform/userDataSync/common/extensionsSync.ts index b932d1ecd10..ccdf3c48517 100644 --- a/src/vs/platform/userDataSync/common/extensionsSync.ts +++ b/src/vs/platform/userDataSync/common/extensionsSync.ts @@ -194,7 +194,7 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse if (added.length || removed.length || updated.length) { // back up all disabled or market place extensions const backUpExtensions = localExtensions.filter(e => e.disabled || !!e.identifier.uuid); - await this.backupLocal(VSBuffer.fromString(JSON.stringify(backUpExtensions))); + await this.backupLocal(VSBuffer.fromString(JSON.stringify(backUpExtensions, null, '\t'))); skippedExtensions = await this.updateLocalExtensions(added, removed, updated, skippedExtensions); } diff --git a/src/vs/platform/userDataSync/common/globalStateSync.ts b/src/vs/platform/userDataSync/common/globalStateSync.ts index 9e6b1d16a43..8e00f07a9ae 100644 --- a/src/vs/platform/userDataSync/common/globalStateSync.ts +++ b/src/vs/platform/userDataSync/common/globalStateSync.ts @@ -165,7 +165,7 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs if (local) { // update local this.logService.trace('UI State: Updating local ui state...'); - await this.backupLocal(VSBuffer.fromString(JSON.stringify(localUserData))); + await this.backupLocal(VSBuffer.fromString(JSON.stringify(localUserData, null, '\t'))); await this.writeLocalGlobalState(local); this.logService.info('UI State: Updated local ui state'); } From d6f49e3b6a8dd3868ebc383b70f2093c94e6e89c Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 26 Feb 2020 14:32:00 +0100 Subject: [PATCH 164/200] Fix #91487 --- src/vs/platform/userDataSync/common/abstractSynchronizer.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/userDataSync/common/abstractSynchronizer.ts b/src/vs/platform/userDataSync/common/abstractSynchronizer.ts index a82acd2b357..7fdd78f0a10 100644 --- a/src/vs/platform/userDataSync/common/abstractSynchronizer.ts +++ b/src/vs/platform/userDataSync/common/abstractSynchronizer.ts @@ -202,7 +202,7 @@ export abstract class AbstractSynchroniser extends Disposable { } protected async backupLocal(content: VSBuffer): Promise { - const resource = joinPath(this.syncFolder, toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')); + const resource = joinPath(this.syncFolder, `${toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')}.json`); try { await this.fileService.writeFile(resource, content); } catch (e) { @@ -215,7 +215,8 @@ export abstract class AbstractSynchroniser extends Disposable { try { const stat = await this.fileService.resolve(this.syncFolder); if (stat.children) { - const all = stat.children.filter(stat => stat.isFile && /^\d{8}T\d{6}$/.test(stat.name)).sort(); + const all = stat.children.filter(stat => stat.isFile && /^\d{8}T\d{6}(\.json)?$/.test(stat.name)).sort(); + console.log(all.map(a => a.name)); const backUpMaxAge = 1000 * 60 * 60 * 24 * (this.configurationService.getValue('sync.localBackupDuration') || 30 /* Default 30 days */); let toDelete = all.filter(stat => { const ctime = stat.ctime || new Date( From 608ccd09ba374949f72414e19df0ba59e9871777 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 26 Feb 2020 16:12:41 +0100 Subject: [PATCH 165/200] Fix #91409 --- src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index 91cb9aa468c..3a24fd01e20 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -99,7 +99,7 @@ const resolveKeybindingsConflictsCommand = { id: 'workbench.userData.actions.res const configureSyncCommand = { id: 'workbench.userData.actions.configureSync', title: localize('configure sync', "Sync: Configure") }; const showSyncActivityCommand = { id: 'workbench.userData.actions.showSyncActivity', title(userDataSyncService: IUserDataSyncService): string { - return getActivityTitle(localize('show sync log', "Sync: Show Activity"), userDataSyncService); + return getActivityTitle(localize('show sync log', "Sync: Show Log"), userDataSyncService); } }; const showSyncSettingsCommand = { id: 'workbench.userData.actions.syncSettings', title: localize('sync settings', "Sync: Settings"), }; From 265487690ab62171e8b28acc4ff445eff0a0a85f Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 26 Feb 2020 16:15:08 +0100 Subject: [PATCH 166/200] use CodeAction#title as bulk edit label option, #91369 --- src/vs/editor/contrib/codeAction/codeActionCommands.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/codeAction/codeActionCommands.ts b/src/vs/editor/contrib/codeAction/codeActionCommands.ts index 185ebd86867..13b92c1289d 100644 --- a/src/vs/editor/contrib/codeAction/codeActionCommands.ts +++ b/src/vs/editor/contrib/codeAction/codeActionCommands.ts @@ -164,7 +164,7 @@ export async function applyCodeAction( }); if (action.edit) { - await bulkEditService.apply(action.edit, { editor }); + await bulkEditService.apply(action.edit, { editor, label: action.title }); } if (action.command) { From e59cb58ccce6393ae01f1ee6302efb2bde5ff988 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 26 Feb 2020 16:18:43 +0100 Subject: [PATCH 167/200] fix #91536 --- src/vs/editor/contrib/links/getLinks.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/links/getLinks.ts b/src/vs/editor/contrib/links/getLinks.ts index a85e7f8c942..0ee40d5166e 100644 --- a/src/vs/editor/contrib/links/getLinks.ts +++ b/src/vs/editor/contrib/links/getLinks.ts @@ -77,8 +77,8 @@ export class LinksList extends Disposable { const newLinks = list.links.map(link => new Link(link, provider)); links = LinksList._union(links, newLinks); // register disposables - if (isDisposable(provider)) { - this._register(provider); + if (isDisposable(list)) { + this._register(list); } } this.links = links; From 65976ac711f25fd3c365c2d083a638825c37c9a3 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 26 Feb 2020 16:29:57 +0100 Subject: [PATCH 168/200] Revert "docs for #91532" This reverts commit ebcd432491259c91013bb0ff0284610f7f84504d. --- test/integration/browser/README.md | 1 - test/smoke/README.md | 1 - 2 files changed, 2 deletions(-) diff --git a/test/integration/browser/README.md b/test/integration/browser/README.md index 8b36a3c172c..10a55f7de17 100644 --- a/test/integration/browser/README.md +++ b/test/integration/browser/README.md @@ -14,7 +14,6 @@ All integration tests run in an Electron instance. You can specify to run the te ## Run (inside browser) - yarn gulp mixin-server resources/server/test/test-web-integration.[sh|bat] --browser [chromium|webkit] [--debug] All integration tests run in a browser instance as specified by the command line arguments. diff --git a/test/smoke/README.md b/test/smoke/README.md index 8836886be6b..8b41d05ed08 100644 --- a/test/smoke/README.md +++ b/test/smoke/README.md @@ -13,7 +13,6 @@ yarn --cwd test/automation yarn smoketest # Dev (Web) -yarn gulp mixin-server yarn smoketest --web --browser [chromium|firefox|webkit] # Build (Electron) From b59e3a6f42541d1df2c855c1a23d5a7148ae047f Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Wed, 26 Feb 2020 16:57:46 +0100 Subject: [PATCH 169/200] range returned from extension API is off by one; fix #91406 --- src/vs/workbench/contrib/debug/browser/debugHover.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugHover.ts b/src/vs/workbench/contrib/debug/browser/debugHover.ts index 5de2c78eea2..0a9b8964e1a 100644 --- a/src/vs/workbench/contrib/debug/browser/debugHover.ts +++ b/src/vs/workbench/contrib/debug/browser/debugHover.ts @@ -209,7 +209,7 @@ export class DebugHoverWidget implements IContentWidget { if (!matchingExpression) { const lineContent = model.getLineContent(pos.lineNumber); - matchingExpression = lineContent.substring(rng.startColumn - 1, rng.endColumn); + matchingExpression = lineContent.substring(rng.startColumn - 1, rng.endColumn - 1); } } From 6a883e03cca4f5688b4dc88383793608e1ada857 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 26 Feb 2020 17:11:29 +0100 Subject: [PATCH 170/200] Fixes #91388: Add a cancel option --- src/vs/platform/undoRedo/common/undoRedoService.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/vs/platform/undoRedo/common/undoRedoService.ts b/src/vs/platform/undoRedo/common/undoRedoService.ts index 516dd459aee..8bcaf9d7d16 100644 --- a/src/vs/platform/undoRedo/common/undoRedoService.ts +++ b/src/vs/platform/undoRedo/common/undoRedoService.ts @@ -289,10 +289,17 @@ export class UndoRedoService implements IUndoRedoService { nls.localize('confirmWorkspace', "Would you like to undo '{0}' across all files?", element.label), [ nls.localize('ok', "Yes, change {0} files.", affectedEditStacks.length), - nls.localize('nok', "No, change only this file.") - ] + nls.localize('nok', "No, change only this file."), + nls.localize('cancel', "Cancel"), + ], + { + cancelId: 2 + } ).then((result) => { - if (result.choice === 0) { + if (result.choice === 2) { + // cancel + return; + } else if (result.choice === 0) { for (const editStack of affectedEditStacks) { editStack.past.pop(); editStack.future.push(element); From f18717988122478a5c40ad8b6fc35834fe7643d9 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 26 Feb 2020 16:58:20 +0100 Subject: [PATCH 171/200] Fix #91575 --- .../userDataSync/browser/userDataSync.ts | 44 ++++++++++++++++--- 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index 3a24fd01e20..15e1daa647c 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -88,12 +88,12 @@ const getActivityTitle = (label: string, userDataSyncService: IUserDataSyncServi } return label; }; -const getIdentityTitle = (label: string, account?: AuthenticationSession): string => { - return account ? `${label} (${account.accountName})` : label; +const getIdentityTitle = (label: string, authenticationProviderId: string, account: AuthenticationSession | undefined, authenticationService: IAuthenticationService): string => { + return account ? `${label} (${authenticationService.getDisplayName(authenticationProviderId)}:${account.accountName})` : label; }; const turnOnSyncCommand = { id: 'workbench.userData.actions.syncStart', title: localize('turn on sync with category', "Sync: Turn on Sync") }; const signInCommand = { id: 'workbench.userData.actions.signin', title: localize('sign in', "Sync: Sign in to sync") }; -const stopSyncCommand = { id: 'workbench.userData.actions.stopSync', title(account?: AuthenticationSession) { return getIdentityTitle(localize('stop sync', "Sync: Turn off Sync"), account); } }; +const stopSyncCommand = { id: 'workbench.userData.actions.stopSync', title(authenticationProviderId: string, account: AuthenticationSession | undefined, authenticationService: IAuthenticationService) { return getIdentityTitle(localize('stop sync', "Sync: Turn off Sync"), authenticationProviderId, account, authenticationService); } }; const resolveSettingsConflictsCommand = { id: 'workbench.userData.actions.resolveSettingsConflicts', title: localize('showConflicts', "Sync: Show Settings Conflicts") }; const resolveKeybindingsConflictsCommand = { id: 'workbench.userData.actions.resolveKeybindingsConflicts', title: localize('showKeybindingsConflicts', "Sync: Show Keybindings Conflicts") }; const configureSyncCommand = { id: 'workbench.userData.actions.configureSync', title: localize('configure sync', "Sync: Configure") }; @@ -509,7 +509,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo const disposables: DisposableStore = new DisposableStore(); const quickPick = this.quickInputService.createQuickPick(); disposables.add(quickPick); - quickPick.title = localize('turn on sync', "Turn on Sync"); + quickPick.title = localize('turn on title', "Sync: Turn On"); quickPick.ok = false; quickPick.customButton = true; if (this.authenticationState.get() === AuthStatus.SignedIn) { @@ -538,6 +538,38 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo } private async doTurnOn(): Promise { + if (this.authenticationState.get() === AuthStatus.SignedIn) { + await new Promise((c, e) => { + const disposables: DisposableStore = new DisposableStore(); + const displayName = this.authenticationService.getDisplayName(this.userDataSyncStore!.authenticationProviderId); + const quickPick = this.quickInputService.createQuickPick<{ id: string, label: string, description?: string }>(); + const chooseAnotherItemId = 'chooseAnother'; + disposables.add(quickPick); + quickPick.title = localize('pick account', "Sync: Choose Account"); + quickPick.ok = false; + quickPick.placeholder = localize('choose account placeholder', "Choose account to sync"); + quickPick.ignoreFocusOut = true; + quickPick.items = [{ + id: 'existing', + label: localize('existing', "Use {0}:{1}", displayName, this.activeAccount!.accountName) + }, { + id: chooseAnotherItemId, + label: localize('choose another', "Choose another account") + }]; + disposables.add(quickPick.onDidAccept(async () => { + if (quickPick.selectedItems.length) { + if (quickPick.selectedItems[0].id === chooseAnotherItemId) { + await this.authenticationService.logout(this.userDataSyncStore!.authenticationProviderId, this.activeAccount!.id); + await this.setActiveAccount(undefined); + } + quickPick.hide(); + c(); + } + })); + disposables.add(quickPick.onDidHide(() => disposables.dispose())); + quickPick.show(); + }); + } if (this.authenticationState.get() === AuthStatus.SignedOut) { await this.signIn(); } @@ -893,7 +925,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo items.push({ id: showSyncSettingsCommand.id, label: showSyncSettingsCommand.title }); items.push({ id: showSyncActivityCommand.id, label: showSyncActivityCommand.title(that.userDataSyncService) }); items.push({ type: 'separator' }); - items.push({ id: stopSyncCommand.id, label: stopSyncCommand.title(that.activeAccount), }); + items.push({ id: stopSyncCommand.id, label: stopSyncCommand.title(that.userDataSyncStore!.authenticationProviderId, that.activeAccount, that.authenticationService) }); quickPick.items = items; const disposables = new DisposableStore(); disposables.add(quickPick.onDidAccept(() => { @@ -918,7 +950,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo constructor() { super({ id: stopSyncCommand.id, - title: stopSyncCommand.title(that.activeAccount), + title: stopSyncCommand.title(that.userDataSyncStore!.authenticationProviderId, that.activeAccount, that.authenticationService), menu: { id: MenuId.CommandPalette, when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), CONTEXT_SYNC_ENABLEMENT), From 15bbdbb2567233662f2941c26c1cb33abb348d18 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 26 Feb 2020 17:16:21 +0100 Subject: [PATCH 172/200] fix #91402 --- src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index 15e1daa647c..2468a00f1a6 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -493,7 +493,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo localize('sync preview message', "Synchronizing your preferences is a preview feature, please read the documentation before turning it on."), [ localize('open doc', "Open Documentation"), - localize('confirm', "Continue"), + localize('turn on sync', "Turn on Sync"), localize('cancel', "Cancel"), ], { From f97a8068f81fbc4d1fc06d829c1ef98ebb53810c Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Wed, 26 Feb 2020 08:20:20 -0800 Subject: [PATCH 173/200] Use thenable instead of promise, fixes #91549 --- src/vs/editor/common/modes.ts | 2 +- src/vs/vscode.proposed.d.ts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index c348245a8ec..627cb4e950a 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -1373,7 +1373,7 @@ export interface RenameProvider { */ export interface AuthenticationSession { id: string; - accessToken(): Promise; + accessToken(): Thenable; accountName: string; } diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 50c03acaa3f..b603632b092 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -20,7 +20,7 @@ declare module 'vscode' { export interface AuthenticationSession { id: string; - accessToken(): Promise; + accessToken(): Thenable; accountName: string; scopes: string[] } @@ -58,13 +58,13 @@ declare module 'vscode' { /** * Returns an array of current sessions. */ - getSessions(): Promise>; + getSessions(): Thenable>; /** * Prompts a user to login. */ - login(scopes: string[]): Promise; - logout(sessionId: string): Promise; + login(scopes: string[]): Thenable; + logout(sessionId: string): Thenable; } export namespace authentication { From a98cd716582224d8c16c6ecbbc74fe59fe73f437 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 26 Feb 2020 17:20:40 +0100 Subject: [PATCH 174/200] Fix #91391 --- src/vs/platform/userDataSync/common/abstractSynchronizer.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/platform/userDataSync/common/abstractSynchronizer.ts b/src/vs/platform/userDataSync/common/abstractSynchronizer.ts index 7fdd78f0a10..92590672d67 100644 --- a/src/vs/platform/userDataSync/common/abstractSynchronizer.ts +++ b/src/vs/platform/userDataSync/common/abstractSynchronizer.ts @@ -213,6 +213,9 @@ export abstract class AbstractSynchroniser extends Disposable { private async cleanUpBackup(): Promise { try { + if (!(await this.fileService.exists(this.syncFolder))) { + return; + } const stat = await this.fileService.resolve(this.syncFolder); if (stat.children) { const all = stat.children.filter(stat => stat.isFile && /^\d{8}T\d{6}(\.json)?$/.test(stat.name)).sort(); From 1d58311f29d9fdfdeb91fc3b1a354995768e2e67 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 26 Feb 2020 17:33:21 +0100 Subject: [PATCH 175/200] Add the "stop detecting" task quick pick changes back in Revert "Revert commits for task quick pick changes. This fixes #90474" This reverts commit d33663fac9508e12a0fd85968b902dd5d07f488d. --- .../parts/quickinput/browser/quickInput.ts | 4 + .../parts/quickinput/common/quickInput.ts | 2 + .../tasks/browser/abstractTaskService.ts | 105 +++++++++++++----- .../tasks/browser/providerProgressManager.ts | 61 ++++++++++ 4 files changed, 147 insertions(+), 25 deletions(-) create mode 100644 src/vs/workbench/contrib/tasks/browser/providerProgressManager.ts diff --git a/src/vs/base/parts/quickinput/browser/quickInput.ts b/src/vs/base/parts/quickinput/browser/quickInput.ts index afb94d70395..059eb29406d 100644 --- a/src/vs/base/parts/quickinput/browser/quickInput.ts +++ b/src/vs/base/parts/quickinput/browser/quickInput.ts @@ -575,6 +575,10 @@ class QuickPick extends QuickInput implements IQuickPi return this.visible ? this.ui.inputBox.hasFocus() : false; } + public focusOnInput() { + this.ui.inputBox.setFocus(); + } + onDidChangeSelection = this.onDidChangeSelectionEmitter.event; onDidTriggerItemButton = this.onDidTriggerItemButtonEmitter.event; diff --git a/src/vs/base/parts/quickinput/common/quickInput.ts b/src/vs/base/parts/quickinput/common/quickInput.ts index 531551f95b0..36905c59c53 100644 --- a/src/vs/base/parts/quickinput/common/quickInput.ts +++ b/src/vs/base/parts/quickinput/common/quickInput.ts @@ -209,6 +209,8 @@ export interface IQuickPick extends IQuickInput { validationMessage: string | undefined; inputHasFocus(): boolean; + + focusOnInput(): void; } export interface IInputBox extends IQuickInput { diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index 728fff5fac2..e1461676966 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -63,7 +63,7 @@ import { getTemplates as getTaskTemplates } from 'vs/workbench/contrib/tasks/com import * as TaskConfig from '../common/taskConfiguration'; import { TerminalTaskSystem } from './terminalTaskSystem'; -import { IQuickInputService, IQuickPickItem, QuickPickInput } from 'vs/platform/quickinput/common/quickInput'; +import { IQuickInputService, IQuickPickItem, QuickPickInput, IQuickPick } from 'vs/platform/quickinput/common/quickInput'; import { TaskDefinitionRegistry } from 'vs/workbench/contrib/tasks/common/taskDefinitionRegistry'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; @@ -80,6 +80,7 @@ import { IPreferencesService } from 'vs/workbench/services/preferences/common/pr import { find } from 'vs/base/common/arrays'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { IViewsService } from 'vs/workbench/common/views'; +import { ProviderProgressMananger } from 'vs/workbench/contrib/tasks/browser/providerProgressManager'; const QUICKOPEN_HISTORY_LIMIT_CONFIG = 'task.quickOpen.history'; const QUICKOPEN_DETAIL_CONFIG = 'task.quickOpen.detail'; @@ -218,6 +219,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer private _providers: Map; private _providerTypes: Map; protected _taskSystemInfos: Map; + private _providerProgressManager: ProviderProgressMananger | undefined; protected _workspaceTasksPromise?: Promise>; protected _areJsonTasksSupportedPromise: Promise = Promise.resolve(false); @@ -1345,11 +1347,24 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer protected abstract getTaskSystem(): ITaskSystem; - private async provideTasksWithWarning(provider: ITaskProvider, type: string, validTypes: IStringDictionary): Promise { + private async provideTasksWithWarning(provider: ITaskProvider, type: string, validTypes: IStringDictionary): Promise { return new Promise(async (resolve, reject) => { - provider.provideTasks(validTypes).then((value) => { + let isDone = false; + let disposable: IDisposable | undefined; + const providePromise = provider.provideTasks(validTypes); + this._providerProgressManager?.addProvider(type, providePromise); + disposable = this._providerProgressManager?.canceled.token.onCancellationRequested(() => { + if (!isDone) { + resolve(); + } + }); + providePromise.then((value) => { + isDone = true; + disposable?.dispose(); resolve(value); }, (e) => { + isDone = true; + disposable?.dispose(); reject(e); }); }); @@ -1361,10 +1376,11 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer TaskDefinitionRegistry.all().forEach(definition => validTypes[definition.taskType] = true); validTypes['shell'] = true; validTypes['process'] = true; + this._providerProgressManager = new ProviderProgressMananger(); return new Promise(resolve => { let result: TaskSet[] = []; let counter: number = 0; - let done = (value: TaskSet) => { + let done = (value: TaskSet | undefined) => { if (value) { result.push(value); } @@ -2060,30 +2076,69 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } return entries; }); - return this.quickInputService.pick(pickEntries, { - placeHolder, - matchOnDescription: true, - onDidTriggerItemButton: context => { - let task = context.item.task; - this.quickInputService.cancel(); - if (ContributedTask.is(task)) { - this.customize(task, undefined, true); - } else if (CustomTask.is(task)) { - this.openConfig(task); + + const picker: IQuickPick = this.quickInputService.createQuickPick(); + picker.placeholder = placeHolder; + picker.matchOnDescription = true; + picker.ignoreFocusOut = true; + + picker.onDidTriggerItemButton(context => { + let task = context.item.task; + this.quickInputService.cancel(); + if (ContributedTask.is(task)) { + this.customize(task, undefined, true); + } else if (CustomTask.is(task)) { + this.openConfig(task); + } + }); + picker.busy = true; + const progressManager = this._providerProgressManager; + const progressTimeout = setTimeout(() => { + if (progressManager) { + progressManager.showProgress = (stillProviding, total) => { + let message = undefined; + if (stillProviding.length > 0) { + message = nls.localize('pickProgressManager.description', 'Detecting tasks ({0} of {1}): {2} in progress', total - stillProviding.length, total, stillProviding.join(', ')); + } + picker.description = message; + }; + progressManager.addOnDoneListener(() => { + picker.focusOnInput(); + picker.customButton = false; + }); + if (!progressManager.isDone) { + picker.customLabel = nls.localize('taskQuickPick.cancel', "Stop detecting"); + picker.onDidCustom(() => { + this._providerProgressManager?.cancel(); + }); + picker.customButton = true; } } - }, cancellationToken).then(async (selection) => { - if (cancellationToken.isCancellationRequested) { - // canceled when there's only one task - const task = (await pickEntries)[0]; - if ((task).task) { - selection = task; + }, 1000); + pickEntries.then(entries => { + clearTimeout(progressTimeout); + progressManager?.dispose(); + picker.busy = false; + picker.items = entries; + }); + picker.show(); + + return new Promise(resolve => { + this._register(picker.onDidAccept(async () => { + let selection = picker.selectedItems ? picker.selectedItems[0] : undefined; + if (cancellationToken.isCancellationRequested) { + // canceled when there's only one task + const task = (await pickEntries)[0]; + if ((task).task) { + selection = task; + } } - } - if (!selection) { - return; - } - return selection; + picker.dispose(); + if (!selection) { + resolve(); + } + resolve(selection); + })); }); } diff --git a/src/vs/workbench/contrib/tasks/browser/providerProgressManager.ts b/src/vs/workbench/contrib/tasks/browser/providerProgressManager.ts new file mode 100644 index 00000000000..7c48da7f73c --- /dev/null +++ b/src/vs/workbench/contrib/tasks/browser/providerProgressManager.ts @@ -0,0 +1,61 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { TaskSet } from 'vs/workbench/contrib/tasks/common/tasks'; +import { Emitter } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; + +export class ProviderProgressMananger extends Disposable { + private _onProviderComplete: Emitter = new Emitter(); + private _stillProviding: Set = new Set(); + private _totalProviders: number = 0; + private _onDone: Emitter = new Emitter(); + private _isDone: boolean = false; + private _showProgress: ((remaining: string[], total: number) => void) | undefined; + public canceled: CancellationTokenSource = new CancellationTokenSource(); + + constructor() { + super(); + this._register(this._onProviderComplete.event(taskType => { + this._stillProviding.delete(taskType); + if (this._stillProviding.size === 0) { + this._isDone = true; + this._onDone.fire(); + } + if (this._showProgress) { + this._showProgress(Array.from(this._stillProviding), this._totalProviders); + } + })); + } + + public addProvider(taskType: string, provider: Promise) { + this._totalProviders++; + this._stillProviding.add(taskType); + provider.then(() => this._onProviderComplete.fire(taskType)); + } + + public addOnDoneListener(onDoneListener: () => void) { + this._register(this._onDone.event(onDoneListener)); + } + + set showProgress(progressDisplayFunction: (remaining: string[], total: number) => void) { + this._showProgress = progressDisplayFunction; + this._showProgress(Array.from(this._stillProviding), this._totalProviders); + } + + get isDone(): boolean { + return this._isDone; + } + + public cancel() { + this._isDone = true; + if (this._showProgress) { + this._showProgress([], 0); + } + this._onDone.fire(); + this.canceled.cancel(); + } +} From bfc01bb9ea31d48133d6053de97dc7f8b2011b10 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 26 Feb 2020 17:36:02 +0100 Subject: [PATCH 176/200] Bandaid for task not running after detection is canceled Fixes #90474 --- .../tasks/browser/abstractTaskService.ts | 43 +++++++++++++++++-- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index e1461676966..27418989dde 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -562,6 +562,37 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer protected abstract versionAndEngineCompatible(filter?: TaskFilter): boolean; + private tasksAndGroupedTasks(filter?: TaskFilter): { tasks: Promise, grouped: Promise } { + if (!this.versionAndEngineCompatible(filter)) { + return { tasks: Promise.resolve([]), grouped: Promise.resolve(new TaskMap()) }; + } + const grouped = this.getGroupedTasks(filter ? filter.type : undefined); + const tasks = grouped.then((map) => { + if (!filter || !filter.type) { + return map.all(); + } + let result: Task[] = []; + map.forEach((tasks) => { + for (let task of tasks) { + if (ContributedTask.is(task) && task.defines.type === filter.type) { + result.push(task); + } else if (CustomTask.is(task)) { + if (task.type === filter.type) { + result.push(task); + } else { + let customizes = task.customizes(); + if (customizes && customizes.type === filter.type) { + result.push(task); + } + } + } + } + }); + return result; + }); + return { tasks, grouped }; + } + public tasks(filter?: TaskFilter): Promise { if (!this.versionAndEngineCompatible(filter)) { return Promise.resolve([]); @@ -705,11 +736,11 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer }); } - public run(task: Task | undefined, options?: ProblemMatcherRunOptions, runSource: TaskRunSource = TaskRunSource.System): Promise { + public run(task: Task | undefined, options?: ProblemMatcherRunOptions, runSource: TaskRunSource = TaskRunSource.System, grouped?: Promise): Promise { if (!task) { throw new TaskError(Severity.Info, nls.localize('TaskServer.noTask', 'Task to execute is undefined'), TaskErrors.TaskNotFound); } - return this.getGroupedTasks().then((grouped) => { + return (grouped ?? this.getGroupedTasks()).then((grouped) => { let resolver = this.createResolver(grouped); if (options && options.attachProblemMatcher && this.shouldAttachProblemMatcher(task) && !InMemoryTask.is(task)) { return this.attachProblemMatcher(task).then((toExecute) => { @@ -2193,7 +2224,11 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer private doRunTaskCommand(tasks?: Task[]): void { this.showIgnoredFoldersMessage().then(() => { - this.showQuickPick(tasks ? tasks : this.tasks(), + let taskResult: { tasks: Promise, grouped: Promise } | undefined = undefined; + if (!tasks) { + taskResult = this.tasksAndGroupedTasks(); + } + this.showQuickPick(tasks ? tasks : taskResult!.tasks, nls.localize('TaskService.pickRunTask', 'Select the task to run'), { label: nls.localize('TaskService.noEntryToRun', 'No task to run found. Configure Tasks...'), @@ -2208,7 +2243,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer if (task === null) { this.runConfigureTasks(); } else { - this.run(task, { attachProblemMatcher: true }, TaskRunSource.User).then(undefined, reason => { + this.run(task, { attachProblemMatcher: true }, TaskRunSource.User, taskResult?.grouped).then(undefined, reason => { // eat the error, it has already been surfaced to the user and we don't care about it here }); } From 6605c1f1745e1678166679d1f18581339c4c857c Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 26 Feb 2020 17:54:10 +0100 Subject: [PATCH 177/200] #91575 updated account picker --- .../contrib/userDataSync/browser/userDataSync.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index 2468a00f1a6..2c902f0708b 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -542,19 +542,20 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo await new Promise((c, e) => { const disposables: DisposableStore = new DisposableStore(); const displayName = this.authenticationService.getDisplayName(this.userDataSyncStore!.authenticationProviderId); - const quickPick = this.quickInputService.createQuickPick<{ id: string, label: string, description?: string }>(); + const quickPick = this.quickInputService.createQuickPick<{ id: string, label: string, description?: string, detail?: string }>(); const chooseAnotherItemId = 'chooseAnother'; disposables.add(quickPick); - quickPick.title = localize('pick account', "Sync: Choose Account"); + quickPick.title = localize('pick account', "{0}: Pick an account", displayName); quickPick.ok = false; - quickPick.placeholder = localize('choose account placeholder', "Choose account to sync"); + quickPick.placeholder = localize('choose account placeholder', "Pick an account for syncing"); quickPick.ignoreFocusOut = true; quickPick.items = [{ id: 'existing', - label: localize('existing', "Use {0}:{1}", displayName, this.activeAccount!.accountName) + label: localize('existing', "{0}", this.activeAccount!.accountName), + detail: localize('signed in', "Signed in"), }, { id: chooseAnotherItemId, - label: localize('choose another', "Choose another account") + label: localize('choose another', "Use another account") }]; disposables.add(quickPick.onDidAccept(async () => { if (quickPick.selectedItems.length) { From e3273a250b058522308b02030b38912bf0233e45 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 26 Feb 2020 18:03:10 +0100 Subject: [PATCH 178/200] :up: distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 46535c5aa3c..05b3a37a358 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.43.0", - "distro": "d9ed2bff7e779c71264b0c08311d08192924f473", + "distro": "efe6f8371664e7a0a280e47cb32d921d183b8aaa", "author": { "name": "Microsoft Corporation" }, From 1f3d37d97ffefc64cadf9ac2efc9847f4f7f7052 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 26 Feb 2020 18:07:46 +0100 Subject: [PATCH 179/200] Fixes #91368: Improve wording for dialog --- src/vs/platform/undoRedo/common/undoRedoService.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/undoRedo/common/undoRedoService.ts b/src/vs/platform/undoRedo/common/undoRedoService.ts index 8bcaf9d7d16..ccbb2c55482 100644 --- a/src/vs/platform/undoRedo/common/undoRedoService.ts +++ b/src/vs/platform/undoRedo/common/undoRedoService.ts @@ -288,8 +288,8 @@ export class UndoRedoService implements IUndoRedoService { Severity.Info, nls.localize('confirmWorkspace', "Would you like to undo '{0}' across all files?", element.label), [ - nls.localize('ok', "Yes, change {0} files.", affectedEditStacks.length), - nls.localize('nok', "No, change only this file."), + nls.localize('ok', "Undo in {0} files.", affectedEditStacks.length), + nls.localize('nok', "Undo this file."), nls.localize('cancel', "Cancel"), ], { From 29843ee1027ec53f47eb8e541d0d9508b3620275 Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Wed, 26 Feb 2020 09:11:01 -0800 Subject: [PATCH 180/200] Fix missed instance of Thenable in extHostAuthentication --- src/vs/workbench/api/common/extHostAuthentication.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/api/common/extHostAuthentication.ts b/src/vs/workbench/api/common/extHostAuthentication.ts index d9bcc811cbf..234de7348f3 100644 --- a/src/vs/workbench/api/common/extHostAuthentication.ts +++ b/src/vs/workbench/api/common/extHostAuthentication.ts @@ -60,7 +60,7 @@ export class AuthenticationProviderWrapper implements vscode.AuthenticationProvi return this._provider.login(scopes); } - logout(sessionId: string): Promise { + logout(sessionId: string): Thenable { return this._provider.logout(sessionId); } } From 14f9ef0f1a33c35c8396feede34775818924ce29 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 26 Feb 2020 18:11:30 +0100 Subject: [PATCH 181/200] Fixes microsoft/monaco-editor#1849: Correct documentation --- src/vs/editor/common/modes.ts | 2 +- src/vs/monaco.d.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 627cb4e950a..98af16d123d 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -494,7 +494,7 @@ export interface CompletionItem { preselect?: boolean; /** * A string or snippet that should be inserted in a document when selecting - * this completion. When `falsy` the [label](#CompletionItem.label) + * this completion. * is used. */ insertText: string; diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 65f7b8f4e4f..c90b75b3bc2 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -5447,7 +5447,7 @@ declare namespace monaco.languages { preselect?: boolean; /** * A string or snippet that should be inserted in a document when selecting - * this completion. When `falsy` the [label](#CompletionItem.label) + * this completion. * is used. */ insertText: string; From 5ffbdc7077e206d0ef5304b8573289a4cea27a7e Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Wed, 26 Feb 2020 09:27:36 -0800 Subject: [PATCH 182/200] Add notification on sign out, fixes #91481 --- extensions/vscode-account/package.json | 3 +++ extensions/vscode-account/src/extension.ts | 5 +++++ extensions/vscode-account/yarn.lock | 5 +++++ 3 files changed, 13 insertions(+) diff --git a/extensions/vscode-account/package.json b/extensions/vscode-account/package.json index c4df52ce732..cd579aef85a 100644 --- a/extensions/vscode-account/package.json +++ b/extensions/vscode-account/package.json @@ -39,5 +39,8 @@ "tslint": "^5.12.1", "@types/node": "^10.12.21", "@types/keytar": "^4.0.1" + }, + "dependencies": { + "vscode-nls": "^4.1.1" } } diff --git a/extensions/vscode-account/src/extension.ts b/extensions/vscode-account/src/extension.ts index 88f5f3133ed..fa94280fe3d 100644 --- a/extensions/vscode-account/src/extension.ts +++ b/extensions/vscode-account/src/extension.ts @@ -5,6 +5,9 @@ import * as vscode from 'vscode'; import { AzureActiveDirectoryService, onDidChangeSessions } from './AADHelper'; +import * as nls from 'vscode-nls'; + +const localize = nls.loadMessageBundle(); export const DEFAULT_SCOPES = 'https://management.core.windows.net/.default offline_access'; @@ -45,6 +48,7 @@ export async function activate(context: vscode.ExtensionContext) { if (sessions.length === 1) { await loginService.logout(loginService.sessions[0].id); onDidChangeSessions.fire(); + vscode.window.showInformationMessage(localize('signedOut', "Successfully signed out.")); return; } @@ -58,6 +62,7 @@ export async function activate(context: vscode.ExtensionContext) { if (selectedSession) { await loginService.logout(selectedSession.id); onDidChangeSessions.fire(); + vscode.window.showInformationMessage(localize('signedOut', "Successfully signed out.")); return; } })); diff --git a/extensions/vscode-account/yarn.lock b/extensions/vscode-account/yarn.lock index 3acdda242e9..4a86ea6a2a2 100644 --- a/extensions/vscode-account/yarn.lock +++ b/extensions/vscode-account/yarn.lock @@ -635,6 +635,11 @@ util-deprecate@^1.0.1, util-deprecate@~1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= +vscode-nls@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.1.tgz#f9916b64e4947b20322defb1e676a495861f133c" + integrity sha512-4R+2UoUUU/LdnMnFjePxfLqNhBS8lrAFyX7pjb2ud/lqDkrUavFUTcG7wR0HBZFakae0Q6KLBFjMS6W93F403A== + which-pm-runs@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.0.0.tgz#670b3afbc552e0b55df6b7780ca74615f23ad1cb" From 3e4cb8f683720151e8224623dad918791308d984 Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Wed, 26 Feb 2020 09:30:25 -0800 Subject: [PATCH 183/200] Sign out -> Sign Out, fixes #91577 --- extensions/vscode-account/package.nls.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/vscode-account/package.nls.json b/extensions/vscode-account/package.nls.json index 8211a3f6e9c..c0bb4c4a6a0 100644 --- a/extensions/vscode-account/package.nls.json +++ b/extensions/vscode-account/package.nls.json @@ -1,6 +1,6 @@ { "displayName": "Microsoft Account", "description": "Microsoft authentication provider", - "signIn": "Sign in", - "signOut": "Sign out" + "signIn": "Sign In", + "signOut": "Sign Out" } From 02154d7ecfe6bc5788f518f0c92b122339240e18 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Wed, 26 Feb 2020 09:47:28 -0800 Subject: [PATCH 184/200] fixes #91421 --- src/vs/workbench/browser/parts/compositeBar.ts | 8 +++++--- src/vs/workbench/common/views.ts | 1 + .../contrib/extensions/browser/extensions.contribution.ts | 3 ++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/browser/parts/compositeBar.ts b/src/vs/workbench/browser/parts/compositeBar.ts index 5c19e2a5b99..f7f3e18a1b3 100644 --- a/src/vs/workbench/browser/parts/compositeBar.ts +++ b/src/vs/workbench/browser/parts/compositeBar.ts @@ -52,7 +52,7 @@ export class CompositeDragAndDrop implements ICompositeDragAndDrop { if (targetCompositeId) { if (currentLocation !== this.targetContainerLocation && this.targetContainerLocation !== ViewContainerLocation.Panel) { const destinationContainer = viewContainerRegistry.get(targetCompositeId); - if (destinationContainer) { + if (destinationContainer && !destinationContainer.rejectAddedViews) { this.viewDescriptorService.moveViewsToContainer(this.viewDescriptorService.getViewDescriptors(currentContainer)!.allViewDescriptors.filter(vd => vd.canMoveView), destinationContainer); this.openComposite(targetCompositeId, true); } @@ -73,7 +73,7 @@ export class CompositeDragAndDrop implements ICompositeDragAndDrop { if (viewDescriptor && viewDescriptor.canMoveView) { if (targetCompositeId) { const destinationContainer = viewContainerRegistry.get(targetCompositeId); - if (destinationContainer) { + if (destinationContainer && !destinationContainer.rejectAddedViews) { if (this.targetContainerLocation === ViewContainerLocation.Sidebar) { this.viewDescriptorService.moveViewsToContainer([viewDescriptor], destinationContainer); this.openComposite(targetCompositeId, true); @@ -134,6 +134,7 @@ export class CompositeDragAndDrop implements ICompositeDragAndDrop { if (this.targetContainerLocation === ViewContainerLocation.Sidebar) { const destinationContainer = viewContainerRegistry.get(targetCompositeId); return !!destinationContainer && + !destinationContainer.rejectAddedViews && this.viewDescriptorService.getViewDescriptors(currentContainer)!.allViewDescriptors.some(vd => vd.canMoveView); } // ... from sidebar to the panel @@ -155,7 +156,8 @@ export class CompositeDragAndDrop implements ICompositeDragAndDrop { } // ... into a destination - return true; + const destinationContainer = viewContainerRegistry.get(targetCompositeId); + return !!destinationContainer && !destinationContainer.rejectAddedViews; } return false; diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index 1e141807590..d1b30384874 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -53,6 +53,7 @@ export interface IViewContainerDescriptor { readonly extensionId?: ExtensionIdentifier; + readonly rejectAddedViews?: boolean; } export interface IViewContainersRegistry { diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts index 8f0b1969062..cf17ac8d9c5 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -87,7 +87,8 @@ Registry.as(ViewContainerExtensions.ViewContainersRegis name: localize('extensions', "Extensions"), ctorDescriptor: new SyncDescriptor(ExtensionsViewPaneContainer), icon: 'codicon-extensions', - order: 4 + order: 4, + rejectAddedViews: true, }, ViewContainerLocation.Sidebar); From bba707d60c0f6ce8445fbf66677c324aea071a9a Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Wed, 26 Feb 2020 09:50:49 -0800 Subject: [PATCH 185/200] Use checkbox option on auth dialog, fixes #91542 --- .../api/browser/mainThreadAuthentication.ts | 52 ++++++++++--------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadAuthentication.ts b/src/vs/workbench/api/browser/mainThreadAuthentication.ts index 311d89790db..22f91f6d2cc 100644 --- a/src/vs/workbench/api/browser/mainThreadAuthentication.ts +++ b/src/vs/workbench/api/browser/mainThreadAuthentication.ts @@ -75,48 +75,52 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu async $getSessionsPrompt(providerId: string, providerName: string, extensionId: string, extensionName: string): Promise { const alwaysAllow = this.storageService.get(`${extensionId}-${providerId}`, StorageScope.GLOBAL); if (alwaysAllow) { - return true; + return alwaysAllow === 'true'; } - const { choice } = await this.dialogService.show( + const { choice, checkboxChecked } = await this.dialogService.show( Severity.Info, nls.localize('confirmAuthenticationAccess', "The extension '{0}' is trying to access authentication information from {1}.", extensionName, providerName), - [nls.localize('cancel', "Cancel"), nls.localize('allow', "Allow"), nls.localize('alwaysAllow', "Always Allow"),], - { cancelId: 0 } + [nls.localize('cancel', "Cancel"), nls.localize('allow', "Allow")], + { + cancelId: 0, + checkbox: { + label: nls.localize('neverAgain', "Don't Show Again") + } + } ); - switch (choice) { - case 1/** Allow */: - return true; - case 2 /** Always Allow */: - this.storageService.store(`${extensionId}-${providerId}`, 'true', StorageScope.GLOBAL); - return true; - default: - return false; + const allow = choice === 1; + if (checkboxChecked) { + this.storageService.store(`${extensionId}-${providerId}`, allow ? 'true' : 'false', StorageScope.GLOBAL); } + + return allow; } async $loginPrompt(providerId: string, providerName: string, extensionId: string, extensionName: string): Promise { const alwaysAllow = this.storageService.get(`${extensionId}-${providerId}`, StorageScope.GLOBAL); if (alwaysAllow) { - return true; + return alwaysAllow === 'true'; } - const { choice } = await this.dialogService.show( + const { choice, checkboxChecked } = await this.dialogService.show( Severity.Info, nls.localize('confirmLogin', "The extension '{0}' wants to sign in using {1}.", extensionName, providerName), - [nls.localize('cancel', "Cancel"), nls.localize('continue', "Continue"), nls.localize('neverAgain', "Don't Show Again")], - { cancelId: 0 } + [nls.localize('cancel', "Cancel"), nls.localize('continue', "Continue")], + { + cancelId: 0, + checkbox: { + label: nls.localize('neverAgain', "Don't Show Again") + } + } ); - switch (choice) { - case 1/** Allow */: - return true; - case 2 /** Always Allow */: - this.storageService.store(`${extensionId}-${providerId}`, 'true', StorageScope.GLOBAL); - return true; - default: - return false; + const allow = choice === 1; + if (checkboxChecked) { + this.storageService.store(`${extensionId}-${providerId}`, allow ? 'true' : 'false', StorageScope.GLOBAL); } + + return allow; } } From 4c8c66caa062b0e52d9760a37637ea2913b20d6b Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 25 Feb 2020 12:27:47 -0800 Subject: [PATCH 186/200] Use ?. --- .../workbench/contrib/customEditor/browser/customEditors.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts index e69de1daeb0..ca4581ede56 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts @@ -333,11 +333,7 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ } const editorInfo = this._editorInfoStore.get(editor.viewType); - if (!editorInfo) { - continue; - } - - if (!editorInfo.matches(newResource)) { + if (!editorInfo?.matches(newResource)) { continue; } From 851cd9b1a5fb708e55b19c4d3fee4f11780c3b5f Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 26 Feb 2020 10:21:23 -0800 Subject: [PATCH 187/200] Use better parameter name for webview panels For #91568 --- src/vs/vscode.proposed.d.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index b603632b092..89e14e3ddc9 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1334,11 +1334,11 @@ declare module 'vscode' { * the event listeners it is interested it. The provider should also take ownership of the passed in `WebviewPanel`. * * @param document Document for resource being resolved. - * @param webview Webview being resolved. The provider should take ownership of this webview. + * @param webviewPanel Webview being resolved. The provider should take ownership of this webview. * * @return Thenable indicating that the webview editor has been resolved. */ - resolveCustomEditor(document: CustomDocument, webview: WebviewPanel): Thenable; + resolveCustomEditor(document: CustomDocument, webviewPanel: WebviewPanel): Thenable; } /** @@ -1359,11 +1359,11 @@ declare module 'vscode' { * the event listeners it is interested it. The provider should also take ownership of the passed in `WebviewPanel`. * * @param document Resource being resolved. - * @param webview Webview being resolved. The provider should take ownership of this webview. + * @param webviewPanel Webview being resolved. The provider should take ownership of this webview. * * @return Thenable indicating that the webview editor has been resolved. */ - resolveCustomTextEditor(document: TextDocument, webview: WebviewPanel): Thenable; + resolveCustomTextEditor(document: TextDocument, webviewPanel: WebviewPanel): Thenable; } namespace window { From feab34eaf048df2eb1048f66c9977a0d07a306b7 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 26 Feb 2020 19:37:44 +0100 Subject: [PATCH 188/200] extensions development - fix broken extension-development-confirm-save support --- .../services/dialogs/browser/abstractFileDialogService.ts | 4 ++++ .../services/dialogs/electron-browser/fileDialogService.ts | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/services/dialogs/browser/abstractFileDialogService.ts b/src/vs/workbench/services/dialogs/browser/abstractFileDialogService.ts index d481f2ee6ff..bdcff69df4f 100644 --- a/src/vs/workbench/services/dialogs/browser/abstractFileDialogService.ts +++ b/src/vs/workbench/services/dialogs/browser/abstractFileDialogService.ts @@ -92,6 +92,10 @@ export abstract class AbstractFileDialogService implements IFileDialogService { return ConfirmResult.DONT_SAVE; // no veto when we are in extension dev mode because we cannot assume we run interactive (e.g. tests) } + return this.doShowSaveConfirm(fileNamesOrResources); + } + + protected async doShowSaveConfirm(fileNamesOrResources: (string | URI)[]): Promise { if (fileNamesOrResources.length === 0) { return ConfirmResult.DONT_SAVE; } diff --git a/src/vs/workbench/services/dialogs/electron-browser/fileDialogService.ts b/src/vs/workbench/services/dialogs/electron-browser/fileDialogService.ts index 14b4f05f932..36e903296cb 100644 --- a/src/vs/workbench/services/dialogs/electron-browser/fileDialogService.ts +++ b/src/vs/workbench/services/dialogs/electron-browser/fileDialogService.ts @@ -198,7 +198,7 @@ export class FileDialogService extends AbstractFileDialogService implements IFil } } - return super.showSaveConfirm(fileNamesOrResources); + return super.doShowSaveConfirm(fileNamesOrResources); } } From dc68e6578e9234059d9916fa97fe6dba8769328b Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 26 Feb 2020 10:29:50 -0800 Subject: [PATCH 189/200] rename custom editor activation event For #77131 Renames the activation event from `onWebviewEditor` to `onCustomEditor` to be consistent with the reset of the API --- extensions/image-preview/package.json | 2 +- extensions/markdown-language-features/package.json | 2 +- src/vs/workbench/api/browser/mainThreadWebview.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/image-preview/package.json b/extensions/image-preview/package.json index 998f3799f4d..48c7ae314a9 100644 --- a/extensions/image-preview/package.json +++ b/extensions/image-preview/package.json @@ -17,7 +17,7 @@ "Other" ], "activationEvents": [ - "onWebviewEditor:imagePreview.previewEditor", + "onCustomEditor:imagePreview.previewEditor", "onCommand:imagePreview.zoomIn", "onCommand:imagePreview.zoomOut" ], diff --git a/extensions/markdown-language-features/package.json b/extensions/markdown-language-features/package.json index f13d46a4b1c..dbc8746c913 100644 --- a/extensions/markdown-language-features/package.json +++ b/extensions/markdown-language-features/package.json @@ -26,7 +26,7 @@ "onCommand:markdown.showPreviewSecuritySelector", "onCommand:markdown.api.render", "onWebviewPanel:markdown.preview", - "onWebviewEditor:vscode.markdown.preview.editor" + "onCustomEditor:vscode.markdown.preview.editor" ], "contributes": { "commands": [ diff --git a/src/vs/workbench/api/browser/mainThreadWebview.ts b/src/vs/workbench/api/browser/mainThreadWebview.ts index 8764b50ffe8..dda6ad071d9 100644 --- a/src/vs/workbench/api/browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/browser/mainThreadWebview.ts @@ -122,7 +122,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma this._register(_webviewWorkbenchService.registerResolver({ canResolve: (webview: WebviewInput) => { if (webview instanceof CustomEditorInput) { - extensionService.activateByEvent(`onWebviewEditor:${webview.viewType}`); + extensionService.activateByEvent(`onCustomEditor:${webview.viewType}`); return false; } From 9d06f1bdd1996f891cc1cf70484f03e6027abd86 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 26 Feb 2020 10:59:25 -0800 Subject: [PATCH 190/200] Better encapsulation of CustomDocument - Prefer using private fields - Explicitly mark internal hook method and prefix them with _ - Renaming --- src/vs/workbench/api/common/extHostWebview.ts | 179 ++++++++++-------- 1 file changed, 98 insertions(+), 81 deletions(-) diff --git a/src/vs/workbench/api/common/extHostWebview.ts b/src/vs/workbench/api/common/extHostWebview.ts index 121f1615990..539f31df1c2 100644 --- a/src/vs/workbench/api/common/extHostWebview.ts +++ b/src/vs/workbench/api/common/extHostWebview.ts @@ -247,143 +247,160 @@ export class ExtHostWebviewEditor extends Disposable implements vscode.WebviewPa type EditType = unknown; -class WebviewEditorCustomDocument extends Disposable implements vscode.CustomDocument { - private _currentEditIndex: number = -1; - private _savePoint: number = -1; - private readonly _edits: Array = []; +class CustomDocument extends Disposable implements vscode.CustomDocument { - public userData: unknown; - - public _capabilities?: vscode.CustomEditorCapabilities = undefined; - - constructor( - private readonly _proxy: MainThreadWebviewsShape, - public readonly viewType: string, - public readonly uri: vscode.Uri, - ) { - super(); + public static create(proxy: MainThreadWebviewsShape, viewType: string, uri: vscode.Uri) { + return Object.seal(new CustomDocument(proxy, viewType, uri)); } - _setCapabilities(capabilities: vscode.CustomEditorCapabilities) { - if (this._capabilities) { - throw new Error('Capabilities already provided'); - } + // Explicitly initialize all properties as we seal the object after creation! - this._capabilities = capabilities; - capabilities.editing?.onDidEdit(edit => { - this.pushEdit(edit, this); - }); + #currentEditIndex: number = -1; + #savePoint: number = -1; + readonly #edits: Array = []; + + readonly #proxy: MainThreadWebviewsShape; + readonly #viewType: string; + readonly #uri: vscode.Uri; + + #capabilities: vscode.CustomEditorCapabilities | undefined = undefined; + + private constructor(proxy: MainThreadWebviewsShape, viewType: string, uri: vscode.Uri) { + super(); + this.#proxy = proxy; + this.#viewType = viewType; + this.#uri = uri; + } + + dispose() { + this.#onDidDispose.fire(); + super.dispose(); } //#region Public API - #_onDidDispose = this._register(new Emitter()); - public readonly onDidDispose = this.#_onDidDispose.event; + public get viewType(): string { return this.#viewType; } + + public get uri(): vscode.Uri { return this.#uri; } + + #onDidDispose = this._register(new Emitter()); + public readonly onDidDispose = this.#onDidDispose.event; + + public userData: unknown = undefined; //#endregion - dispose() { - this.#_onDidDispose.fire(); - super.dispose(); + //#region Internal + + /** @internal*/ _setCapabilities(capabilities: vscode.CustomEditorCapabilities) { + if (this.#capabilities) { + throw new Error('Capabilities already provided'); + } + + this.#capabilities = capabilities; + capabilities.editing?.onDidEdit(edit => { + this.pushEdit(edit); + }); } - private pushEdit(edit: EditType, trigger: any) { - this.spliceEdits(edit); - - this._currentEditIndex = this._edits.length - 1; - this.updateState(); - // this._onApplyEdit.fire({ edits: [edit], trigger }); - } - - private updateState() { - const dirty = this._edits.length > 0 && this._savePoint !== this._currentEditIndex; - this._proxy.$onDidChangeCustomDocumentState(this.uri, this.viewType, { dirty }); - } - - private spliceEdits(editToInsert?: EditType) { - const start = this._currentEditIndex + 1; - const toRemove = this._edits.length - this._currentEditIndex; - - editToInsert - ? this._edits.splice(start, toRemove, editToInsert) - : this._edits.splice(start, toRemove); - } - - revert() { + /** @internal*/ _revert() { const editing = this.getEditingCapability(); - if (this._currentEditIndex === this._savePoint) { + if (this.#currentEditIndex === this.#savePoint) { return true; } - if (this._currentEditIndex >= this._savePoint) { - const editsToUndo = this._edits.slice(this._savePoint, this._currentEditIndex); + if (this.#currentEditIndex >= this.#savePoint) { + const editsToUndo = this.#edits.slice(this.#savePoint, this.#currentEditIndex); editing.undoEdits(editsToUndo.reverse()); - } else if (this._currentEditIndex < this._savePoint) { - const editsToRedo = this._edits.slice(this._currentEditIndex, this._savePoint); + } else if (this.#currentEditIndex < this.#savePoint) { + const editsToRedo = this.#edits.slice(this.#currentEditIndex, this.#savePoint); editing.applyEdits(editsToRedo); } - this._currentEditIndex = this._savePoint; + this.#currentEditIndex = this.#savePoint; this.spliceEdits(); this.updateState(); return true; } - undo() { + /** @internal*/ _undo() { const editing = this.getEditingCapability(); - if (this._currentEditIndex < 0) { + if (this.#currentEditIndex < 0) { // nothing to undo return; } - const undoneEdit = this._edits[this._currentEditIndex]; - --this._currentEditIndex; + const undoneEdit = this.#edits[this.#currentEditIndex]; + --this.#currentEditIndex; editing.undoEdits([undoneEdit]); this.updateState(); } - redo() { + /** @internal*/ _redo() { const editing = this.getEditingCapability(); - if (this._currentEditIndex >= this._edits.length - 1) { + if (this.#currentEditIndex >= this.#edits.length - 1) { // nothing to redo return; } - ++this._currentEditIndex; - const redoneEdit = this._edits[this._currentEditIndex]; + ++this.#currentEditIndex; + const redoneEdit = this.#edits[this.#currentEditIndex]; editing.applyEdits([redoneEdit]); this.updateState(); } - save() { + /** @internal*/ _save() { return this.getEditingCapability().save(); } - saveAs(target: vscode.Uri) { + /** @internal*/ _saveAs(target: vscode.Uri) { return this.getEditingCapability().saveAs(target); } - backup(cancellation: CancellationToken) { + /** @internal*/ _backup(cancellation: CancellationToken) { return this.getEditingCapability().backup(cancellation); } + //#endregion + + private pushEdit(edit: EditType) { + this.spliceEdits(edit); + + this.#currentEditIndex = this.#edits.length - 1; + this.updateState(); + } + + private updateState() { + const dirty = this.#edits.length > 0 && this.#savePoint !== this.#currentEditIndex; + this.#proxy.$onDidChangeCustomDocumentState(this.uri, this.viewType, { dirty }); + } + + private spliceEdits(editToInsert?: EditType) { + const start = this.#currentEditIndex + 1; + const toRemove = this.#edits.length - this.#currentEditIndex; + + editToInsert + ? this.#edits.splice(start, toRemove, editToInsert) + : this.#edits.splice(start, toRemove); + } + private getEditingCapability(): vscode.CustomEditorEditingCapability { - if (!this._capabilities?.editing) { + if (!this.#capabilities?.editing) { throw new Error('Document is not editable'); } - return this._capabilities.editing; + return this.#capabilities.editing; } } class WebviewDocumentStore { - private readonly _documents = new Map(); + private readonly _documents = new Map(); - public get(viewType: string, resource: vscode.Uri): WebviewEditorCustomDocument | undefined { + public get(viewType: string, resource: vscode.Uri): CustomDocument | undefined { return this._documents.get(this.key(viewType, resource)); } - public add(document: WebviewEditorCustomDocument) { + public add(document: CustomDocument) { const key = this.key(document.viewType, document.uri); if (this._documents.has(key)) { throw new Error(`Document already exists for viewType:${document.viewType} resource:${document.uri}`); @@ -391,7 +408,7 @@ class WebviewDocumentStore { this._documents.set(key, document); } - public delete(document: WebviewEditorCustomDocument) { + public delete(document: CustomDocument) { const key = this.key(document.viewType, document.uri); this._documents.delete(key); } @@ -622,7 +639,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape { } const revivedResource = URI.revive(resource); - const document = Object.seal(new WebviewEditorCustomDocument(this._proxy, viewType, revivedResource)); + const document = CustomDocument.create(this._proxy, viewType, revivedResource); const capabilities = await entry.provider.resolveCustomDocument(document); document._setCapabilities(capabilities); this._documents.add(document); @@ -687,39 +704,39 @@ export class ExtHostWebviews implements ExtHostWebviewsShape { async $undo(resourceComponents: UriComponents, viewType: string): Promise { const document = this.getDocument(viewType, resourceComponents); - document.undo(); + document._undo(); } async $redo(resourceComponents: UriComponents, viewType: string): Promise { const document = this.getDocument(viewType, resourceComponents); - document.redo(); + document._redo(); } async $revert(resourceComponents: UriComponents, viewType: string): Promise { const document = this.getDocument(viewType, resourceComponents); - document.revert(); + document._revert(); } async $onSave(resourceComponents: UriComponents, viewType: string): Promise { const document = this.getDocument(viewType, resourceComponents); - document.save(); + document._save(); } async $onSaveAs(resourceComponents: UriComponents, viewType: string, targetResource: UriComponents): Promise { const document = this.getDocument(viewType, resourceComponents); - return document.saveAs(URI.revive(targetResource)); + return document._saveAs(URI.revive(targetResource)); } async $backup(resourceComponents: UriComponents, viewType: string, cancellation: CancellationToken): Promise { const document = this.getDocument(viewType, resourceComponents); - return document.backup(cancellation); + return document._backup(cancellation); } private getWebviewPanel(handle: WebviewPanelHandle): ExtHostWebviewEditor | undefined { return this._webviewPanels.get(handle); } - private getDocument(viewType: string, resource: UriComponents): WebviewEditorCustomDocument { + private getDocument(viewType: string, resource: UriComponents): CustomDocument { const document = this._documents.get(viewType, URI.revive(resource)); if (!document) { throw new Error('No webview editor custom document found'); From 835c77b8a5919d1908164f238f736a1758f4826e Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Wed, 26 Feb 2020 11:16:51 -0800 Subject: [PATCH 191/200] improve view moving command flow fixes #91422 --- .../browser/actions/layoutActions.ts | 76 ++++++++++--------- 1 file changed, 42 insertions(+), 34 deletions(-) diff --git a/src/vs/workbench/browser/actions/layoutActions.ts b/src/vs/workbench/browser/actions/layoutActions.ts index 50906972e6c..f03e93dd95f 100644 --- a/src/vs/workbench/browser/actions/layoutActions.ts +++ b/src/vs/workbench/browser/actions/layoutActions.ts @@ -25,9 +25,10 @@ import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/commo import { SideBarVisibleContext } from 'vs/workbench/common/viewlet'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IViewDescriptorService, IViewContainersRegistry, Extensions as ViewContainerExtensions, IViewsService, FocusedViewContext, ViewContainerLocation } from 'vs/workbench/common/views'; -import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; +import { IQuickInputService, IQuickPickItem, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { IActivityBarService } from 'vs/workbench/services/activityBar/browser/activityBarService'; const registry = Registry.as(WorkbenchExtensions.WorkbenchActions); const viewCategory = nls.localize('view', "View"); @@ -534,6 +535,7 @@ export class MoveFocusedViewAction extends Action { @IQuickInputService private quickInputService: IQuickInputService, @IContextKeyService private contextKeyService: IContextKeyService, @INotificationService private notificationService: INotificationService, + @IActivityBarService private activityBarService: IActivityBarService, @IViewletService private viewletService: IViewletService ) { super(id, label); @@ -542,58 +544,64 @@ export class MoveFocusedViewAction extends Action { run(): Promise { const viewContainerRegistry = Registry.as(ViewContainerExtensions.ViewContainersRegistry); - const focusedView = FocusedViewContext.getValue(this.contextKeyService); + const focusedViewId = FocusedViewContext.getValue(this.contextKeyService); - if (focusedView === undefined || focusedView.trim() === '') { + if (focusedViewId === undefined || focusedViewId.trim() === '') { this.notificationService.error(nls.localize('moveFocusedView.error.noFocusedView', "There is no view currently focused.")); return Promise.resolve(); } - const viewDescriptor = this.viewDescriptorService.getViewDescriptor(focusedView); + const viewDescriptor = this.viewDescriptorService.getViewDescriptor(focusedViewId); if (!viewDescriptor || !viewDescriptor.canMoveView) { - this.notificationService.error(nls.localize('moveFocusedView.error.nonMovableView', "The currently focused view is not movable {0}.", focusedView)); + this.notificationService.error(nls.localize('moveFocusedView.error.nonMovableView', "The currently focused view is not movable {0}.", focusedViewId)); return Promise.resolve(); } const quickPick = this.quickInputService.createQuickPick(); - quickPick.placeholder = nls.localize('moveFocusedView.selectDestination', "Select a destination area for the view..."); - quickPick.autoFocusOnList = true; + quickPick.placeholder = nls.localize('moveFocusedView.selectDestination', "Select a Destination for the View"); - quickPick.items = [ - { - id: 'sidebar', - label: nls.localize('sidebar', "Sidebar") - }, - { - id: 'panel', + const pinnedViewlets = this.activityBarService.getPinnedViewletIds(); + const items: Array = this.viewletService.getViewlets() + .filter(viewlet => { + if (viewlet.id === this.viewDescriptorService.getViewContainer(focusedViewId)!.id) { + return false; + } + + return !viewContainerRegistry.get(viewlet.id)!.rejectAddedViews && pinnedViewlets.indexOf(viewlet.id) !== -1; + }) + .map(viewlet => { + return { + id: viewlet.id, + label: viewlet.name, + }; + }); + + if (this.viewDescriptorService.getViewLocation(focusedViewId) !== ViewContainerLocation.Panel) { + items.unshift({ + type: 'separator', + label: nls.localize('sidebar', "Side Bar") + }); + items.push({ + type: 'separator', label: nls.localize('panel', "Panel") - } - ]; + }); + items.push({ + id: '_.panel.newcontainer', + label: nls.localize('moveFocusedView.newContainerInPanel', "New Container in Panel"), + }); + } + + quickPick.items = items; quickPick.onDidAccept(() => { const destination = quickPick.selectedItems[0]; - if (destination.id === 'panel') { - quickPick.hide(); + if (destination.id === '_.panel.newcontainer') { this.viewDescriptorService.moveViewToLocation(viewDescriptor!, ViewContainerLocation.Panel); - this.viewsService.openView(focusedView, true); - - return; - } else if (destination.id === 'sidebar') { - quickPick.placeholder = nls.localize('moveFocusedView.selectDestinationContainer', "Select a destination view group..."); - quickPick.items = this.viewletService.getViewlets().map(viewlet => { - return { - id: viewlet.id, - label: viewlet.name - }; - }); - - return; + this.viewsService.openView(focusedViewId, true); } else if (destination.id) { - quickPick.hide(); this.viewDescriptorService.moveViewsToContainer([viewDescriptor], viewContainerRegistry.get(destination.id)!); - this.viewsService.openView(focusedView, true); - return; + this.viewsService.openView(focusedViewId, true); } quickPick.hide(); From 4cdce8d4e8b9673f1f9bbb4ceb4cc6a64bdaadc3 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Wed, 26 Feb 2020 11:27:04 -0800 Subject: [PATCH 192/200] fixes #91423 --- src/vs/workbench/browser/parts/views/viewPaneContainer.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts index 90554ca9351..bf954530159 100644 --- a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts +++ b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts @@ -255,7 +255,10 @@ export abstract class ViewPane extends Pane implements IView { this._onDidFocus.fire(); })); this._register(focusTracker.onDidBlur(() => { - this.focusedViewContextKey.reset(); + if (this.focusedViewContextKey.get() === this.id) { + this.focusedViewContextKey.reset(); + } + this._onDidBlur.fire(); })); } From 611231f0c02a3f79bcee47fa58ade692851202b6 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Wed, 26 Feb 2020 11:32:54 -0800 Subject: [PATCH 193/200] debug: update js-debug-nightly to "2020.2.2517" @ 2020-02-26T01:05:44.117Z --- build/builtInExtensions.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/builtInExtensions.json b/build/builtInExtensions.json index b0483145e01..ddc65ea119f 100644 --- a/build/builtInExtensions.json +++ b/build/builtInExtensions.json @@ -46,7 +46,7 @@ }, { "name": "ms-vscode.js-debug-nightly", - "version": "2020.2.2507", + "version": "2020.2.2517", "forQualities": [ "insider" ], From 38aa49e5dc248d1e23673d8c601b978f5de4ce6e Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Wed, 26 Feb 2020 11:36:50 -0800 Subject: [PATCH 194/200] fixes #91483 --- src/vs/workbench/browser/parts/compositeBarActions.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/workbench/browser/parts/compositeBarActions.ts b/src/vs/workbench/browser/parts/compositeBarActions.ts index b025bf17474..c4eb0fbc8ed 100644 --- a/src/vs/workbench/browser/parts/compositeBarActions.ts +++ b/src/vs/workbench/browser/parts/compositeBarActions.ts @@ -616,6 +616,8 @@ export class CompositeActionViewItem extends ActivityActionViewItem { const data = this.compositeTransfer.getData(DraggedViewIdentifier.prototype); if (Array.isArray(data)) { const draggedViewId = data[0].id; + this.updateFromDragging(container, false); + this.compositeTransfer.clearData(DraggedViewIdentifier.prototype); this.dndHandler.drop(new CompositeDragAndDropData('view', draggedViewId), this.activity.id, e); } From 428c0d091c831675a27fccbbd7e096b41027cf04 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 26 Feb 2020 21:10:09 +0100 Subject: [PATCH 195/200] Fix #91584 --- .../userDataSync/browser/userDataSync.ts | 43 +++++++++++-------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index 2c902f0708b..e1f7ad007a6 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -409,9 +409,9 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo const sourceArea = getSyncAreaLabel(error.source); this.notificationService.notify({ severity: Severity.Error, - message: localize('too large', "Disabled syncing {0} because size of the {1} file to sync is larger than {2}. Please open the file and reduce the size and enable sync", sourceArea, sourceArea, '100kb'), + message: localize('too large', "Disabled syncing {0} because size of the {1} file to sync is larger than {2}. Please open the file and reduce the size and enable sync", sourceArea.toLowerCase(), sourceArea.toLowerCase(), '100kb'), actions: { - primary: [new Action('open sync file', localize('open file', "Open {0} file", sourceArea), undefined, true, + primary: [new Action('open sync file', localize('open file', "Open {0} File", sourceArea), undefined, true, () => error.source === SyncSource.Settings ? this.preferencesService.openGlobalSettings(true) : this.preferencesService.openGlobalKeybindingSettings(true))] } }); @@ -450,22 +450,31 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo } private handleInvalidContentError(source: SyncSource): void { - if (!this.invalidContentErrorDisposables.has(source)) { - const errorArea = getSyncAreaLabel(source); - const handle = this.notificationService.notify({ - severity: Severity.Error, - message: localize('errorInvalidConfiguration', "Unable to sync {0} because there are some errors/warnings in the file. Please open the file to correct errors/warnings in it.", errorArea), - actions: { - primary: [new Action('open sync file', localize('open file', "Open {0} file", errorArea), undefined, true, - () => source === SyncSource.Settings ? this.preferencesService.openGlobalSettings(true) : this.preferencesService.openGlobalKeybindingSettings(true))] - } - }); - this.invalidContentErrorDisposables.set(source, toDisposable(() => { - // close the error warning notification - handle.close(); - this.invalidContentErrorDisposables.delete(source); - })); + if (this.invalidContentErrorDisposables.has(source)) { + return; } + if (source !== SyncSource.Settings && source !== SyncSource.Keybindings) { + return; + } + const resource = source === SyncSource.Settings ? this.workbenchEnvironmentService.settingsResource : this.workbenchEnvironmentService.keybindingsResource; + if (isEqual(resource, this.editorService.activeEditor?.resource)) { + // Do not show notification if the file in error is active + return; + } + const errorArea = getSyncAreaLabel(source); + const handle = this.notificationService.notify({ + severity: Severity.Error, + message: localize('errorInvalidConfiguration', "Unable to sync {0} because there are some errors/warnings in the file. Please open the file to correct errors/warnings in it.", errorArea.toLowerCase()), + actions: { + primary: [new Action('open sync file', localize('open file', "Open {0} File", errorArea), undefined, true, + () => source === SyncSource.Settings ? this.preferencesService.openGlobalSettings(true) : this.preferencesService.openGlobalKeybindingSettings(true))] + } + }); + this.invalidContentErrorDisposables.set(source, toDisposable(() => { + // close the error warning notification + handle.close(); + this.invalidContentErrorDisposables.delete(source); + })); } private async updateBadge(): Promise { From 3b6b125d6e70d727746c93c8189f72d38a35ef6d Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 26 Feb 2020 21:10:54 +0100 Subject: [PATCH 196/200] #91544 revert label change --- src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index e1f7ad007a6..9ce3ae4a81e 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -895,7 +895,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo constructor() { super({ id: 'workbench.userData.actions.syncStatus', - title: localize('sync is on', "Sync..."), + title: localize('sync is on', "Sync is on"), menu: [ { id: MenuId.GlobalActivity, From 9db2563ee5c43204bf22574c758958fcfd4f14dc Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 26 Feb 2020 21:13:03 +0100 Subject: [PATCH 197/200] #91584 convert to lower case --- .../workbench/contrib/userDataSync/browser/userDataSync.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index 9ce3ae4a81e..0a54cd4b5b4 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -290,7 +290,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo const conflictsEditorInput = this.getConflictsEditorInput(conflictsSource); if (!conflictsEditorInput && !this.conflictsDisposables.has(conflictsSource)) { const conflictsArea = getSyncAreaLabel(conflictsSource); - const handle = this.notificationService.prompt(Severity.Warning, localize('conflicts detected', "Unable to sync due to conflicts in {0}. Please resolve them to continue.", conflictsArea), + const handle = this.notificationService.prompt(Severity.Warning, localize('conflicts detected', "Unable to sync due to conflicts in {0}. Please resolve them to continue.", conflictsArea.toLowerCase()), [ { label: localize('accept remote', "Accept Remote"), @@ -1133,8 +1133,8 @@ class AcceptChangesContribution extends Disposable implements IEditorContributio ? localize('Sync accept remote', "Sync: {0}", acceptRemoteLabel) : localize('Sync accept local', "Sync: {0}", acceptLocalLabel), message: isRemote - ? localize('confirm replace and overwrite local', "Would you like to accept Remote {0} and replace Local {1}?", syncAreaLabel, syncAreaLabel) - : localize('confirm replace and overwrite remote', "Would you like to accept Local {0} and replace Remote {1}?", syncAreaLabel, syncAreaLabel), + ? localize('confirm replace and overwrite local', "Would you like to accept Remote {0} and replace Local {1}?", syncAreaLabel.toLowerCase(), syncAreaLabel.toLowerCase()) + : localize('confirm replace and overwrite remote', "Would you like to accept Local {0} and replace Remote {1}?", syncAreaLabel.toLowerCase(), syncAreaLabel.toLowerCase()), primaryButton: isRemote ? acceptRemoteLabel : acceptLocalLabel }); if (result.confirmed) { From 57f7d837ac32a01842607a00ca823dba86261efa Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Wed, 26 Feb 2020 22:06:32 +0100 Subject: [PATCH 198/200] [folding] add setting to allow clicking in empty space to unfold. Fixes #88522 --- src/vs/editor/common/config/editorOptions.ts | 10 +++++----- .../editor/common/standalone/standaloneEnums.ts | 2 +- src/vs/editor/contrib/folding/folding.ts | 16 +++++++--------- src/vs/monaco.d.ts | 6 +++--- 4 files changed, 16 insertions(+), 18 deletions(-) diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index de910398d63..270e0018dff 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -510,7 +510,7 @@ export interface IEditorOptions { * Controls whether clicking on the empty content after a folded line will unfold the line. * Defaults to false. */ - unfoldOnClickInEmptyContent?: boolean; + unfoldOnClickAfterEndOfLine?: boolean; /** * Enable highlighting of matching brackets. * Defaults to 'always'. @@ -3325,7 +3325,7 @@ export const enum EditorOption { folding, foldingStrategy, foldingHighlight, - unfoldOnClickInEmptyContent, + unfoldOnClickAfterEndOfLine, fontFamily, fontInfo, fontLigatures, @@ -3625,9 +3625,9 @@ export const EditorOptions = { EditorOption.foldingHighlight, 'foldingHighlight', true, { description: nls.localize('foldingHighlight', "Controls whether the editor should highlight folded ranges.") } )), - unfoldOnClickInEmptyContent: register(new EditorBooleanOption( - EditorOption.unfoldOnClickInEmptyContent, 'unfoldOnClickInEmptyContent', false, - { description: nls.localize('unfoldOnClickInEmptyContent', "Controls whether clicking on the empty content after a folded line will unfold the line.") } + unfoldOnClickAfterEndOfLine: register(new EditorBooleanOption( + EditorOption.unfoldOnClickAfterEndOfLine, 'unfoldOnClickAfterEndOfLine', false, + { description: nls.localize('unfoldOnClickAfterEndOfLine', "Controls whether clicking on the empty content after a folded line will unfold the line.") } )), fontFamily: register(new EditorStringOption( EditorOption.fontFamily, 'fontFamily', EDITOR_FONT_DEFAULTS.fontFamily, diff --git a/src/vs/editor/common/standalone/standaloneEnums.ts b/src/vs/editor/common/standalone/standaloneEnums.ts index 458faca8bf2..418fe492a04 100644 --- a/src/vs/editor/common/standalone/standaloneEnums.ts +++ b/src/vs/editor/common/standalone/standaloneEnums.ts @@ -199,7 +199,7 @@ export enum EditorOption { folding = 31, foldingStrategy = 32, foldingHighlight = 33, - unfoldOnClickInEmptyContent = 34, + unfoldOnClickAfterEndOfLine = 34, fontFamily = 35, fontInfo = 36, fontLigatures = 37, diff --git a/src/vs/editor/contrib/folding/folding.ts b/src/vs/editor/contrib/folding/folding.ts index c2d01a5ce0e..e80d1f68e5f 100644 --- a/src/vs/editor/contrib/folding/folding.ts +++ b/src/vs/editor/contrib/folding/folding.ts @@ -62,7 +62,7 @@ export class FoldingController extends Disposable implements IEditorContribution private readonly editor: ICodeEditor; private _isEnabled: boolean; private _useFoldingProviders: boolean; - private _unfoldOnClickInEmptyContent: boolean; + private _unfoldOnClickAfterEndOfLine: boolean; private readonly foldingDecorationProvider: FoldingDecorationProvider; @@ -92,7 +92,7 @@ export class FoldingController extends Disposable implements IEditorContribution const options = this.editor.getOptions(); this._isEnabled = options.get(EditorOption.folding); this._useFoldingProviders = options.get(EditorOption.foldingStrategy) !== 'indentation'; - this._unfoldOnClickInEmptyContent = options.get(EditorOption.unfoldOnClickInEmptyContent); + this._unfoldOnClickAfterEndOfLine = options.get(EditorOption.unfoldOnClickAfterEndOfLine); this.foldingModel = null; this.hiddenRangeModel = null; @@ -114,8 +114,7 @@ export class FoldingController extends Disposable implements IEditorContribution this._register(this.editor.onDidChangeConfiguration((e: ConfigurationChangedEvent) => { if (e.hasChanged(EditorOption.folding)) { - const options = this.editor.getOptions(); - this._isEnabled = options.get(EditorOption.folding); + this._isEnabled = this.editor.getOptions().get(EditorOption.folding); this.foldingEnabled.set(this._isEnabled); this.onModelChanged(); } @@ -126,12 +125,11 @@ export class FoldingController extends Disposable implements IEditorContribution this.onModelContentChanged(); } if (e.hasChanged(EditorOption.foldingStrategy)) { - const options = this.editor.getOptions(); - this._useFoldingProviders = options.get(EditorOption.foldingStrategy) !== 'indentation'; + this._useFoldingProviders = this.editor.getOptions().get(EditorOption.foldingStrategy) !== 'indentation'; this.onFoldingStrategyChanged(); } - if (e.hasChanged(EditorOption.unfoldOnClickInEmptyContent)) { - this._unfoldOnClickInEmptyContent = options.get(EditorOption.unfoldOnClickInEmptyContent); + if (e.hasChanged(EditorOption.unfoldOnClickAfterEndOfLine)) { + this._unfoldOnClickAfterEndOfLine = this.editor.getOptions().get(EditorOption.unfoldOnClickAfterEndOfLine); } })); this.onModelChanged(); @@ -370,7 +368,7 @@ export class FoldingController extends Disposable implements IEditorContribution iconClicked = true; break; case MouseTargetType.CONTENT_EMPTY: { - if (this._unfoldOnClickInEmptyContent && this.hiddenRangeModel.hasRanges()) { + if (this._unfoldOnClickAfterEndOfLine && this.hiddenRangeModel.hasRanges()) { const data = e.target.detail as IEmptyContentData; if (!data.isAfterLines) { break; diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index c90b75b3bc2..fd704b03bd3 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -3035,7 +3035,7 @@ declare namespace monaco.editor { * Controls whether clicking on the empty content after a folded line will unfold the line. * Defaults to false. */ - unfoldOnClickInEmptyContent?: boolean; + unfoldOnClickAfterEndOfLine?: boolean; /** * Enable highlighting of matching brackets. * Defaults to 'always'. @@ -3825,7 +3825,7 @@ declare namespace monaco.editor { folding = 31, foldingStrategy = 32, foldingHighlight = 33, - unfoldOnClickInEmptyContent = 34, + unfoldOnClickAfterEndOfLine = 34, fontFamily = 35, fontInfo = 36, fontLigatures = 37, @@ -3941,7 +3941,7 @@ declare namespace monaco.editor { folding: IEditorOption; foldingStrategy: IEditorOption; foldingHighlight: IEditorOption; - unfoldOnClickInEmptyContent: IEditorOption; + unfoldOnClickAfterEndOfLine: IEditorOption; fontFamily: IEditorOption; fontInfo: IEditorOption; fontLigatures2: IEditorOption; From eb1f88b5dc57ea7e6cb5f17ea9cdfb3f5892dc90 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 26 Feb 2020 22:25:18 +0100 Subject: [PATCH 199/200] #91556 Add telemetry to auto sync trigger sources --- .../common/userDataAutoSyncService.ts | 11 +++++- .../userDataSync/common/userDataSync.ts | 4 +- .../userDataSync/common/userDataSyncIpc.ts | 2 +- .../common/userDataSyncService.ts | 4 +- .../userDataAutoSyncService.ts | 13 ++++--- .../browser/userDataAutoSyncService.ts | 13 ++++--- .../userDataSync/browser/userDataSync.ts | 2 +- .../browser/userDataSyncTrigger.ts | 39 +++++++++++-------- .../userDataAutoSyncService.ts | 4 +- .../electron-browser/userDataSyncService.ts | 2 +- 10 files changed, 55 insertions(+), 39 deletions(-) diff --git a/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts b/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts index b64192c62d6..b623b9754a0 100644 --- a/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts +++ b/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts @@ -8,6 +8,11 @@ import { Event, Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { IUserDataSyncLogService, IUserDataSyncService, SyncStatus, IUserDataAutoSyncService, UserDataSyncError, UserDataSyncErrorCode, IUserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync'; import { IAuthenticationTokenService } from 'vs/platform/authentication/common/authentication'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; + +type AutoSyncTriggerClassification = { + source: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; +}; export class UserDataAutoSyncService extends Disposable implements IUserDataAutoSyncService { @@ -25,6 +30,7 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto @IUserDataSyncService private readonly userDataSyncService: IUserDataSyncService, @IUserDataSyncLogService private readonly logService: IUserDataSyncLogService, @IAuthenticationTokenService private readonly authTokenService: IAuthenticationTokenService, + @ITelemetryService private readonly telemetryService: ITelemetryService, ) { super(); this.updateEnablement(false, true); @@ -32,7 +38,7 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto this._register(Event.any(authTokenService.onDidChangeToken)(() => this.updateEnablement(true, true))); this._register(Event.any(userDataSyncService.onDidChangeStatus)(() => this.updateEnablement(true, true))); this._register(this.userDataSyncEnablementService.onDidChangeEnablement(() => this.updateEnablement(true, false))); - this._register(this.userDataSyncEnablementService.onDidChangeResourceEnablement(() => this.triggerAutoSync())); + this._register(this.userDataSyncEnablementService.onDidChangeResourceEnablement(() => this.triggerAutoSync(['resourceEnablement']))); } private async updateEnablement(stopIfDisabled: boolean, auto: boolean): Promise { @@ -99,7 +105,8 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto this.successiveFailures = 0; } - async triggerAutoSync(): Promise { + async triggerAutoSync(sources: string[]): Promise { + sources.forEach(source => this.telemetryService.publicLog2<{ source: string }, AutoSyncTriggerClassification>('sync/triggerAutoSync', { source })); if (this.enabled) { return this.syncDelayer.trigger(() => { this.logService.info('Auto Sync: Triggered.'); diff --git a/src/vs/platform/userDataSync/common/userDataSync.ts b/src/vs/platform/userDataSync/common/userDataSync.ts index 23bd81b6526..79589c43e4f 100644 --- a/src/vs/platform/userDataSync/common/userDataSync.ts +++ b/src/vs/platform/userDataSync/common/userDataSync.ts @@ -278,7 +278,7 @@ export interface IUserDataSyncService { readonly conflictsSources: SyncSource[]; readonly onDidChangeConflicts: Event; - readonly onDidChangeLocal: Event; + readonly onDidChangeLocal: Event; readonly onSyncErrors: Event<[SyncSource, UserDataSyncError][]>; readonly lastSyncTime: number | undefined; @@ -299,7 +299,7 @@ export const IUserDataAutoSyncService = createDecorator; - triggerAutoSync(): Promise; + triggerAutoSync(sources: string[]): Promise; } export const IUserDataSyncUtilService = createDecorator('IUserDataSyncUtilService'); diff --git a/src/vs/platform/userDataSync/common/userDataSyncIpc.ts b/src/vs/platform/userDataSync/common/userDataSyncIpc.ts index 04c8649a703..a2b8c25e64d 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncIpc.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncIpc.ts @@ -86,7 +86,7 @@ export class UserDataAutoSyncChannel implements IServerChannel { call(context: any, command: string, args?: any): Promise { switch (command) { - case 'triggerAutoSync': return this.service.triggerAutoSync(); + case 'triggerAutoSync': return this.service.triggerAutoSync(args[0]); } throw new Error('Invalid call'); } diff --git a/src/vs/platform/userDataSync/common/userDataSyncService.ts b/src/vs/platform/userDataSync/common/userDataSyncService.ts index 07203267160..4d0a2619afe 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncService.ts @@ -34,7 +34,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ private _onDidChangeStatus: Emitter = this._register(new Emitter()); readonly onDidChangeStatus: Event = this._onDidChangeStatus.event; - readonly onDidChangeLocal: Event; + readonly onDidChangeLocal: Event; private _conflictsSources: SyncSource[] = []; get conflictsSources(): SyncSource[] { return this._conflictsSources; } @@ -74,7 +74,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ } this._lastSyncTime = this.storageService.getNumber(LAST_SYNC_TIME_KEY, StorageScope.GLOBAL, undefined); - this.onDidChangeLocal = Event.any(...this.synchronisers.map(s => s.onDidChangeLocal)); + this.onDidChangeLocal = Event.any(...this.synchronisers.map(s => Event.map(s.onDidChangeLocal, () => s.source))); } async pull(): Promise { diff --git a/src/vs/platform/userDataSync/electron-browser/userDataAutoSyncService.ts b/src/vs/platform/userDataSync/electron-browser/userDataAutoSyncService.ts index 770865e5e26..6fa694e1d27 100644 --- a/src/vs/platform/userDataSync/electron-browser/userDataAutoSyncService.ts +++ b/src/vs/platform/userDataSync/electron-browser/userDataAutoSyncService.ts @@ -8,6 +8,7 @@ import { Event } from 'vs/base/common/event'; import { IElectronService } from 'vs/platform/electron/node/electron'; import { UserDataAutoSyncService as BaseUserDataAutoSyncService } from 'vs/platform/userDataSync/common/userDataAutoSyncService'; import { IAuthenticationTokenService } from 'vs/platform/authentication/common/authentication'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; export class UserDataAutoSyncService extends BaseUserDataAutoSyncService { @@ -17,15 +18,15 @@ export class UserDataAutoSyncService extends BaseUserDataAutoSyncService { @IElectronService electronService: IElectronService, @IUserDataSyncLogService logService: IUserDataSyncLogService, @IAuthenticationTokenService authTokenService: IAuthenticationTokenService, + @ITelemetryService telemetryService: ITelemetryService, ) { - super(userDataSyncEnablementService, userDataSyncService, logService, authTokenService); + super(userDataSyncEnablementService, userDataSyncService, logService, authTokenService, telemetryService); - // Sync immediately if there is a local change. - this._register(Event.debounce(Event.any( - electronService.onWindowFocus, - electronService.onWindowOpen, + this._register(Event.debounce(Event.any( + Event.map(electronService.onWindowFocus, () => 'windowFocus'), + Event.map(electronService.onWindowOpen, () => 'windowOpen'), userDataSyncService.onDidChangeLocal, - ), () => undefined, 500)(() => this.triggerAutoSync())); + ), (last, source) => last ? [...last, source] : [source], 1000)(sources => this.triggerAutoSync(sources))); } } diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataAutoSyncService.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataAutoSyncService.ts index 3caeb60f404..7e035fddbce 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataAutoSyncService.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataAutoSyncService.ts @@ -10,6 +10,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { UserDataSyncTrigger } from 'vs/workbench/contrib/userDataSync/browser/userDataSyncTrigger'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IAuthenticationTokenService } from 'vs/platform/authentication/common/authentication'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; export class UserDataAutoSyncService extends BaseUserDataAutoSyncService { @@ -20,15 +21,15 @@ export class UserDataAutoSyncService extends BaseUserDataAutoSyncService { @IAuthenticationTokenService authTokenService: IAuthenticationTokenService, @IInstantiationService instantiationService: IInstantiationService, @IHostService hostService: IHostService, + @ITelemetryService telemetryService: ITelemetryService, ) { - super(userDataSyncEnablementService, userDataSyncService, logService, authTokenService); + super(userDataSyncEnablementService, userDataSyncService, logService, authTokenService, telemetryService); - // Sync immediately if there is a local change. - this._register(Event.debounce(Event.any( - userDataSyncService.onDidChangeLocal, + this._register(Event.debounce(Event.any( + Event.map(hostService.onDidChangeFocus, () => 'windowFocus'), instantiationService.createInstance(UserDataSyncTrigger).onDidTriggerSync, - hostService.onDidChangeFocus - ), () => undefined, 500)(() => this.triggerAutoSync())); + userDataSyncService.onDidChangeLocal, + ), (last, source) => last ? [...last, source] : [source], 1000)(sources => this.triggerAutoSync(sources))); } } diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index 0a54cd4b5b4..8bf9147ddcb 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -163,7 +163,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo this.registerActions(); this.initializeActiveAccount().then(_ => { if (!isWeb) { - this._register(instantiationService.createInstance(UserDataSyncTrigger).onDidTriggerSync(() => userDataAutoSyncService.triggerAutoSync())); + this._register(instantiationService.createInstance(UserDataSyncTrigger).onDidTriggerSync(source => userDataAutoSyncService.triggerAutoSync([source]))); } }); diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSyncTrigger.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSyncTrigger.ts index 43b93c20ab2..1642cd210a4 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSyncTrigger.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSyncTrigger.ts @@ -16,8 +16,8 @@ import { IViewlet } from 'vs/workbench/common/viewlet'; export class UserDataSyncTrigger extends Disposable { - private readonly _onDidTriggerSync: Emitter = this._register(new Emitter()); - readonly onDidTriggerSync: Event = this._onDidTriggerSync.event; + private readonly _onDidTriggerSync: Emitter = this._register(new Emitter()); + readonly onDidTriggerSync: Event = this._onDidTriggerSync.event; constructor( @IEditorService editorService: IEditorService, @@ -25,37 +25,44 @@ export class UserDataSyncTrigger extends Disposable { @IViewletService viewletService: IViewletService, ) { super(); - this._register(Event.debounce(Event.any( - Event.filter(editorService.onDidActiveEditorChange, () => this.isUserDataEditorInput(editorService.activeEditor)), - Event.filter(viewletService.onDidViewletOpen, viewlet => this.isUserDataViewlet(viewlet)) - ), () => undefined, 500)(() => this._onDidTriggerSync.fire())); + this._register(Event.any( + Event.map(editorService.onDidActiveEditorChange, () => this.getUserDataEditorInputSource(editorService.activeEditor)), + Event.map(viewletService.onDidViewletOpen, viewlet => this.getUserDataViewletSource(viewlet)) + )(source => { + if (source) { + this._onDidTriggerSync.fire(source); + } + })); } - private isUserDataViewlet(viewlet: IViewlet): boolean { - return viewlet.getId() === VIEWLET_ID; + private getUserDataViewletSource(viewlet: IViewlet): string | undefined { + if (viewlet.getId() === VIEWLET_ID) { + return 'extensionsViewlet'; + } + return undefined; } - private isUserDataEditorInput(editorInput: IEditorInput | undefined): boolean { + private getUserDataEditorInputSource(editorInput: IEditorInput | undefined): string | undefined { if (!editorInput) { - return false; + return undefined; } if (editorInput instanceof SettingsEditor2Input) { - return true; + return 'settingsEditor'; } if (editorInput instanceof PreferencesEditorInput) { - return true; + return 'settingsEditor'; } if (editorInput instanceof KeybindingsEditorInput) { - return true; + return 'keybindingsEditor'; } const resource = editorInput.resource; if (isEqual(resource, this.workbenchEnvironmentService.settingsResource)) { - return true; + return 'settingsEditor'; } if (isEqual(resource, this.workbenchEnvironmentService.keybindingsResource)) { - return true; + return 'keybindingsEditor'; } - return false; + return undefined; } } diff --git a/src/vs/workbench/services/userDataSync/electron-browser/userDataAutoSyncService.ts b/src/vs/workbench/services/userDataSync/electron-browser/userDataAutoSyncService.ts index 4dc5de8bc46..88ee0dcb78f 100644 --- a/src/vs/workbench/services/userDataSync/electron-browser/userDataAutoSyncService.ts +++ b/src/vs/workbench/services/userDataSync/electron-browser/userDataAutoSyncService.ts @@ -24,8 +24,8 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto this.channel = sharedProcessService.getChannel('userDataAutoSync'); } - triggerAutoSync(): Promise { - return this.channel.call('triggerAutoSync'); + triggerAutoSync(sources: string[]): Promise { + return this.channel.call('triggerAutoSync', [sources]); } } diff --git a/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncService.ts b/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncService.ts index 2b495a43dc9..b3ec8ffeb95 100644 --- a/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncService.ts +++ b/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncService.ts @@ -22,7 +22,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ private _onDidChangeStatus: Emitter = this._register(new Emitter()); readonly onDidChangeStatus: Event = this._onDidChangeStatus.event; - get onDidChangeLocal(): Event { return this.channel.listen('onDidChangeLocal'); } + get onDidChangeLocal(): Event { return this.channel.listen('onDidChangeLocal'); } private _conflictsSources: SyncSource[] = []; get conflictsSources(): SyncSource[] { return this._conflictsSources; } From 2e16c59fde65096c9ac2b3334f6b8b1fcbf87606 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Wed, 26 Feb 2020 14:06:56 -0800 Subject: [PATCH 200/200] Update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 05b3a37a358..09843bf7d28 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.43.0", - "distro": "efe6f8371664e7a0a280e47cb32d921d183b8aaa", + "distro": "e16fca95fbe6abb7e846db3fd372c95da67a41ad", "author": { "name": "Microsoft Corporation" },